JavaScript: Upload file
Asked Answered
Z

4

311

Let's say I have this element on the page:

<input id="image-file" type="file" />

This will create a button that allows the users of the web page to select a file via an OS "File open..." dialog in the browser.

Let's say the user clicks said button, selects a file in the dialog, then clicks the "Ok" button to close the dialog.

The selected file name is now stored in:

document.getElementById("image-file").value

Now, let's say that the server handles multi-part POSTs at the URL "/upload/image".

How do I send the file to "/upload/image"?

Also, how do I listen for notification that the file is finished uploading?

Zorine answered 7/4, 2011 at 21:36 Comment(3)
JavaScript is not handling the uploads, because it is serverside. The server side script will recieve the file, and then move it. For php from temporary folder to the desired folder.Finish
i found a nice solution using PHP hereTiffanytiffi
Voted to reopen because the question is about POJS (plain old javascript) not jQuery.Censure
I
246

Pure JS

You can use fetch optionally with await-try-catch

let photo = document.getElementById("image-file").files[0];
let formData = new FormData();
     
formData.append("photo", photo);
fetch('/upload/image', {method: "POST", body: formData});

async function SavePhoto(inp) 
{
    let user = { name:'john', age:34 };
    let formData = new FormData();
    let photo = inp.files[0];      
         
    formData.append("photo", photo);
    formData.append("user", JSON.stringify(user)); 
    
    const ctrl = new AbortController()    // timeout
    setTimeout(() => ctrl.abort(), 5000);
    
    try {
       let r = await fetch('/upload/image', 
         {method: "POST", body: formData, signal: ctrl.signal}); 
       console.log('HTTP response code:',r.status); 
    } catch(e) {
       console.log('Huston we have problem...:', e);
    }
    
}
<input id="image-file" type="file" onchange="SavePhoto(this)" >
<br><br>
Before selecting the file open chrome console > network tab to see the request details.
<br><br>
<small>Because in this example we send request to https://stacksnippets.net/upload/image the response code will be 404 ofcourse...</small>

<br><br>
(in stack overflow snippets there is problem with error handling, however in <a href="https://jsfiddle.net/Lamik/b8ed5x3y/5/">jsfiddle version</a> for 404 errors 4xx/5xx are <a href="https://mcmap.net/q/101358/-try-catch-not-catching-async-await-errors">not throwing</a> at all but we can read response status which contains code)

Old school approach - xhr

let photo = document.getElementById("image-file").files[0];  // file from input
let req = new XMLHttpRequest();
let formData = new FormData();

formData.append("photo", photo);                                
req.open("POST", '/upload/image');
req.send(formData);

function SavePhoto(e) 
{
    let user = { name:'john', age:34 };
    let xhr = new XMLHttpRequest();
    let formData = new FormData();
    let photo = e.files[0];      
    
    formData.append("user", JSON.stringify(user));   
    formData.append("photo", photo);
    
    xhr.onreadystatechange = state => { console.log(xhr.status); } // err handling
    xhr.timeout = 5000;
    xhr.open("POST", '/upload/image'); 
    xhr.send(formData);
}
<input id="image-file" type="file" onchange="SavePhoto(this)" >
<br><br>
Choose file and open chrome console > network tab to see the request details.
<br><br>
<small>Because in this example we send request to https://stacksnippets.net/upload/image the response code will be 404 ofcourse...</small>

<br><br>
(the stack overflow snippets, has some problem with error handling - the xhr.status is zero (instead of 404) which is similar to situation when we run script from file on <a href="https://mcmap.net/q/101359/-xmlhttprequest-status-0-responsetext-is-empty">local disc</a> - so I provide also js fiddle version which shows proper http error code <a href="https://jsfiddle.net/Lamik/k6jtq3uh/2/">here</a>)

