Como faço para enviar um arquivo com a API de busca JS?

170

Eu ainda estou tentando envolver minha cabeça em torno disso.

Posso fazer com que o usuário selecione o arquivo (ou até vários) com a entrada do arquivo:

<form>
  <div>
    <label>Select file to upload</label>
    <input type="file">
  </div>
  <button type="submit">Convert</button>
</form>

E eu posso pegar o submitevento usando <fill in your event handler here>. Mas quando eu faço, como envio o arquivo usando fetch?

fetch('/files', {
  method: 'post',
  // what goes here? What is the "body" for this? content-type header?
}).then(/* whatever */);
deitch
fonte
1
documento oficial funciona para mim depois de tentar algumas respostas falharem: developer.mozilla.org/en-US/docs/Web/API/Fetch_API/… , algo pode confirmar: 1. precisa quebrar o arquivo no FromData; 2. não precisa declarar Content-Type: multipart/form-datano cabeçalho do pedido
Spark.Bao

Respostas:

127

Este é um exemplo básico com comentários. A uploadfunção é o que você está procurando:

// Select your input type file and store it in a variable
const input = document.getElementById('fileinput');

// This will upload the file after having read it
const upload = (file) => {
  fetch('http://www.example.net', { // Your POST endpoint
    method: 'POST',
    headers: {
      // Content-Type may need to be completely **omitted**
      // or you may need something
      "Content-Type": "You will perhaps need to define a content-type here"
    },
    body: file // This is your file object
  }).then(
    response => response.json() // if the response is a JSON object
  ).then(
    success => console.log(success) // Handle the success response object
  ).catch(
    error => console.log(error) // Handle the error response object
  );
};

// Event handler executed when a file is selected
const onSelectFile = () => upload(input.files[0]);

// Add a listener on your input
// It will be triggered when a file will be selected
input.addEventListener('change', onSelectFile, false);
Damien
fonte
8
Por que este exemplo inclui cabeçalhos de tipo de conteúdo, mas outra resposta diz para omiti-los ao enviar arquivos com a API Fetch? Qual é esse?
Jjrabbit 24/02/19
12
NÃO defina o tipo de conteúdo. Passei muito tempo tentando fazê-lo funcionar e, em seguida, encontrei este artigo dizendo para não defini-lo. E funciona! muffinman.io/uploading-files-using-fetch-multipart-form-data
Kostiantyn
como você ler este arquivo, digamos, Express back-end. Como o arquivo não é enviado como dados do formulário. Em vez disso, é enviado apenas como o objeto de arquivo. Express-fileupload ou multer analisa essas cargas úteis?
sakib11 22/04
221

Eu fiz assim:

var input = document.querySelector('input[type="file"]')

var data = new FormData()
data.append('file', input.files[0])
data.append('user', 'hubot')

fetch('/avatars', {
  method: 'POST',
  body: data
})
Integ
fonte
16
Você não precisa agrupar o conteúdo do arquivo em um FormDataobjeto se tudo o que você estiver carregando for o arquivo (que é o que a pergunta original deseja). fetchaceitará input.files[0]acima como bodyparâmetro.
Klaus
17
Se você possui um back-end PHP manipulando o upload do arquivo, desejará agrupar o arquivo em um FormData para que a matriz $ _FILES seja preenchida corretamente.
ddelrio1986
3
Também notei que o Google Chrome não mostraria o arquivo na carga útil da solicitação sem a parte FormData por algum motivo. Parece um bug no painel Rede do Google Chrome.
ddelrio1986
4
Esta deve realmente ser a resposta correta. A outra maneira funciona também, mas é mais complicado
jnmandal
o que você quer dizer com / avatares? Você está se referindo a algum ponto de extremidade da API de back-end?
Kartikeya Mishra
90

Uma observação importante para o envio de arquivos com a API de busca

É necessário omitir o content-typecabeçalho da solicitação de busca. Em seguida, o navegador adicionará automaticamente o Content typecabeçalho, incluindo o limite do formulário, que se parece com

Content-Type: multipart/form-data; boundary=—-WebKitFormBoundaryfgtsKTYLsT7PNUVD

O limite do formulário é o delimitador para os dados do formulário

madhu131313
fonte
17
ISTO! Muito importante! Não use seu próprio tipo de conteúdo com busca em multipartes. Eu não tinha ideia de por que meu código não funciona.
Ernestas Stankevičius
1
Isto é ouro! Eu perdi 1 hora sem entender isso. Obrigado por compartilhar esta dica
Ashwin Prabhu
1
Voto negativo, mesmo que seja uma informação útil, mas isso não tenta responder à pergunta do OP.
toraritte
3
Esta é uma informação muito importante que não é capturada nos documentos MDN Fetch .
Plasty Grove
36

Se você deseja vários arquivos, pode usar este

var input = document.querySelector('input[type="file"]')

var data = new FormData()
for (const file of input.files) {
  data.append('files',file,file.name)
}

