Jak przesłać plik za pomocą interfejsu JS Fetch API?


170

Wciąż staram się owinąć wokół tego głowę.

Mogę poprosić użytkownika o wybranie pliku (lub nawet wielu) za pomocą wejścia pliku:

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

I mogę złapać submitwydarzenie za pomocą <fill in your event handler here>. Ale kiedy już to zrobię, jak wysłać plik przy użyciu fetch?

fetch('/files', {
  method: 'post',
  // what goes here? What is the "body" for this? content-type header?
}).then(/* whatever */);

1
oficjalny dokument działa dla mnie po tym, jak niektóre odpowiedzi nie powiodły się: developer.mozilla.org/en-US/docs/Web/API/Fetch_API/ ... coś może potwierdzić: 1. potrzebujesz pliku zawijania w FromData; 2. nie trzeba deklarować Content-Type: multipart/form-dataw nagłówku żądania
— Spark.Bao

Odpowiedzi:


127

To jest podstawowy przykład z komentarzami. uploadFunkcją jest to, czego szukasz:

// 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);

8
Dlaczego ten przykład zawiera nagłówki Content-Type, ale inna odpowiedź mówi, aby je pominąć podczas wysyłania plików za pomocą Fetch API? Który to?
— jjrabbit

12
NIE ustawiaj typu zawartości. Spędziłem dużo czasu, starając się, aby to działało, a następnie znalazłem ten artykuł, w którym napisano, że nie należy go ustawiać. I to działa! muffinman.io/uploading-files-using-fetch-multipart-form-data
— Kostiantyn

jak odczytasz ten plik z, powiedzmy, zaplecza Express. Ponieważ plik nie jest wysyłany jako dane formularza. Zamiast tego jest wysyłany jako po prostu obiekt pliku. Czy express-fileupload lub multer analizuje takie ładunki?
— sakib11

221

Zrobiłem to tak:

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
})

16
Nie musisz zawijać zawartości pliku w FormDataobiekt, jeśli wszystko, co przesyłasz, to plik (czego chce pierwotne pytanie). fetchzaakceptuje input.files[0]powyższe jako swój bodyparametr.
— Klaus

17
Jeśli masz zaplecze PHP obsługujące przesyłanie plików, będziesz chciał opakować plik w FormData, aby tablica $ _FILES została prawidłowo wypełniona.
— ddelrio1986

2
Zauważyłem również, że Google Chrome z jakiegoś powodu nie wyświetla pliku w ładunku żądania bez części FormData. Wygląda na błąd w panelu sieci przeglądarki Google Chrome.
— ddelrio1986

4
To naprawdę powinna być prawidłowa odpowiedź. Drugi sposób też działa, ale jest bardziej
— zawiły

co masz na myśli mówiąc / avatary? Czy odnosisz się do jakiegoś punktu końcowego interfejsu API zaplecza?
— Kartikeya Mishra

90

Ważna uwaga dotycząca wysyłania plików za pomocą Fetch API

W content-typeżądaniu Fetch należy pominąć nagłówek. Następnie przeglądarka automatycznie doda Content typenagłówek zawierający wyglądającą granicę formularza

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

Granica formularza jest ogranicznikiem danych formularza


17
TO! Bardzo ważne! Nie używaj własnego typu zawartości do pobierania w wielu częściach. Nie miałem pojęcia, dlaczego mój kod nie działa.
— Ernestas Stankevičius


1
To jest złoto! Zmarnowałem godzinę, nie rozumiejąc tego. Dzięki za podzielenie się tą wskazówką
— Ashwin Prabhu,

1
Głosuj przeciw, bo chociaż to przydatne informacje, ale to nie jest próbą odpowiedzi na pytanie OP.
— toraritte

3
To bardzo ważna informacja, która nie jest przechwytywana w dokumentacji MDN Fetch .
— Plasty Grove

36

Jeśli chcesz mieć wiele plików, możesz użyć tego

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
})

@ Saly3301 Miałem ten sam problem, ponieważ moja funkcja API próbowała przekonwertować formData na JSON. (Skomentowałem tylko szansę, że komuś to pomaga)
— mp035

19

Aby przesłać pojedynczy plik, możesz po prostu użyć Fileobiektu z input„s .filestablicy bezpośrednio jako wartość body:w fetch()inicjatora:

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],
});

To działa, ponieważ Filedziedziczy po Blobi Blobjest jednym z dopuszczalnych BodyInittypów zdefiniowanych w Fetch Standard.


To jest najprostsza odpowiedź, ale jak wpływa body: myInput.files[0]na ilość bajtów przechowywanych w pamięci po stronie klienta?
— bhantol,

2
Chciałbym się spodziewać , że z tego rozwiązania przeglądarka byłoby sensowne wystarczy strumień plik i nie wymagają do odczytu do pamięci, @bhantol, ale nie wyszło mi z drogi, aby dowiedzieć się (albo empirycznie lub zagłębiając specyfikacja). Jeśli chcesz to potwierdzić, możesz spróbować (w każdej z głównych przeglądarek), używając tego podejścia, aby przesłać plik o rozmiarze 50 GB lub coś w tym stylu i sprawdzić, czy twoja przeglądarka próbuje użyć zbyt dużo pamięci i zostaje zabita.
— Mark Amery,

Nie działa dla mnie. express-fileuploadnie udało się przeanalizować strumienia żądań. Ale FormDatadziała jak urok.
— attacomsian

1
@attacomsian Na pierwszy rzut oka wydaje mi się, że express-fileuploadjest to biblioteka po stronie serwera do obsługi multipart/form-datażądań zawierających pliki, więc tak, nie jest zgodna z tym podejściem (które po prostu wysyła plik bezpośrednio jako treść żądania).
— Mark Amery,

6

Przyjęta tutaj odpowiedź jest nieco przestarzała. Od kwietnia 2020 r. Zalecane podejście widoczne na stronie MDN sugeruje użycie, FormDataale także nie wymaga ustawiania typu treści.https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

Dla wygody cytuję fragment kodu:

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);
});

1
Używanie FormDatabędzie działać tylko wtedy, gdy serwer oczekuje danych z formularza. Jeśli serwer chce pliku surowego jako treści POST, zaakceptowana odpowiedź jest poprawna.
— Clyde

2

Odejście od podejścia Alexa Montoyi do wielu elementów wejściowych plików

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 })

1

Problem polegał na tym, że użyłem response.blob () do wypełnienia danych formularza. Najwyraźniej nie możesz tego zrobić przynajmniej z React Native, więc ostatecznie użyłem

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

Wydaje się, że program Fetch rozpoznaje ten format i wysyła plik wskazany przez uri.


0

Oto mój kod:

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>


1
Z recenzji: Cześć, nie odpowiadaj tylko za pomocą kodu źródłowego. Postaraj się przedstawić miły opis tego, jak działa Twoje rozwiązanie. Zobacz: Jak napisać dobrą odpowiedź? . Dzięki
— sɐunıɔ ןɐ qɐp
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.