Preview de imagens antes do upload
Realizar o upload de arquivos é uma tarefa corriqueira do desenvolvedor web. Em se tratando do upload de imagens, realizar o preview antes do envio pode evitar que o usuário selecione a imagem errada. Neste artigo aprenderemos como realizar o preview da imagem escolhida.
Sobre upload de arquivos o leitor pode consultar o artigo Express, realizando upload com multer deste mesmo autor.
O problema
Temos o arquivo index.html
que define um simples <form>
com as tags <input type="file">
e <input type="submit">
:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Preview</title>
</head>
<body>
<form action='api-url-here' method='post' encType="multipart/form-data">
<input class="file-chooser" type="file" accept="image/*">
<input type="submit" value="upload">
</form>
</body>
</html>
Quando selecionamos uma imagem apenas seu nome será exibido ao lado do <input type="file">
. Veremos a seguir como realizar o preview da imagem selecionada.
Solução
Vamos adicionar uma tag <img>
com a classe preview-img
entre a escolha do arquivo e o botão de submissão. É este elemento que fará o preview da imagem selecionada:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Preview</title>
</head>
<body>
<form action='api-url-here' method='post' encType="multipart/form-data">
<input class="file-chooser" type="file" accept="image/*">
<img class="preview-img">
<input type="submit" value="upload">
</form>
<script>
// nosso script fica aqui!
</script>
</body>
</html>
Para darmos continuidade à solução precisamos obter do DOM os elementos de input da seleção do arquivo e a da imagem. Para o input de seleção planejaremos uma resposta para o evento onchange
, aquele disparado quando um arquivo qualquer for selecionado:
const $ = document.querySelector.bind(document);
const previewImg = $('.preview-img');
const fileChooser = $('.file-chooser');
fileChooser.onchange = e => { /* falta implementar */};
Excelente! O evento onchange
de um <input type="file">
nos dá acesso ao arquivo selecionado através de e.target.files.item(0)
:
const $ = document.querySelector.bind(document);
const previewImg = $('.preview-I=img');
const fileChooser = $('.file-chooser');
fileChooser.onchange = e => {
// arquivo que faremos o upload
const fileToUpload = e.target.files.item(0);
};
Não podemos simplesmente atribuir fileToUpload
à previewImg.src
, pois o primeiro é um binário (blob). Precisamos converter o arquivo para DataURL que nada mais é do que a representação do binário como uma string que é automaticamente decodificada pelo navegador. Faremos essa conversão através de um FileReader
:
const $ = document.querySelector.bind(document);
const previewImg = $('.preview-img');
const fileChooser = $('.file-chooser');
fileChooser.onchange = e => {
const fileToUpload = e.target.files.item(0);
const reader = new FileReader();
// evento disparado quando o reader terminar de ler
reader.onload = e => previewImg.src = e.target.result;
// solicita ao reader que leia o arquivo
// transformando-o para DataURL.
// Isso disparará o evento reader.onload.
reader.readAsDataURL(fileToUpload);
};
Quando reader.readAsDataURL
terminar, o evento reader.onload
será disparado. Através do parâmetro do evento temos acesso ao resultado da conversão através de e.target.result
. É importante perceber que esta é uma operação assíncrona.
Pronto! Ao selecionarmos uma imagem ela será exibida para o usuário permitindo-o verificar se ela é a que ele realmente deseja subir.
Bônus: alterando a label do botão de seleção de arquivo
Uma coisa bem incômoda do <input type="file">
é que ele não permite mudar o texto do botão. Inclusive quem utiliza Bootstrap pode ter alguma dificuldade em estilizá-lo.
Uma das soluções é escondermos o <input type="file">
e dispará-lo através do clique de um <input type="button">
. Vejamos a solução.
Primeiro, vamos adicionar o <input type="button">
e tornar o <input type="file">
invisível através do atributo hidden
:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Preview</title>
</head>
<body>
<form action='image/upload' method='post' encType="multipart/form-data">
<!-- novo elemento! -->
<input class="file-button" type="button" value="Choose your beautiful image">
<!-- invisível -->
<input class="file-chooser" type="file" accept="image/*" hidden>
<img class="preview-img">
<input type="submit" value="upload">
</form>
<script>
// nosso script fica aqui!
</script>
</body>
</html>
Agora, vamos obter o novo botão para em seguida adicionarmos o evento onclick
. É neste evento que chamaremos fileChooser.click()
, forçando assim o click e como resultado a seleção do arquivo:
const $ = document.querySelector.bind(document);
const previewImg = $('.preview-img');
const fileChooser = $('.file-chooser');
const fileButton = $('.file-button');
fileButton.onclick = () => fileChooser.click();
fileChooser.onchange = e => {
const fileToUpload = e.target.files.item(0);
const reader = new FileReader();
reader.onload = e => previewImg.src = e.target.result;
reader.readAsDataURL(fileToUpload);
};
Perfeito. Agora temos maior liberdade para estilizar nosso botão.
Conclusão
Muitas vezes a tecnologia vigente não traz uma resposta direta para determinado problema de usabilidade e com isso força o desenvolvedor a elaborar soluções utilizando os recursos que tem. E você? Já precisou realizar o preview de imagens antes? Deixe sua opinião.