HTML5 - File Upload utilizando Drag And Drop

15 Mar 2012 . category: article . Comments
#html5 #javascript #file upload

Introdução

Apesar do HTML 5 ainda não ter sido totalmente especificado e por isso ainda não ter sido liberado pela W3C, ele já está sendo adotado por diversos navegadores e com isso várias aplicações web estão fazendo uso destes novos recursos.

Neste artigo irei mostrar uma forma de realizar upload de arquivos utilizando a File API do HTML 5 simplesmente arrastando um arquivo do desktop em um área pré-determinada da página. Além disso, também será utilizado o FileReader que possibilita o ler o conteúdo do arquivo que é carregado do file system.

ATENÇÃO: Se você está visualizando este post através do feed, eu peço que visite a página do conteúdo no site para ver o exemplo funcionando em seu navegador.

No exemplo, um arquivo arrastado a partir do desktop é carregado pelo navegador e é exibida no frame. É um exemplo simples mas que exemplifica bem o uso desses novos recursos disponíveis pelo Html 5. Se o arquivo arrastado não for uma imagem nada será exibido.

Arraste a imagem aqui
Digite um titulo

A estrutura HTML e o Estilo

Neste exemplo iremos utilizar uma simples div para delimitarmos a área em que o arquivo será arrastado. E colocaremos um elemento span para avisarmos nosso usuário que o arquivo deve ser arrastado para este lugar.

<html>
  <head>
    <link href="html5-fileapi.css" media="all" rel="stylesheet" type="text/css" />
  </head>
  <body>

    <div id="example-content">

      <div id="frame">

        <div id="image-area">

          <span id="drop-message"> Arraste a imagem aqui </span>

        </div>

        <span id="title"> Digite um titulo </span>
      </div>

    </div>

  </body>
  <script src="html5-file-upload-code.js" type="text/javascript"></script>
</html>

O estilo utilizado no exemplo é bem simples e pode ser visto logo abaixo:

# example-content { text-align: center; }
# frame { background-color: #000; color: #fff; margin: 50px; width: 400px; height: 450px; padding: 50px; }
# image-area { width: 100%; border: 1px dotted #fff; padding-top: 50%; padding-bottom: 50%; margin-bottom: 15px; overflow:hidden; }

.hover { border: 1px solid #fff !important; }

O Código Javascript

Abaixo irei colocar o código comentado que será utilizado neste exemplo. Há bastante comentários no código, mas como de costume, quero reforçar que qualquer dúvida utilize o sistema de comentários do site.

// Criação da nossa "classe" de controle.
// Não entrarei neste detalhe aqui, mas por favor,
// se você ouvir alguém falar que javascript é uma
// linguagem orientada a objetos, no mínimo, não
// acredite nele. Javascript é uma linguagem prototipada.
// Essa maneira de programar javascript é uma preferencia
// minha. Prefiro este estilo a criar várias funções perdidas.
function FileFrame(fileArea, fileTitle) {
  var self = this;

  this.fileArea = fileArea;
  this.fileTitle = fileTitle;

  this.init = function() {
    // Registrando eventos de drag and drop
    self.fileArea.addEventListener("dragleave", self.dragHover, false);
    self.fileArea.addEventListener("dragover", self.dragHover, false);
    self.fileArea.addEventListener("drop", self.drop, false);
 
  };

  this.dragHover = function(e) {
    // Impede possíveis tratamentos dos arquivos
    // arrastados pelo navegador, por exemplo, exibir
    // o conteudo do mesmo.
    e.stopPropagation();  
    e.preventDefault();  

    // Quando o arquivo está sobre área alteramos o seu estilo
    self.fileArea.className = (e.type == "dragover" ? "hover" : "");  
  };

  this.drop = function(e) {
    self.dragHover(e);  

    // Volta um array com os arquivos arratados,
    // porém neste exemplo iremos tratar apenas
    // o primeiro arquivo
    self.file = e.dataTransfer.files[0];  
   
    // Recupera nome do arquivo
    self.fileTitle.innerHTML = self.file.name;


    self.read(self.file);
    
    // Neste ponto podemos implementar uma função para
    // enviar os arquivos via ajax.
    // Irei deixar um exemplo, qualquer dúvida eu peço
    // que utilize o sistema de comentários do site.
    /*
    self.sendFile(self.file);
    */
  };

  // Esse método irá ler o arquivo na memória,
  // depois iremos mostrá-lo no nosso frame
  this.read = function(file) {
    // Iremos ler apenas imagens nesse exemplo
    // e iremos exibi-lo no frame
    if (file.type.match('image.*')) {
      var reader = new FileReader();

      // Callback que será executado após a leitura do arquivo
      reader.onload = function(f) {
        self.fileArea.innerHTML = "";
        self.fileArea.setAttribute("style", "padding: 0px !important;");
        
        // Criação do elemento que será utilizado para exibir a imagem
        var img = document.createElement("img");
        img.setAttribute("src", f.target.result);
        img.setAttribute("height", "350");

        self.fileArea.appendChild(img);
      }

      // Irá ler o arquivo para ser acessado através de uma url
      reader.readAsDataURL(file);
    }
  }

  // Essa função pode ser utilizada como 
  this.sendFile = function(file) {

    // Criaremos um formulário
    var f = new FormData();
    // Passando o arquivo para o formulário
    f.append("file", file);

    // Chamada async para realizar o upload da imagem
    var request = new XMLHttpRequest();
    request.open("POST", "", true);
    request.send(f);
    request.onreadystatechange=function(){
      // Término do envio do formulário
      if(request.readyState==4) {
      }
    }
  };

}

// Recupera a div que conterá a imagem
// e o span com o título de nosso arquivo
var area = document.getElementById("image-area");
var title = document.getElementById("title");

var fileFrameArea = new FileFrame(area, title);
fileFrameArea.init();

Conclusão

Infelizmente o uso deste tipo de recurso em produção ainda dever ser utilizado em aplicações bastante especificas ou devemos também criar alternativas a elas já que uma bela parcela de usuários ainda estão utilizando navegadores que não suportam esse tipo de recurso. Mas é legal ver o que está por vir.

Os arquivos utilizados neste exemplo podem ser encontrados no repositório do blog no github.


Me

Tenho estudado esse mundo mágico da programação desde 2005. Já consegui sustentar minha família usando Ruby, Java, Python, C++ e Javascript. O resto tenho usado para diversão ou aprendizado.