Artigo original: How to upload single or multiple files the easy way with FormData

Neste artigo, aprenderemos sobre a interface FormData, disponível nos navegadores modernos da web como parte das especificações do HTML5.

Veremos exemplos de uso de FormData com Ajax e indicaremos links para o uso com Angular 7, Ionic e React.

O que é a FormData?

FormData é, simplesmente, uma estrutura de dados que pode ser usada para armazenar pares chave-valor. Assim como sugere o seu nome, ela foi projetada para manter dados de formulários, ou seja, você pode usá-la com o JavaScript para criar um objeto que corresponda a um formulário de HTML. Em geral, ela é útil quando precisamos enviar dados do formulário para endpoints de uma API RESTful, por exemplo, para fazer o upload de um ou de vários arquivos usando a interface XMLHttpRequest, a API fetch() ou o Axios.

Você pode criar um objeto na FormData instanciando a interface FormData usando o operador new, conforme segue:

const formData = new FormData()

A referência de formData é a uma instância de FormData. Você pode chamar diversos métodos no objeto para adicionar e trabalhar com pares de dados. Cada par tem uma chave e um valor.

Esses são os métodos disponíveis nos objetos de FormData:

  • append() : usado para anexar um par chave-valor ao objeto. Se a chave já existir, o valor é anexado ao valor original daquela chave;
  • delete(): usado para excluir um par chave-valor;
  • entries(): retorna um objeto Iterator que você pode usar para percorrer o objeto na forma de uma lista de seus pares chave-valor;
  • get(): usado para retornar o valor de uma chave. Se vários valores forem anexados, retorna o primeiro valor;
  • getAll(): usado para retornar todos os valores de uma chave específica;
  • has(): usado para verificar se uma chave existe;
  • keys(): retorna um objeto Iterator que você pode usar para listar as chaves disponíveis do objeto;
  • set():  usado para adicionar um valor ao objeto com a chave especificada. Ele substituirá o valor caso a chave já exista;
  • values():  retorna um objeto Iterator para os valores do objeto FormData.

Exemplo de upload de arquivos em JavaScript puro

Vamos ver agora um exemplo simples de upload de arquivos usando o JavaScript puro, XMLHttpRequest e FormData.

Navegue até a pasta de trabalho e crie o arquivo index.html com o seguinte conteúdo:

<!DOCTYPE html>
<html>

<head>
	<title>Parcel Sandbox</title>
	<meta charset="UTF-8" />
</head>

<body>
	<div id="app"></div>

	<script src="index.js">
	</script>
</body>

</html>

Simplesmente criamos um documento HTML com uma <div> identificada pelo id app. Em seguida, incluímos o arquivo index.js usando uma tag <script>.

Depois, criamos o arquivo index.js e adicionamos a ele o código abaixo:

document.getElementById("app").innerHTML = `
<h1>File Upload & FormData Example</h1>
<div>
<input type="file" id="fileInput" />
</div>
`;

const fileInput = document.querySelector("#fileInput");

const uploadFile = file => {
  console.log("Uploading file...");
  const API_ENDPOINT = "https://file.io";
  const request = new XMLHttpRequest();
  const formData = new FormData();

  request.open("POST", API_ENDPOINT, true);
  request.onreadystatechange = () => {
    if (request.readyState === 4 && request.status === 200) {
      console.log(request.responseText);
    }
  };
  formData.append("file", file);
  request.send(formData);
};

fileInput.addEventListener("change", event => {
  const files = event.target.files;
  uploadFile(files[0]);
});

Primeiro, inserimos um elemento <input type="file" id="fileInput" /> em nossa página HTML. Ele será usado para selecionar o arquivo do qual faremos o upload.

Então, consultamos o elemento de entrada de arquivo usando o método querySelector().

Depois, definimos o método uploadFile() no qual, primeiro, declaramos uma variável API_ENDPOINT, que mantém o endereço de nosso endpoint para o upload do arquivo. Em seguida, criamos uma solicitação a XMLHttpRequest e um objeto FormData vazio.

Usamos o método append de FormData para anexar o arquivo, passado como parâmetro para o método uploadFile(), para a chave file. Isso criará um par chave-valor com file como chave e o conteúdo do arquivo passado como valor.

Após fazermos isso, enviamos a solicitação usando o método send() de XMLHttpRequest e passamos o objeto FormData como argumento.

Depois de definir o método uploadFile(), escutamos o evento de alteração no elemento <input> e chamamos o método uploadFile() com o arquivo selecionado como argumento. O arquivo é acessado pelo array event.target.files.

Você pode experimentar com o exemplo deste sandbox de código:

Fazendo o upload de vários arquivos

Você pode facilmente modificar o código acima para dar suporte ao upload de vários arquivos.

Primeiro, você precisa adicionar a propriedade multiple ao elemento <input>:

<input type="file" id="fileInput" multiple />

Agora, você poderá selecionar diversos arquivos da sua unidade.

Em seguida, altere o método uploadFile() para que aceite um array de arquivos como argumento e simplesmente percorra o array e anexe os arquivos ao objeto de FormData:

const uploadFile = (files) => {
  console.log("Uploading file...");
  const API_ENDPOINT = "https://file.io";
  const request = new XMLHttpRequest();
  const formData = new FormData();

  request.open("POST", API_ENDPOINT, true);
  request.onreadystatechange = () => {
    if (request.readyState === 4 && request.status === 200) {
      console.log(request.responseText);
    }
  };
  
  for (let i = 0; i < files.length; i++) {
    formData.append(files[i].name, files[i])
  }
  request.send(formData);
};

Por fim, chame o método com um array de arquivos como argumento:

fileInput.addEventListener("change", event => {
  const files = event.target.files;
  uploadFile(files);
});

Quando tiver a oportunidade, confira esses tutoriais avançados (em inglês) sobre como usar FormData com Angular, Ionic e React: