Percebi (ao olhar para mim mesmo) que existem muitas demonstrações e tutoriais que mostram como arrastar e soltar um arquivo no navegador e, em seguida, renderizar-o na página. Eles são muitas vezes rotulados como "arrastar e soltar e carregar", mas eles realmente não carregam. Este tutorial vai dar-lhe o passo final.
Vou te levar através de um exemplo com o que você pode reproduzir: arraste um arquivo para a página da Web, e ele será carregado para o servidor instantaneamente.
Tarefas #
Vamos dividir esse processo em tarefas específicas:
- Capture o evento de queda e leia seus dados.
- Poste esses dados binários no servidor.
- Forneça feedback sobre o progresso do upload.
- Opcionalmente, faça uma visualização do que está sendo carregado e seu status.
Para conseguir tudo isso, precisamos das seguintes APIs HTML5 e não HTML5:
Tenha em mente que nem todos os navegadores suportam toda essa tecnologia hoje, mas eles estão chegando perto. Eu só queria criar um tutorial claro e completo sobre como ir o porco inteiro e carregar esse arquivo caiu.
O resultado final: somos capazes de soltar o arquivo em qualquer lugar do navegador, temos uma visualização e progresso do upload, e é uma experiência liso.

Arrastar e soltar #
Assim como um aviso prévio, drag-and-drop é conhecido por ser um pouco de killjoy. Na verdade, é um pesadelo, mas não repetirei o que foi dito antes.
Nosso exemplo permitirá que você arraste e solte um arquivo em qualquer lugar dentro do navegador. Para isso, precisamos anexar nossos ouvintes de eventos ao co
rpo
ou d
ocumentarElemen
t (ou seja, o nó HTML raiz). Ao ouvir no documento
Element, este có
digo pode estar em qualquer lugar da página, poi
s o documentoEle
ment sempre existirá. Você só pode ouvir o cor
po u
ma vez que
o<body
>elemento foi encontrado.</body>
Aqui está o padrão de código que precisamos para capturar o evento de queda em todo o documento:
var doc = document.documentElement; doc.ondragover = função () { this.className = 'hover';return false; }; doc.ondragend = função () { this.className = ''; returnfalse; }; doc.ondrop = função (evento) { event.preventDefault && event.preventDefault(); this.className = ''; agora faça algo com: arquivos var = event.dataTransfer.files; retorno falso; };
Estou usando a
class
e hover para que eu possa alternar uma dica explicando ao usuário que o arquivo pode ser deixado na página.
Dentro do ev
ento
de queda, podemos acessar os arquivos descartados
viaevent.dataTransfer.file
s.
Uma alternativa para arrastar e soltar #
Irritantemente, ao escrever isso, percebo que essa combinação de requisitos de API atualmente só é atendida pelo Chrome e Firefox. Assim, fazendo o teste do Modernizador, podemos testar o suporte e fornecer nossa alternativa:
var dndSupported = função () { var div = document.createElement('div'); retorno ('draggable' em div) || ('ondragstart' em div &'ondrop' em div); }; se (!dndSupported()) { // tomar rota alternativa }
Em vez de arrastar e soltar, podemos inserir um elemento de entrada de arquivo (eu dei-lh
e
uma id de "upload"), e quando seu valor é alterado, o arquivo pode ser coletado em:
document.getElementById('upload').onchange = função (evento){ // 'isso' refere-se ao elemento que o evento disparou sobre os arquivos var = este.files; };
Você pode ver que o equivalente é simplesmente
a pro
priedade de arquivo
s do objetoHTMLInp
utElement. Isso nos dará acesso ao mesmo arquivo do eve
nt.dataTransfer.files
do evento de queda.
Carregando automaticamente o arquivo #
Esta é a parte limpa, e é dolorosamente simples. Usamos um recurso da especificação XHR2
: FormDat
a. Uma vez que criamos uma instância
do FormD
ata, podemos anexar itens a ele:
var formData = novo FormData(); para (var i = 0; i < files.length; i++) { formData.append('file', fil[i]es); } // now post a new XHR request var xhr = new XMLHttpRequest(); xhr.open('POST', '/upload'); xhr.onload = function () { if (xhr.status === 200) { console.log('all done: ' + xhr.status); } else { console.log('Something went terribly wrong...'); } }; xhr.send(formData);
FormData
Sim, é isso †
Vamos olhar para um par de coisas-chave que estão acontecendo no código acima.
formData.append('arquivo', arquivo[i]s);
Estamos enviando parâmetros nomeados para o nosso servidor, especificamente uma matriz de valores chamada a
rquiv
o. Obviamente, você pode chamá-lo do que quiser, mas o a
rquiv
o é o nome que seu servidor estará procurando quando salvar o arquivo (ou arquivos) carregados.
xhr.onload = função () { se (xhr.status === 200) { console.log('tudo feito: ' + xhr.status); } outra { console.log ('Algo deu terrivelmente errado...'); } };
Se você estiver familiarizado com o XHR, você notará que não estamos ouvin
do toonreadystatechan
ge, apen
as onlo
ad — que é (na verdade) uma função de conveniência para que você saiba quando o Est
ado pronto
é 4 (ou seja, carregado!). Você ainda deve verificar e responder adequadamente ao código de status da solicitação para garantir que ele seja 200 OK, em vez de 500 (erro interno do servidor) ou 404 (não encontrado) ou qualquer outra coisa.
xhr.send(formData);
O bom truque aqui é que o XHR definiu automaticamente a codificação dos dados postados em multipart
as/dados de formulári
o. Essa codificação permite que seu servidor leia e salve os arquivos. É como a codificação usada quando você envia um anexo em um e-mail.
Fornecendo Feedback ao Usuário #
XHR2 agora (flippin' finalmente) vem com um ev
ento de pr
ogresso. Então, se você está enviando ou recuperando um arquivo grande via XHR, você pode dizer ao usuário o quão longe você está.
É bem simples. Se você quiser acompanhar o andamento da solicitação XHR, ouça o even
to de pr
ogresso. Um gotcha que me pegou por algum tempo: eu precisava acompanhar o progresso do upload, não o download. Para fazer isso corretamente, você precisa ouvir o progresso no objeto de upload doXHR, assim:
xhr.upload.onprogress = função (evento) { se (event.lengthComputável) { var completo = (event.loaded / event.total * 100 | 0); progress.value = progress.innerHTML = completo; } }; xhr.onload = função () { // apenas no caso de ficarmos presos em torno de 99% de progresso.valor = progresso.innerHTML = 100; };
progresso
do XHR2Note que em vez de x
hr.onprogress
, usamos
xhr.upload.onprogess
. Quando este evento é acionado, verificamos se o evento suporta calcular a quantidade de dados carregados (a pa
rte de comprimento
Computável) e, em seguida, calcular o valor concluído.
No meu HTML, estou usando um
<progress>el
emento (semelhante ao<meter>
elemento
, mas mais semanticamente apropriado à medida que estamos apresentando o progresso de uma tarefa) para mostrar ao usuário quanto de seu arquivo foi carregado:</meter> </progress>
<progress id="progress" min="0" max="100" value="0">0</progress>
Note que estou usando o i
nnerHTML pa
ra navegadores que ainda não suportam<progres
s>.</progres
s> Em seguida, com uma pitada de conteúdo gerado pelo CSS, tant
o o innerH
TML qu
anto o
valor do progresso podem ser os mesmos (talvez uma otimização excessiva, mas eu estava bastante orgulhoso de mim mesmo na época!).
E, finalmente, renderizando uma pré-visualização #
Como uma boa adição, vamos dar ao usuário uma prévia do que estamos enviando para eles. Ele requer a API de arquivo — especificamente a
API DoFileR
eader.
Como a operação arrastar e soltar (ou a
entr[type=file]
ada) contém um objeto de arquivo, podemos entregá-lo ao
FileReader
para obter o conteúdo do arquivo. Se for algo como uma imagem, podemos obter os dados base64 e dar ao usuário uma visualização. Ou se for texto, poderíamos renderizar em um<div
>.</di
v> O tipo MIME para o arquivo está disponível como pro
pried
ade do tipo no objeto de arquivo.
Então vamos supor que só aceitamos imagens. Aqui está a parte de visualização que é executada após o arquivo ter sido recebido pelo navegador:
var aceitoTypes = { 'image/png': true, 'image/jpeg': true, 'image/gif': true }; se (aceitoTypes === verdad[file.type]eiro) { leitor de var = novo FileReader(); reader.onload = função (evento) { imagem var = nova Imagem(); image.src = event.target.result; image.width = 100; um falso documento de redimensionar.body.appendChild (imagem); }; reader.readAsDataURL(arquivo); }
Criamos o File
Reader e da
mos a ele um arquivo para ler. No exemplo acima, usei readAsDataUR
L, mas você tam
bém pode ler arquivos em texto simples e formatos binários (de acordo com a especificação).
Quando o leitor lê o arquivo e os dados estão disponíveis, ele dispara o even
to de
carga, que por sua vez cria nossa nova imagem.
O resultado da ação de leitura de arquivos está
no event.target.resul
tproperty. No caso da readA
sDataURL, o
valor está nos moldes de dat
a:image/png;base64,ABC...
.
Usando todas as ferramentas no galpão #
Este exemplo pode ser um pouco inventado para o artigo que eu queria escrever. Eu usei muitas tecnologias diferentes para fazer algo que é um padrão bastante comum na web. Na verdade, foi porque tive dificuldade em encontrar uma fonte definitiva sobre o upload para o servidor real (exceto, é claro, como escrevi este artigo encontrei este exemplo no final de 2010!).
Espero que você veja como cada pedaço de tecnologia pode trabalhar em conjunto para criar um aplicativo, mas também espero que você possa ver onde isso funcionaria se nenhuma ou apenas parte da tecnologia não estivesse disponível. Certifique-se de detectar funcionalidades. Certifique-se de fornecer recuos. Certifique-se de desenvolver com responsabilidade.
Aqui está a demonstração final de arrastar e soltar e carregar para você jogar.