Adding a file display list to a multi-file upload HTML control

This post is more than 2 years old.

I'm working on something a bit interesting with a multi-file upload control, but while that is in development, I thought I'd share a quick tip about working with multi-file upload controls in general.

If you are not clear about what I'm talking about, I simply mean adding the multiple attribute to the input tag for file uploads. Like so:

<input type="file" name="foo" id="foo" multiple>

In browsers that support it, the user will be able to select multiple files. In browsers that don't support it, it still works fine as a file control, but they are limited to one file. In theory, this is pretty trivial to use, but there's a UX issue that kind of bugs me. Here is a screen shot of a form using this control. I've selected two files:

Notice something? The user isn't told what files they selected. Now obviously in a form this small it isn't that big of a deal, but in a larger form the user may forget or simply want to double check before they submit the form. Unfortunately there is no way to do that. Clicking the Browse button simply opens the file picker again. Surprisingly, IE handles this the best. It provides a read-only list of what you selected:

One could use a bit of CSS to make that field a bit larger for sure and easier to read, but you get the idea. So how can we provide some feedback to the user about what files they have selected?

First, let's add a simple change handler to our input field:

document.addEventListener("DOMContentLoaded", init, false);
	
function init() {
	document.querySelector('#files').addEventListener('change', handleFileSelect, false);
}

Next, let's write an event handler and see if we can get access to the files property of the event. Not all browsers support this, but in the ones that do, we can enumerate over them.

function handleFileSelect(e) {
		
	if(!e.target.files) return;
		
	var files = e.target.files;
	for(var i=0; i < files.length; i++) {
		var f = files[i];
	}
		
}

The file object gives us a few properties, but the one we care about is the name. So let's create a full demo of this. I'm going to add a little div below my input field and use it as place to list my files.

<!doctype html>
<html>
<head>
<title>Proper Title</title>
</head>
    
<body>
	
	<form id="myForm" method="post" enctype="multipart/form-data">

        Files: <input type="file" id="files" name="files" multiple><br/>

        <div id="selectedFiles"></div>

        <input type="submit">
	</form>

	<script>
	var selDiv = "";
		
	document.addEventListener("DOMContentLoaded", init, false);
	
	function init() {
		document.querySelector('#files').addEventListener('change', handleFileSelect, false);
		selDiv = document.querySelector("#selectedFiles");
	}
		
	function handleFileSelect(e) {
		
		if(!e.target.files) return;
		
		selDiv.innerHTML = "";
		
		var files = e.target.files;
		for(var i=0; i<files.length; i++) {
			var f = files[i];
			
			selDiv.innerHTML += f.name + "<br/>";

		}
		
	}
	</script>

</body>
</html>

Pretty simple, right? You can view an example of this here: https://static.raymondcamden.com/demos/2013/sep/10/test0A.html. And here is a quick screen shot in case you are viewing this in a non-compliant browser.

Pretty simple, right? Let's kick it up a notch. Some browsers support FileReader (MDN Reference), a basic way of reading files on the user system. We could check for FileReader support and use it to provide image previews. I'll share the code first and then explain how it works.

Edit on September 11: A big thank you to Sime Vidas for pointing out a stupid little bug in my code I missed on first pass around. I made a classic array/callback bug and didn't notice it. I fixed the code and the screen shot, but if you want to see the broken code, view source on https://static.raymondcamden.com/demos/2013/sep/10/test0orig.html.

<!doctype html>
<html>
<head>
<title>Proper Title</title>
<style>
	#selectedFiles img {
		max-width: 125px;
		max-height: 125px;
		float: left;
		margin-bottom:10px;
	}
</style>
</head>
    