fetch('/avatars', {
  method: 'POST',
  body: data
})
Alex Montoya
fonte
@ Saly3301 Eu tive o mesmo problema, foi porque minha função API estava tentando converter o formData para JSON. (Eu só comentou sobre o off chance de que ele ajuda a alguém)
mp035
19

Para enviar um único arquivo, você pode simplesmente usar o Fileobjeto do input's .filesgama diretamente como o valor body:em sua fetch()initializer:

const myInput = document.getElementById('my-input');

// Later, perhaps in a form 'submit' handler or the input's 'change' handler:
fetch('https://example.com/some_endpoint', {
  method: 'POST',
  body: myInput.files[0],
});

Isso funciona porque Fileherda de Blobe Blobé um dos BodyInittipos permitidos definidos no padrão de busca.

Mark Amery
fonte
Esta é a resposta mais simples, mas como body: myInput.files[0]causa a quantidade de bytes mantidos na memória no lado do cliente?
precisa saber é
2
Eu esperaria que, com esta solução, o navegador fosse sensato o suficiente para transmitir o arquivo e não exigisse que ele fosse lido na memória, @bhantol, mas não fiz o possível para descobrir (empiricamente ou investigando as especificações). Se você quiser confirmar, você pode tentar (em cada um dos principais navegadores) usar essa abordagem para fazer upload de um arquivo de 50 GB ou algo assim e verificar se o seu navegador tenta usar muita memória e se mata.
Mark Amery
não funcionou para mim. express-fileuploadfalha ao analisar o fluxo de solicitação. Mas FormDatafunciona como um encanto.
Attacomsian
1
@attacomsian À primeira vista, parece-me express-fileuploaduma biblioteca do lado do servidor para lidar com multipart/form-datasolicitações que contêm arquivos, então sim, não é compatível com essa abordagem (que envia o arquivo diretamente como corpo da solicitação).
Mark Amery
6

A resposta aceita aqui é um pouco datada. Em abril de 2020, uma abordagem recomendada vista no site da MDN sugere o uso FormDatae também não solicita a definição do tipo de conteúdo. https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

Estou citando o trecho de código por conveniência:

const formData = new FormData();
const fileField = document.querySelector('input[type="file"]');

formData.append('username', 'abc123');
formData.append('avatar', fileField.files[0]);

fetch('https://example.com/profile/avatar', {
  method: 'PUT',
  body: formData
})
.then((response) => response.json())
.then((result) => {
  console.log('Success:', result);
})
.catch((error) => {
  console.error('Error:', error);
});
Raiyan
fonte
1
O uso FormDatasó funcionará se o servidor estiver esperando dados do formulário. Se o servidor desejar um arquivo bruto como o corpo do POST, a resposta aceita estará correta.
Clyde
2

Partindo da abordagem de Alex Montoya para vários elementos de entrada de arquivo

const inputFiles = document.querySelectorAll('input[type="file"]');
const formData = new FormData();

for (const file of inputFiles) {
    formData.append(file.name, file.files[0]);
}

fetch(url, {
    method: 'POST',
    body: formData })
Jerald Macachor
fonte
1

O problema para mim era que eu estava usando um response.blob () para preencher os dados do formulário. Aparentemente, você não pode fazer isso pelo menos com o reagir nativo, então acabei usando

data.append('fileData', {
  uri : pickerResponse.uri,
  type: pickerResponse.type,
  name: pickerResponse.fileName
 });

A busca parece reconhecer esse formato e enviar o arquivo para o qual a uri está apontando.

NickJ
fonte
0

Aqui está o meu código:

html:

const upload = (file) => {
    console.log(file);

    

    fetch('http://localhost:8080/files/uploadFile', { 
    method: 'POST',
    // headers: {
    //   //"Content-Disposition": "attachment; name='file'; filename='xml2.txt'",
    //   "Content-Type": "multipart/form-data; boundary=BbC04y " //"multipart/mixed;boundary=gc0p4Jq0M2Yt08jU534c0p" //  ή // multipart/form-data 
    // },
    body: file // This is your file object
  }).then(
    response => response.json() // if the response is a JSON object
  ).then(
    success => console.log(success) // Handle the success response object
  ).catch(
    error => console.log(error) // Handle the error response object
  );

  //cvForm.submit();
};

const onSelectFile = () => upload(uploadCvInput.files[0]);

uploadCvInput.addEventListener('change', onSelectFile, false);
<form id="cv_form" style="display: none;"
										enctype="multipart/form-data">
										<input id="uploadCV" type="file" name="file"/>
										<button type="submit" id="upload_btn">upload</button>
</form>
<ul class="dropdown-menu">
<li class="nav-item"><a class="nav-link" href="#" id="upload">UPLOAD CV</a></li>
<li class="nav-item"><a class="nav-link" href="#" id="download">DOWNLOAD CV</a></li>
</ul>

Andreas Patsimas
fonte
1
Da avaliação: Olá, por favor, não responda apenas com o código fonte. Tente fornecer uma boa descrição sobre como sua solução funciona. Veja: Como escrevo uma boa resposta? . Thanks
Enviado em 13/12/19