Resumo : neste tutorial, você aprenderá sobre a API JavaScript FileReader e como usá-la para implementar o upload de arquivos.
Introdução à API JavaScript FileReader
Quando você arrasta e solta arquivos no navegador da web ou seleciona arquivos para upload por meio do elemento de entrada de arquivo, o JavaScript representa cada arquivo como um File
objeto.
O File
objeto permite acessar o arquivo selecionado em JavaScript. E o JavaScript usa o FileList
objeto para armazenar os File
objetos.
Para ler o conteúdo de um arquivo, você usa o FileReader
objeto. Observe que o FileReader
único pode acessar os arquivos selecionados por meio de arrastar e soltar ou entrada de arquivo.
Para usar o FileReader
objeto, siga estas etapas:
Primeiro, crie um novo FileObject
:
const reader = new FileReader();
Linguagem de código: JavaScript ( javascript )
Segundo, chame um dos métodos read para ler o conteúdo de um arquivo. Por exemplo:
reader.readAsDataURL(file);
Linguagem de código: JavaScript ( javascript )
O readAsDataURL()
método lê o conteúdo do arquivo, que você obtém do FileList
objeto.
O readAsDataURL()
método retorna um objeto com a result
propriedade que contém os dados como um arquivo data: URL
. O data:URL
representa os dados do arquivo como uma string codificada em base64.
Por exemplo, você pode usar readAsDataURL()
para ler uma imagem e mostrar sua string codificada em base64 em uma página da web.
Além do readAsDataURL()
método, FileReader
possui outros métodos para leitura de dados de arquivos como readAsText()
, readAsBinaryString()
, e readAsArrayBuffer()
.
Como todos esses métodos leem os dados do arquivo de forma assíncrona, você não pode simplesmente retornar o resultado assim:
const data = reader.readAsDataURL(file);
Linguagem de código: JavaScript ( javascript )
Quando o readAsDataURL()
método conclui a leitura do arquivo com sucesso, ele FileReader
dispara o load
evento.
Terceiro, adicione um manipulador de eventos para manipular o load
evento do FileReader
objeto:
reader.addEventListener('load', (e) => {
const data = e.target.result;
}
Linguagem de código: JavaScript ( javascript )
Usando JavaScript FileReader para implementar um aplicativo de upload de imagens
Usaremos o FileReader
para implementar um aplicativo de upload de imagens :
Quando você arrasta e solta imagens na zona para soltar, o aplicativo usará o FileReader
para ler as imagens e mostrá-las na página junto com o nome e o tamanho do arquivo:
Além disso, o aplicativo usará a API Fetch para fazer upload dos arquivos para o servidor.
Para o lado do servidor, implementaremos um script PHP simples que carrega as imagens para a 'uploads'
pasta no servidor.
Configurando a estrutura do projeto
Primeiro, crie a seguinte estrutura de arquivos e diretórios:
├── css
| └── style.css
├── images
| └── upload.svg
├── js
| └── app.js
├── index.html
├── upload.php
└── uploads
Linguagem de código: JavaScript ( javascript )
index.html
O seguinte mostra o index.html
arquivo:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="css/style.css" />
<title>FileReader API Demo - Image Upload Application</title>
</head>
<body>
<main>
<div class="dropzone">
<img src="images/upload.svg" alt="upload" width="60" />
<input type="file" class="files" id="images"
accept="image/png, image/jpeg"
multiple />
<label for="images">Choose multiple images</label>
<h3>or drag & drop your PNG or JPEG files here</h3>
</div>
<div class="image-list"></div>
</main>
<script src="js/app.js"></script>
</body>
</html>
Linguagem de código: JavaScript ( javascript )
No index.html
, adicionamos o css/style.css
ao head
do html
documento e js/app.js
antes da tag envolvente body
.
O div
elemento com a classe dropzone
permite arrastar e soltar imagens nele. Além disso, o elemento de entrada de arquivo permitirá que você selecione os arquivos para upload.
O elemento de entrada file aceita vários arquivos e permite apenas imagens jpeg e png:
<input type="file" class="files" id="images"
accept="image/png, image/jpeg"
multiple />
Linguagem de código: JavaScript ( javascript )
Fornece style.css
os estilos que transformam o elemento de entrada do arquivo em um botão. Além disso, possui a active
classe que destaca a zona de lançamento quando você arrasta o arquivo para ela.
O div
elemento com a image-list
classe mostrará as imagens enviadas.
aplicativo.js
Primeiro, selecione dropzone, entrada de arquivo (arquivos) e elementos da lista de imagens usando o querySelector()
método:
const imageList = document.querySelector('.image-list');
const fileInput = document.querySelector('.files');
const dropzone = document.querySelector('.dropzone');
Linguagem de código: JavaScript ( javascript )
Segundo, defina uma função que adicione ou remova a classe ativa da zona de lançamento:
const setActive = (dropzone, active = true) => {
const hasActiveClass = dropzone.classList.contains('active');
if (active && !hasActiveClass) {
return dropzone.classList.add('active');
}
if (!active && hasActiveClass) {
return dropzone.classList.remove('active');
}
};
Linguagem de código: JavaScript ( javascript )
Se você ligar setActive(dropzone)
, ele adicionará a classe ativa ao arquivo dropzone
. Se você ligar setActive(dropzone, false)
, isso removerá a active
classe do dropzone
.
Terceiro, destaque dropzone
quando os eventos dragenter
e dragover
ocorrem e remova o destaque quando os eventos dragleave
e drop
ocorrem:
dropzone.addEventListener('dragenter', (e) => {
e.preventDefault();
setActive(dropzone);
});
dropzone.addEventListener('dragover', (e) => {
e.preventDefault();
setActive(dropzone);
});
dropzone.addEventListener('dragleave', (e) => {
e.preventDefault();
setActive(dropzone, false);
});
dropzone.addEventListener('drop', (e) => {
e.preventDefault();
setActive(dropzone, false);
// ..
});
Linguagem de código: JavaScript ( javascript )
Quarto, obtenha o FileList
objeto no e.target
manipulador e.target.files
de drop
eventos de dropzone
:
dropzone.addEventListener('drop', (e) => {
e.preventDefault();
setActive(dropzone, false);
// get the FileList
const { files } = e.dataTransfer;
handleImages(files);
});
Linguagem de código: JavaScript ( javascript )
No manipulador de eventos drop, usamos a desestruturação de objetos para obter o FileList
objeto e chamar a handleImages()
função para manipular as imagens carregadas:
Quinto, defina a handleImages()
função:
const handleImages = (files) => {
// get valid images
let validImages = [...files].filter((file) =>
['image/jpeg', 'image/png'].includes(file.type)
);
// show the image
validImages.forEach(showImage);
// upload all images
uploadImages(validImages);
};
Linguagem de código: JavaScript ( javascript )
A handleImages()
função obtém as imagens válidas, mostra cada imagem válida na página usando a showImage()
função e carrega todas as imagens para o servidor usando a uploadImages()
função.
Sexto, defina a showImage()
função que mostra cada imagem do validImages
array:
const showImage = (image) => {
const reader = new FileReader();
reader.readAsDataURL(image);
reader.addEventListener('load', (e) => {
const div = document.createElement('div');
div.classList.add('image');
div.innerHTML = `
<img src="${e.target.result}" alt="${image.name}">
<p>${image.name}</p>
<p>${formatBytes(image.size)}</p>
`;
imageList.appendChild(div);
});
};
Linguagem de código: JavaScript ( javascript )
O showImage()
usa FileReader
para ler a imagem carregada como o URL de dados. Assim que FileReader
concluir a leitura do arquivo, ele criará um novo div
elemento para armazenar as informações da imagem.
Observe que a formatBytes()
função converte o tamanho em bytes em um formato legível por humanos:
function formatBytes(size, decimals = 2) {
if (size === 0) return '0 bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(size) / Math.log(k));
return parseFloat((size / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
Linguagem de código: JavaScript ( javascript )
Sétimo, defina a uploadImages()
função que carrega todas as imagens para o servidor:
const uploadImages = async (images) => {
const formData = new FormData();
[...images].forEach((image) =>
formData.append('images[]', image, image.name)
);
const response = await fetch('upload.php', {
method: 'POST',
body: formData,
});
return await response.json();
};
Linguagem de código: JavaScript ( javascript )
A uploadImages()
função usa a FormData
API para construir dados para envio:
const formData = new FormData();
Linguagem de código: JavaScript ( javascript )
Para cada imagem, nós a adicionamos ao FormData
objeto:
[...images].forEach((image) =>
formData.append('images[]', image, image.name)
);
Linguagem de código: JavaScript ( javascript )
Observe que a images
variável é um FileList
objeto, não um array. Para usar o forEach()
método, você usa o operador spread ( ...
) para converter o FileList
objeto em um array como este:
[...images]
Linguagem de código: JavaScript ( javascript )
Todos os pares chave/valor nos dados do formulário têm a mesma chave que images[]
; em PHP, você pode acessá-lo como um array ( $_FILES['images']
)
A uploadImages()
função usa a API Fetch para fazer upload das imagens (como FormData
objeto) para o servidor:
const response = await fetch('upload.php', {
method: 'POST',
body: formData,
});
return await response.json();
Linguagem de código: JavaScript ( javascript )
Oitavo, adicione o manipulador de eventos change ao elemento de entrada do arquivo se os usuários selecionarem arquivos usando este elemento de entrada:
fileInput.addEventListener('change', (e) => {
const { files } = e.target;
handleImages(files);
});
Linguagem de código: JavaScript ( javascript )
No manipulador de eventos change, você pode acessar o objeto FileList como arquivo e.target.files
. A lógica para mostrar e fazer upload de imagens é a mesma de arrastar e soltar.
Observe que se você arrastar e soltar imagens fora da zona de lançamento, o navegador da web exibirá as imagens por padrão.
Para evitar isso, você chama o preventDefault()
método dos objetos de evento dragover
e drop
do documento assim:
// prevent the drag & drop on the page
document.addEventListener('dragover', (e) => e.preventDefault());
document.addEventListener('drop', (e) => e.preventDefault());
Linguagem de código: JavaScript ( javascript )
A seguir mostra o app.js
arquivo completo:
const imageList = document.querySelector('.image-list');
const fileInput = document.querySelector('.files');
const dropzone = document.querySelector('.dropzone');
const setActive = (dropzone, active = true) => {
// active class
const hasActiveClass = dropzone.classList.contains('active');
if (active && !hasActiveClass) {
return dropzone.classList.add('active');
}
if (!active && hasActiveClass) {
return dropzone.classList.remove('active');
}
};
dropzone.addEventListener('dragenter', (e) => {
e.preventDefault();
setActive(dropzone);
});
dropzone.addEventListener('dragover', (e) => {
e.preventDefault();
setActive(dropzone);
});
dropzone.addEventListener('dragleave', (e) => {
e.preventDefault();
setActive(dropzone, false);
});
dropzone.addEventListener('drop', (e) => {
e.preventDefault();
setActive(dropzone, false);
// get the valid files
const { files } = e.dataTransfer;
// hand images
handleImages(files);
});
const handleImages = (files) => {
// get valid images
let validImages = [...files].filter((file) =>
['image/jpeg', 'image/png'].includes(file.type)
);
// show the image
validImages.forEach(showImage);
// upload files
uploadImages(validImages);
};
const showImage = (image) => {
const reader = new FileReader();
reader.readAsDataURL(image);
reader.addEventListener('load', (e) => {
const div = document.createElement('div');
div.classList.add('image');
div.innerHTML = `
<img src="${e.target.result}" alt="${image.name}">
<p>${image.name}</p>
<p>${formatBytes(image.size)}</p>
`;
imageList.appendChild(div);
});
};
const uploadImages = async (images) => {
const formData = new FormData();
[...images].forEach((image) =>
formData.append('images[]', image, image.name)
);
const response = await fetch('upload.php', {
method: 'POST',
body: formData,
});
return await response.json();
};
function formatBytes(size, decimals = 2) {
if (size === 0) return '0 bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(size) / Math.log(k));
return parseFloat((size / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
fileInput.addEventListener('change', (e) => {
const { files } = e.target;
handleImages(files);
});
// prevent the drag & drop on the page
document.addEventListener('dragover', (e) => e.preventDefault());
document.addEventListener('drop', (e) => e.preventDefault());
Linguagem de código: JavaScript ( javascript )
Por fim, crie um upload.php
script simples que mova as imagens enviadas para a uploads
pasta:
<?php
const APP_ROOT = 'http://localhost:8080/';
const UPLOAD_DIR = __DIR__ . '/uploads';
const MESSAGES = [
UPLOAD_ERR_OK => 'File uploaded successfully',
UPLOAD_ERR_INI_SIZE => 'File is too big to upload',
UPLOAD_ERR_FORM_SIZE => 'File is too big to upload',
UPLOAD_ERR_PARTIAL => 'File was only partially uploaded',
UPLOAD_ERR_NO_FILE => 'No file was uploaded',
UPLOAD_ERR_NO_TMP_DIR => 'Missing a temporary folder on the server',
UPLOAD_ERR_CANT_WRITE => 'File is failed to save to disk.',
UPLOAD_ERR_EXTENSION => 'File is not allowed to upload to this server',
];
const ALLOWED_FILES = [
'image/png' => 'png',
'image/jpeg' => 'jpg'
];
const MAX_SIZE = 5 * 1024 * 1024; // 5MB
const HTTP_STATUSES = [
200 => 'OK',
400 => 'Bad Request',
404 => 'Not Found',
405 => 'Method Not Allowed'
];
$is_post_request = strtolower($_SERVER['REQUEST_METHOD']) === 'post';
$has_files = isset($_FILES['images']);
if (!$is_post_request || !$has_files) {
response(405, [
'success' => false,
'message' => ' Method not allowed or files do not exist'
]);
}
$files = $_FILES['images'];
$file_count = count($files['name']);
// validation
$errors = [];
for ($i = 0; $i < $file_count; $i++) {
// get the uploaded file info
$status = $files['error'][$i];
$filename = $files['name'][$i];
$tmp = $files['tmp_name'][$i];
// an error occurs
if ($status !== UPLOAD_ERR_OK) {
$errors[$filename] = MESSAGES[$status];
continue;
}
// validate the file size
$filesize = filesize($tmp);
if ($filesize > MAX_SIZE) {
// construct an error message
$message = sprintf(
"The file %s is %s which is greater than the allowed size %s",
$filename,
format_filesize($filesize),
format_filesize(MAX_SIZE)
);
$errors[$filesize] = $message;
continue;
}
// validate the file type
if (!in_array(get_mime_type($tmp), array_keys(ALLOWED_FILES))) {
$errors[$filename] = "The file $filename is allowed to upload";
}
}
if ($errors) {
response(400, [
'success' => false,
'message' => $errors
]);
}
// move the files
for ($i = 0; $i < $file_count; $i++) {
$filename = $files['name'][$i];
$tmp = $files['tmp_name'][$i];
$mime_type = get_mime_type($tmp);
// set the filename as the basename + extension
$uploaded_file = pathinfo($filename, PATHINFO_FILENAME) . '.' . ALLOWED_FILES[$mime_type];
// new filepath
$filepath = UPLOAD_DIR . '/' . $uploaded_file;
// move the file to the upload dir
$success = move_uploaded_file($tmp, $filepath);
if (!$success) {
$errors[$filename] = "The file $filename was failed to move.";
}
}
if ($errors) {
response(400, [
'success' => false,
'message' => $errors
]);
}
response(200, [
'success' => true,
'message' => 'The files uploaded successfully'
]);
/**
* Return a mime type of file or false if an error occurred
*
* @param string $filename
* @return string | bool
*/
function get_mime_type(string $filename)
{
$info = finfo_open(FILEINFO_MIME_TYPE);
if (!$info) {
return false;
}
$mime_type = finfo_file($info, $filename);
finfo_close($info);
return $mime_type;
}
/**
* Return a human-readable file size
*
* @param int $bytes
* @param int $decimals
* @return string
*/
function format_filesize(int $bytes, int $decimals = 2): string
{
$units = 'BKMGTP';
$factor = floor((strlen($bytes) - 1) / 3);
return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . $units[(int)$factor];
}
/**
* Response JSON to the client
* @param int $status_code
* @param array|null $data
*/
function response(int $status_code, array $data = null)
{
header("HTTP/1.1 " . $status_code . " " . HTTP_STATUSES[$status_code]);
header("Content-Type: application/json");
echo json_encode($data);
exit;
}
Linguagem de código: JavaScript ( javascript )
Leia mais sobre como fazer upload de vários arquivos em PHP para obter mais informações.
Resumo
- Use a API JavaScript FileReader para ler os arquivos selecionados pelos usuários por meio de arrastar e soltar ou elemento de entrada de arquivo.