<body>
	
	<form id="myForm" method="post" enctype="multipart/form-data">

        Files: <input type="file" id="files" name="files" multiple accept="image/*"><br/>

        <div id="selectedFiles"></div>

        <input type="submit">
	</form>

	<script>
	var selDiv = "";
		
	document.addEventListener("DOMContentLoaded", init, false);
	
	function init() {
		document.querySelector('#files').addEventListener('change', handleFileSelect, false);
		selDiv = document.querySelector("#selectedFiles");
	}
		
	function handleFileSelect(e) {
		
		if(!e.target.files || !window.FileReader) return;

		selDiv.innerHTML = "";
		
		var files = e.target.files;
		var filesArr = Array.prototype.slice.call(files);
		filesArr.forEach(function(f) {
			var f = files[i];
			if(!f.type.match("image.*")) {
				return;
			}

			var reader = new FileReader();
			reader.onload = function (e) {
				var html = "<img src=\"" + e.target.result + "\">" + f.name + "<br clear=\"left\"/>";
				selDiv.innerHTML += html;				
			}
			reader.readAsDataURL(f); 
		});
		
	}
	</script>

</body>
</html>

I've modified the handleFileSelect code to check for both the files array as well as FileReader. (Note - I should do this before I even attach the event handler. I just thought of that.) I've updated my input field to say it accepts only images and added a second check within the event handler. Once we are sure we have an image, I use the FileReader API to create a DataURL (string) version of the image. With that I can actually draw the image as a preview.

You can view a demo of this here: https://static.raymondcamden.com/demos/2013/sep/10/test0.html. And again, a screen shot:

Check it out and let me know what you think. As I said, it should be fully backwards compatible (in that it won't break) and works well in Chrome, Firefox, IE10, and Safari.

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by David Jennings posted on 9/10/2013 at 6:50 PM

Would be good if the preview thumbnails could just be converted to base64 and saved to the server as a thumbnail. Rather than having to upload the full size image

Comment 2 by Raymond Camden posted on 9/10/2013 at 6:54 PM

Data URLs are base64 with some stuff in front, so you could use that. But I'm not sure it would be better to save it to the server. Why send that data to the server when I want to preview it locally? The end goal (in the last demo) is to upload the images, not the previews. The previews are for the user's sake and don't need to be on the server, know what I mean?

Now - you may ask - what if we want to perform resizes before uploading? That could be useful for large images. That could be done with Canvas I believe. But I haven't tried that myself.

Comment 3 by Salvatore fusto posted on 9/10/2013 at 7:15 PM

Ray,
tested demos with Chrome and Firefox: i don't see a list of thumbs, but last image i've just selected.
regards

Comment 4 by Raymond Camden posted on 9/10/2013 at 7:17 PM

What version of Chrome?

Comment 5 by Salvatore fusto posted on 9/10/2013 at 7:30 PM

Chrome: 29.0.1547.66 m
Firefox: 22

Comment 6 by Raymond Camden posted on 9/10/2013 at 7:38 PM

Ok, FF22 is 2 behind me, I'm on beta channel. But at least it didn't break, which is good.

Chrome... are you on mobile chrome?

Comment 7 by Salvatore fusto posted on 9/10/2013 at 8:00 PM

no, i'm on my pc with windows 7 ultimate.
if i select many images, i always see th last selected, as thumb and with its description on the right of the select file button.

Comment 8 by Raymond Camden posted on 9/10/2013 at 8:42 PM

Fascinating. I tested IE on Win8. So - do you mind downloading the HTML and doing some editing to see if you can figure it out yourself?

Comment 9 by Woo posted on 9/10/2013 at 8:46 PM

Works fine on Win 7 Pro latest FF & Chrome.

Some progress bar when uploading would be useful. Looking forward to finished script.

Comment 10 by Raymond Camden posted on 9/10/2013 at 8:49 PM

Progress bar for uploads is definitely possible.

Comment 11 by Šime Vidas posted on 9/11/2013 at 7:21 AM

Hey, I've refactored the code a bit: http://jsfiddle.net/simevid.... I'm still not satisfied since there is still DOM serialization going on (elem.innerHTML += string), but I did fix that bug where all files share the name of the last read file (see the last image in your post).

Comment 12 by Raymond Camden posted on 9/11/2013 at 2:23 PM

Oh lord I did the classic callback/array thing and didn't notice. Thanks. Going to fix and edit the post so folks don't copy it. While I agree about innerHTML+, imo, I wanted that aspect to be as simple and direct as possible and not direct from the main topic.

Comment 13 by Raymond Camden posted on 9/11/2013 at 2:36 PM

Sime - updated. Used the toArray method MDN uses in the Arguments docs. Also credited you and updated the screen shot.

Comment 14 by Raymond posted on 2/10/2014 at 7:43 PM

Great application but
1= Is it possible to make it work in IE* and up.

2 = Is it possible to keep the file name in a var. so I could write it down in a response.write in asp.

Thank you very much in advance. Great job

Comment 15 by Raymond Camden posted on 2/10/2014 at 10:29 PM

@Raymond: I could fire up a VM, but I'd rather walk you through the steps. :) What did you test? The first thing would be to test input type=file/multiple and see when IE begins to suport it.

As for 2, you don't need to do that. It should be available to your server side. I know ColdFusion supports this just fine (giving you both the client file name and the server one, in case it was renamed).

Comment 16 by Roman posted on 2/13/2014 at 6:32 AM

This is very good example of file upload approach! But one part is missing here - how about if user needed remove one , 2 or all files from selection and select different files?

Comment 17 by Raymond Camden posted on 2/15/2014 at 7:19 PM

See the next post: http://www.raymondcamden.co....

In that I talk about the behavior of multiple clicks on the control. The default is to wipe away the last selection. If you use what I did in the other post, you could add a simple delete link and remove the selection from the list of stuff to upload.

Comment 18 by Ansar Abbas posted on 4/13/2014 at 11:04 AM

Thanks a lot Raymond Camden and Sime Vidas. You guys are great!

Comment 19 by Ansar Abbas posted on 4/13/2014 at 11:30 AM

Kindly tell me how do i deselect a file from selected list?

Comment 20 by Raymond Camden posted on 4/14/2014 at 6:29 AM

I've got a new demo for this - will blog it (and link it) in a bit (or tomorrow morning).

Comment 21 by Raymond Camden posted on 4/14/2014 at 6:09 PM
Comment 22 by AB posted on 9/8/2014 at 2:41 PM

Its also not working on IE9

Comment 23 by Raymond Camden posted on 9/8/2014 at 2:46 PM

Does IE9 even support multiple w/ the input field? If not, than there is nothing I can do about it. ;) It should still let you do uploads though.