SUMMARY

  • In server side you can read original file name (and other info) which is automatically included to request by browser in filename formData parameter.
  • You do NOT need to set request header Content-Type to multipart/form-data - this will be set automatically by browser (which will include the mandatory boundary parameter).
  • Instead of /upload/image you can use full address like http://.../upload/image (of course both addresses are arbitrary and depends on server - and same situation with param method - usually on servers "POST" is used for file upload but sometimes "PUT" or other can be used).
  • If you want to send many files in single request use multiple attribute: <input multiple type=... />, and attach all chosen files to formData in similar way (e.g. photo2=...files[2];... formData.append("photo2", photo2);)
  • You can include additional data (json) to request e.g. let user = {name:'john', age:34} in this way: formData.append("user", JSON.stringify(user));
  • You can set timeout: for fetch using AbortController, for old approach by xhr.timeout= milisec
  • This solutions should work on all major browsers.
Ishtar answered 29/6, 2018 at 21:31 Comment(10)
formData === form submit enctype="multipart/form-data"Vinasse
I am getting error net::ERR_ABORTED 405 (Method Not Allowed)Dichroscope
@HidaytRahman this err usually appears when serwer not implement POST method for your urlPetrel
Weird - As per your heading i was thinking we not longer required server :DDichroscope
I am getting POST http://localhost:8000/upload/image 404 (Not Found). Created /upload/image under /src and /public in my React Node test project.Despot
@JayJ probably the problem is on the serwer side - it not recognise your urlPetrel
@KamilKiełczewski thanks. Even if I say ‘/‘ in the fetch it behaves the same. This is a Node implementation. Any pointers on where maybe to configure? localhost:8000 properly serves up the homepage so the url works.Despot
@trinalbadger587 can you give more details (including your definition of correctly bound headers) ?Petrel
@KamilKiełczewski, in the end, this was actually just an ASP.NET issue.Reitman
At the risk of sounding pedantic... Houston is spelled Houston.Snyder
B
107

Unless you're trying to upload the file using ajax, just submit the form to /upload/image.

<form enctype="multipart/form-data" action="/upload/image" method="post">
    <input id="image-file" type="file" />
</form>

If you do want to upload the image in the background (e.g. without submitting the whole form), you can use ajax:

Bauer answered 7/4, 2011 at 21:38 Comment(4)
or iframe 0x0 size with image uploader script inside its src="" and post the form to it using target="iframeId" reading the result back on iframe src .load event which can be bound using jQuery for example.Bacardi
It's actually possible to upvote via Javascript and Ajax nowadays, see: https://mcmap.net/q/53558/-jquery-ajax-file-upload (I didn't downvote).Thibodeau
@KajMagnus, you probably meant 'upload' but accidentally said 'upvote'Cremona
I tried this method, but the request option does not seem to contain the image file in the serverChrista
N
6

I have been trying to do this for a while and none of these answers worked for me. This is how I did it.

I had a select file and a submit button

<input type="file" name="file" id="file">
<button onclick="doupload()" name="submit">Upload File</button>

Then in my javascript code I put this

function doupload() {
    let data = document.getElementById("file").files[0];
    let entry = document.getElementById("file").files[0];
    console.log('doupload',entry,data)
    fetch('uploads/' + encodeURIComponent(entry.name), {method:'PUT',body:data});
    alert('your file has been uploaded');
    location.reload();
};

If you like StackSnippets...

function doupload() {
    let data = document.getElementById("file").files[0];
    let entry = document.getElementById("file").files[0];
    console.log('doupload',entry,data)
    fetch('uploads/' + encodeURIComponent(entry.name), {method:'PUT',body:data});
    alert('your file has been uploaded');
};
<input type="file" name="file" id="file">
<button onclick="doupload()" name="submit">Upload File</button>

The PUT method is slightly different than the POST method. In this case, in web server for chrome, the POST method is not implemented.

Tested with web server for chrome - https://chrome.google.com/webstore/detail/web-server-for-chrome/ofhbbkphhbklhfoeikjpcbhemlocgigb?hl=en

Note- When using web server for chrome you need to go into advanced options and check the option 'enable file upload'. If you do not, you will get an error for not allowed.

Napolitano answered 6/4, 2021 at 19:46 Comment(0)
I
3

Here is the answer for submission of form with files that is not in form:

const formData = new FormData();
const files = event.target.files;

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

$.ajax({
   type: "POST",
   url: urlString,
   data: formData,
   error : function (result) {
       console.log('error', result);
   },
   success : function (result) {
      console.log('success', result)
   }
});
Incubus answered 24/2, 2023 at 11:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.