Comment 24 by winter posted on 4/10/2015 at 10:14 AM

how to get the image url?

Comment 25 (In reply to #24) by Raymond Camden posted on 4/10/2015 at 1:05 PM

I believe I explain this in the post, right?

Comment 26 (In reply to #25) by winter posted on 4/16/2015 at 8:04 AM

i mean get the img folder path

Comment 27 (In reply to #26) by winter posted on 4/16/2015 at 8:05 AM

opsss.. sry i saw it~
try it now

Comment 28 (In reply to #25) by HR posted on 4/16/2015 at 8:24 AM

you've mention that only can use for IE, how about for google chrome or other browser?

Comment 29 (In reply to #24) by Raymond Camden posted on 4/16/2015 at 10:54 AM

You mean like the filesystem url? Pretty sure it is in the initial array of data. Try dumping it to console.

Comment 30 (In reply to #28) by Raymond Camden posted on 4/16/2015 at 10:55 AM

Eh? When did I say this was only for IE?

Comment 31 by Sayam Nandy posted on 5/5/2015 at 2:42 PM

where is the "myForm"page??

Comment 32 (In reply to #31) by Raymond Camden posted on 5/5/2015 at 2:47 PM

What do you mean? The code is above in the post, and there is a link to the demo. myForm is just the ID of the form.

Comment 33 (In reply to #32) by Sayam Nandy posted on 5/5/2015 at 3:04 PM

Sir,i'm newbie in advance java..i'm trying to develop a project in j2ee..therefore i need help..if i ask you few questions about java,can you just guide me sir?please sir help,please sir..

Comment 34 (In reply to #33) by Raymond Camden posted on 5/5/2015 at 5:54 PM

I do not use Java. I know it a tiny bit, but I cannot help you with that.

Comment 35 by Jyo posted on 8/10/2015 at 9:48 PM

Thanks a lot! It is very helpful.

Comment 36 (In reply to #12) by Shubham Gupta posted on 9/4/2015 at 8:21 AM

Hello Raymond Camden , I have two questions:

1-As per @Šime Vidas , "he said that he is not satisfied with DOM Serialization and he fixed a bug". Is DOM serialization not good and what bug he is talking about ?

2- What is this "classic callback/array thing" ?

Thank You [I am a complete amateur in JavaScript]

Comment 37 (In reply to #36) by Raymond Camden posted on 9/4/2015 at 10:48 AM

1) I believe he meant where I was adding to the DOM by appending a string in a loop. This can have performance issues versus modifying the DOM one time. In *this* case, it wouldn't ever be a problem unless the user selected a large number of files, but it is something to remember.

2) I tried to find a good post on the issue and I'm not having any luck. Ahah - try this one: http://stackoverflow.com/qu...

Comment 38 by Jens posted on 11/4/2015 at 7:58 AM

This is very useful. But you can't select files from different directories. If one click the browse button another time the previously selected file got removed.

Comment 39 (In reply to #38) by Raymond Camden posted on 11/4/2015 at 2:23 PM

See my later comments - this is expected behavior.

Comment 40 by Arnold Babasa posted on 2/10/2016 at 4:38 PM

Thank you for this code. Absolutely fantastic. Helped me out a great deal.

Comment 41 by user posted on 2/17/2016 at 12:39 AM

If your form has a reset button:

document.getElementById('myForm').addEventListener('reset', clearULForm, false);

function clearULForm(evt) {
selDiv.innerHTML = '';
}

Comment 42 by Lavika Kurda posted on 2/17/2016 at 6:33 PM

What if I want to remove one of the images selected. Can I change the files of file input field?

Comment 43 by khan hamid posted on 3/9/2016 at 1:47 PM

How to remove selected image?

Comment 44 (In reply to #43) by Raymond Camden posted on 3/12/2016 at 3:49 PM

Click to select again.

Comment 45 (In reply to #42) by Lucky posted on 3/26/2016 at 2:55 PM

That isn't possible for security reasons.

Comment 46 by Adem Ökmen posted on 5/23/2016 at 9:55 AM

Is it possible somehow to add files/images to different folders?

Imagine I have 2 folders, Folder A and folder B.
I'd like to upload 5 images to folder A, 2 images to folder B but I also want to upload images to a non-existing folder, Folder C.

How do I do that?

Comment 47 (In reply to #46) by Raymond Camden posted on 5/23/2016 at 11:44 AM

Absolutely - but that's a server-side issue. Your code on that side needs to handle putting files in the right place.

Comment 48 by Elyor M posted on 7/5/2016 at 4:04 PM

How to implement loading method while displaying all the images? you know, it takes some time if you choose many images...

Comment 49 (In reply to #48) by Raymond Camden posted on 7/5/2016 at 9:03 PM

The same as any other loading style logic:

before you begin the async process, add a message somewhere in the DOM, "Doing stuff..."

when done - remove the message.

Comment 50 by santhosh posted on 7/20/2016 at 5:08 AM

how to remove file from file list for example if i selected 5 file but i want remove two files from 5 files , how can i do that

Comment 51 (In reply to #50) by Raymond Camden posted on 7/20/2016 at 12:51 PM
Comment 52 by kusuma reddy posted on 9/26/2016 at 5:51 AM

how to choose file from file list for example if i selected 5 file but i want add two files again, now i want to see 7 files. and seven files should inserted to database. How can i do that

Comment 53 (In reply to #52) by Raymond Camden posted on 9/26/2016 at 1:19 PM

I have no idea what you are asking. Can you rephrase?

Comment 54 (In reply to #53) by kusuma reddy posted on 9/27/2016 at 9:03 AM

I am working on Multiple image uploading .for 1st time i choose 4 images and if i click on choose again and i select 3 images. then previous images are geeting deleted and newly uploaded(3) images are displaying . but i need all the 7 images to display.

Comment 55 (In reply to #54) by Raymond Camden posted on 9/27/2016 at 3:19 PM

See the two followups - the second is here

https://www.raymondcamden.c...

and I link to the followup on top.

Comment 56 by Md Jainuddin posted on 10/7/2016 at 5:46 AM

Not working in safari browser, it doesn't show image preview.

Comment 57 (In reply to #56) by Raymond Camden posted on 10/7/2016 at 11:32 AM

Safari is a bit behind in terms of web standards. What error do you see in the console?

Comment 58 (In reply to #52) by sneha posted on 11/11/2016 at 9:45 AM

hey.. i too have the same problem.. u got the solution?

Comment 59 by Daniel Bidulock posted on 11/19/2016 at 9:04 PM

Thank you for this great example.

In your completed script:

```
// ...
filesArr.forEach(function(f) {
var f = files[i];
// ...
```

The `i` variable is `undefined`. I simply removed the line and everything works fine.

Comment 60 (In reply to #59) by Raymond Camden posted on 11/20/2016 at 2:51 PM

Thanks - I must have forgotten to remove it.

Comment 61 by jyothi posted on 2/15/2017 at 8:57 AM

Hi Sir Good afternoon
Sir I am facing small issue i.e., i am uploading the 5 images , after that i removed 2 images instead of 5 images and but it's inserting to the database all 5 images .. i dont want that , when i remove the 2 images the remaining 3 images should be insert.. please give me any solution

Comment 62 (In reply to #61) by Raymond Camden posted on 2/15/2017 at 7:33 PM
Comment 63 (In reply to #62) by jyothi posted on 2/16/2017 at 9:18 AM

no sir you refered site it's showing onlt onthe page itself it's removing preview .. i explain clearley sir choose file 5files it's displayed , when i remove the 2 images , update the "choose file is 3 files".. and file having only 3 files

Comment 64 (In reply to #63) by Raymond Camden posted on 2/16/2017 at 12:30 PM

I have no idea what are you saying here. You posted on *this* blog entry, which to me implies you were using this code, which doesn't have the ability to remove files.

If you can share the URL of your code, I can take a look. Outside of that, I don't know what to tell you.

Comment 65 by Filip posted on 2/16/2017 at 7:46 PM

Hi Raymond, Thank you so much for this example. After little changes I made it saved my day :)

Comment 66 (In reply to #64) by jyothi posted on 2/17/2017 at 5:03 AM

ok thank you

Comment 67 by Mike A posted on 3/1/2017 at 7:16 PM

The link in the post (currently dated 5 months ago) below "See the two followups - the second is here" generates a 404 error, and I do not see any "and I link to the followup on top.". I want to be able to add one or more files to a list that was previously chosen (and not submitted). Can you provide an example of how to do that?

Comment 68 (In reply to #67) by Raymond Camden posted on 3/2/2017 at 2:40 AM

I fixed the link - but that's all I can do now.

Comment 69 (In reply to #39) by Mukul Kandpal posted on 5/3/2017 at 10:15 AM

my problem is when we upload 5 selected image after that we upload 1 image then it is add to the queue .but its save only one image which is last image which i had uploaded it. please solve my problem dear

Comment 70 (In reply to #69) by Raymond Camden posted on 5/3/2017 at 1:02 PM

This one does not support that. See my comment to Jens.

Comment 71 (In reply to #60) by Ryan posted on 5/24/2017 at 2:21 AM

Maybe you are deliberate for the lesson thnx :p

Comment 72 by Eduardo Heredia posted on 6/2/2017 at 4:11 PM

I have copied this message from Filip cause i have the same feeling:
Hi Raymond, Thank you so much for this example. After little changes I made it saved my day :)

Comment 73 by Harshal Panchal posted on 11/3/2017 at 11:42 AM

can we add delete btn to each uploaded file?

Comment 74 (In reply to #73) by Raymond Camden posted on 11/3/2017 at 8:39 PM
Comment 75 (In reply to #74) by Harshal Panchal posted on 11/4/2017 at 3:04 AM

Thanks i got the idea

Comment 76 by Rahul Sharma posted on 11/14/2017 at 1:48 PM

when i am uploading only one file then it is showing the file name 2 times.
https://prnt.sc/hac0j9

Comment 77 (In reply to #76) by Raymond Camden posted on 11/15/2017 at 9:00 PM

That's the browser rendering it to the right of the control - you can't control that as far as I know.

Comment 78 by Adriano Ghezzi posted on 12/13/2017 at 8:50 AM

very nice and well written but....
when post fired in $_FILES there is only one file.
Is there anything I didn't understand ?
thx

Comment 79 (In reply to #78) by Raymond Camden posted on 12/13/2017 at 1:25 PM

My JS code just adds a display layer - at the core it is one input/type=file tag. I'd say check the PHP code to see if it properly handles input/type=file/multiple. For example, ColdFusion didn't in version... um I don't know. But an earlier version had a bug with it.

Comment 80 by RD Desai posted on 12/16/2017 at 12:13 PM

Do you have delete image code from selected file?

Comment 81 (In reply to #80) by Raymond Camden posted on 12/16/2017 at 2:02 PM
Comment 82 by Trushal Thummar posted on 2/21/2018 at 12:21 PM

When I select multiple files at one time then it works fine but how can i do this thing when upload image one by one

Comment 83 (In reply to #82) by Raymond Camden posted on 2/21/2018 at 4:36 PM

I do not understand your question. Can you rephrase?

Comment 84 by Dhananjay Kumar posted on 4/13/2018 at 4:38 AM

when i upload multiple image at a time it working fine... but what if i select an image and upload image after that we select another image and upload for another position and so on ... for more than 30 image..

Comment 85 (In reply to #84) by Raymond Camden posted on 4/13/2018 at 7:39 PM

The default behavior is to wipe currently selected files when you select more images. See this example for one that handles this better: https://www.raymondcamden.c...

Comment 86 (In reply to #85) by Dhananjay Kumar posted on 4/14/2018 at 2:55 AM

i think you can't understand my problem? let me explain ... I am creating a boy bio-data. where all the photo of are kept in one container for boy, his father, his mother, his sister and many more photo. Every picture have separate button for choose photo.. But when we select 1st photo (i.e boy) it upload at boys position. After that we select his father photo. then problem arise .photo of father is saved at the position of boy.. this happen for all the reaming picture.. All picture are saved only at 1st position by replacing previous photo.... Now Tell me Jquery for that... Thanks

Comment 87 (In reply to #86) by Raymond Camden posted on 4/14/2018 at 2:16 PM

It sounds like you have an interesting app there, but this really isn't on topic anymore for *this* blog post. I'd suggest posting to Stack Overflow.

Comment 88 by Nayeem posted on 12/26/2018 at 7:45 AM

How we can get height and with on the images when we have option to upload more then one image

Comment 89 (In reply to #88) by Raymond Camden posted on 12/26/2018 at 9:17 PM

There's multiple answers for this: https://stackoverflow.com/q...

Comment 90 by Aiden posted on 4/22/2020 at 9:53 PM

Comment Deleted

Comment 91 by tarek posted on 6/22/2020 at 10:12 PM

merci beaucoup votre tuto ma beaucoup aide a réalisé un travail qui ma été confie ..think's

Comment 92 (In reply to #91) by Raymond Camden posted on 6/22/2020 at 10:13 PM

you are most welcome