Pure HTML5 file upload
HTML5 upload tutorial: today we will develop a great HTML5 file upload form with progress bar and preview (at client-side). We have already gave you jQuery based solution, but today’s application don’t require jQuery at all. All made in pure HTML5 Javascript. I’m going to use FileReader (html5) to implement live preview (without uploading to server), and, going to use XMLHttpRequest to send data to server.
Here are our demo and downloadable package:
Live Demo
[sociallocker]
download in package
[/sociallocker]
Ok, download the sources and lets begin !
Step 1. HTML
At this page you can see out form for upload images
index.html
<html lang="en" > <head> <meta charset="utf-8" /> <title>Pure HTML5 file upload | Script Tutorials</title> <link href="css/main.css" rel="stylesheet" type="text/css" /> <script src="js/script.js"></script> </head> <body> <header> <h2>Pure HTML5 file upload</h2> <a href="https://script-tutorials.com/pure-html5-file-upload/" class="stuts">Back to original tutorial on <span>Script Tutorials</span></a> </header> <div class="container"> <div class="contr"><h2>You can select the file (image) and click Upload button</h2></div> <div class="upload_form_cont"> <form id="upload_form" enctype="multipart/form-data" method="post" action="upload.php"> <div> <div><label for="image_file">Please select image file</label></div> <div><input type="file" name="image_file" id="image_file" onchange="fileSelected();" /></div> </div> <div> <input type="button" value="Upload" onclick="startUploading()" /> </div> <div id="fileinfo"> <div id="filename"></div> <div id="filesize"></div> <div id="filetype"></div> <div id="filedim"></div> </div> <div id="error">You should select valid image files only!</div> <div id="error2">An error occurred while uploading the file</div> <div id="abort">The upload has been canceled by the user or the browser dropped the connection</div> <div id="warnsize">Your file is very big. We can't accept it. Please select more small file</div> <div id="progress_info"> <div id="progress"></div> <div id="progress_percent"> </div> <div class="clear_both"></div> <div> <div id="speed"> </div> <div id="remaining"> </div> <div id="b_transfered"> </div> <div class="clear_both"></div> </div> <div id="upload_response"></div> </div> </form> <img id="preview" /> </div> </div> </body> </html>
Step 2. CSS
css/main.css
I have selected all necessary styles for our html5 upload form
css/main.css
.upload_form_cont { background: -moz-linear-gradient(#ffffff, #f2f2f2); background: -ms-linear-gradient(#ffffff, #f2f2f2); background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ffffff), color-stop(100%, #f2f2f2)); background: -webkit-linear-gradient(#ffffff, #f2f2f2); background: -o-linear-gradient(#ffffff, #f2f2f2); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f2f2f2'); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f2f2f2')"; background: linear-gradient(#ffffff, #f2f2f2); color:#000; overflow:hidden; } #upload_form { float:left; padding:20px; width:700px; } #preview { background-color:#fff; display:block; float:right; width:200px; } #upload_form > div { margin-bottom:10px; } #speed,#remaining { float:left; width:100px; } #b_transfered { float:right; text-align:right; } .clear_both { clear:both; } input { border-radius:10px; -moz-border-radius:10px; -ms-border-radius:10px; -o-border-radius:10px; -webkit-border-radius:10px; border:1px solid #ccc; font-size:14pt; padding:5px 10px; } input[type=button] { background: -moz-linear-gradient(#ffffff, #dfdfdf); background: -ms-linear-gradient(#ffffff, #dfdfdf); background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ffffff), color-stop(100%, #dfdfdf)); background: -webkit-linear-gradient(#ffffff, #dfdfdf); background: -o-linear-gradient(#ffffff, #dfdfdf); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#dfdfdf'); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#dfdfdf')"; background: linear-gradient(#ffffff, #dfdfdf); } #image_file { width:400px; } #progress_info { font-size:10pt; } #fileinfo,#error,#error2,#abort,#warnsize { color:#aaa; display:none; font-size:10pt; font-style:italic; margin-top:10px; } #progress { border:1px solid #ccc; display:none; float:left; height:14px; border-radius:10px; -moz-border-radius:10px; -ms-border-radius:10px; -o-border-radius:10px; -webkit-border-radius:10px; background: -moz-linear-gradient(#66cc00, #4b9500); background: -ms-linear-gradient(#66cc00, #4b9500); background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #66cc00), color-stop(100%, #4b9500)); background: -webkit-linear-gradient(#66cc00, #4b9500); background: -o-linear-gradient(#66cc00, #4b9500); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#66cc00', endColorstr='#4b9500'); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#66cc00', endColorstr='#4b9500')"; background: linear-gradient(#66cc00, #4b9500); } #progress_percent { float:right; } #upload_response { margin-top: 10px; padding: 20px; overflow: hidden; display: none; border: 1px solid #ccc; border-radius:10px; -moz-border-radius:10px; -ms-border-radius:10px; -o-border-radius:10px; -webkit-border-radius:10px; box-shadow: 0 0 5px #ccc; background: -moz-linear-gradient(#bbb, #eee); background: -ms-linear-gradient(#bbb, #eee); background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #bbb), color-stop(100%, #eee)); background: -webkit-linear-gradient(#bbb, #eee); background: -o-linear-gradient(#bbb, #eee); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#bbb', endColorstr='#eee'); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#bbb', endColorstr='#eee')"; background: linear-gradient(#bbb, #eee); }
Step 3. HTML5 JS
js/script.js
// common variables var iBytesUploaded = 0; var iBytesTotal = 0; var iPreviousBytesLoaded = 0; var iMaxFilesize = 1048576; // 1MB var oTimer = 0; var sResultFileSize = ''; function secondsToTime(secs) { // we will use this function to convert seconds in normal time format var hr = Math.floor(secs / 3600); var min = Math.floor((secs - (hr * 3600))/60); var sec = Math.floor(secs - (hr * 3600) - (min * 60)); if (hr < 10) {hr = "0" + hr; } if (min < 10) {min = "0" + min;} if (sec < 10) {sec = "0" + sec;} if (hr) {hr = "00";} return hr + ':' + min + ':' + sec; }; function bytesToSize(bytes) { var sizes = ['Bytes', 'KB', 'MB']; if (bytes == 0) return 'n/a'; var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); return (bytes / Math.pow(1024, i)).toFixed(1) + ' ' + sizes[i]; }; function fileSelected() { // hide different warnings document.getElementById('upload_response').style.display = 'none'; document.getElementById('error').style.display = 'none'; document.getElementById('error2').style.display = 'none'; document.getElementById('abort').style.display = 'none'; document.getElementById('warnsize').style.display = 'none'; // get selected file element var oFile = document.getElementById('image_file').files[0]; // filter for image files var rFilter = /^(image\/bmp|image\/gif|image\/jpeg|image\/png|image\/tiff)$/i; if (! rFilter.test(oFile.type)) { document.getElementById('error').style.display = 'block'; return; } // little test for filesize if (oFile.size > iMaxFilesize) { document.getElementById('warnsize').style.display = 'block'; return; } // get preview element var oImage = document.getElementById('preview'); // prepare HTML5 FileReader var oReader = new FileReader(); oReader.onload = function(e){ // e.target.result contains the DataURL which we will use as a source of the image oImage.src = e.target.result; oImage.onload = function () { // binding onload event // we are going to display some custom image information here sResultFileSize = bytesToSize(oFile.size); document.getElementById('fileinfo').style.display = 'block'; document.getElementById('filename').innerHTML = 'Name: ' + oFile.name; document.getElementById('filesize').innerHTML = 'Size: ' + sResultFileSize; document.getElementById('filetype').innerHTML = 'Type: ' + oFile.type; document.getElementById('filedim').innerHTML = 'Dimension: ' + oImage.naturalWidth + ' x ' + oImage.naturalHeight; }; }; // read selected file as DataURL oReader.readAsDataURL(oFile); } function startUploading() { // cleanup all temp states iPreviousBytesLoaded = 0; document.getElementById('upload_response').style.display = 'none'; document.getElementById('error').style.display = 'none'; document.getElementById('error2').style.display = 'none'; document.getElementById('abort').style.display = 'none'; document.getElementById('warnsize').style.display = 'none'; document.getElementById('progress_percent').innerHTML = ''; var oProgress = document.getElementById('progress'); oProgress.style.display = 'block'; oProgress.style.width = '0px'; // get form data for POSTing //var vFD = document.getElementById('upload_form').getFormData(); // for FF3 var vFD = new FormData(document.getElementById('upload_form')); // create XMLHttpRequest object, adding few event listeners, and POSTing our data var oXHR = new XMLHttpRequest(); oXHR.upload.addEventListener('progress', uploadProgress, false); oXHR.addEventListener('load', uploadFinish, false); oXHR.addEventListener('error', uploadError, false); oXHR.addEventListener('abort', uploadAbort, false); oXHR.open('POST', 'upload.php'); oXHR.send(vFD); // set inner timer oTimer = setInterval(doInnerUpdates, 300); } function doInnerUpdates() { // we will use this function to display upload speed var iCB = iBytesUploaded; var iDiff = iCB - iPreviousBytesLoaded; // if nothing new loaded - exit if (iDiff == 0) return; iPreviousBytesLoaded = iCB; iDiff = iDiff * 2; var iBytesRem = iBytesTotal - iPreviousBytesLoaded; var secondsRemaining = iBytesRem / iDiff; // update speed info var iSpeed = iDiff.toString() + 'B/s'; if (iDiff > 1024 * 1024) { iSpeed = (Math.round(iDiff * 100/(1024*1024))/100).toString() + 'MB/s'; } else if (iDiff > 1024) { iSpeed = (Math.round(iDiff * 100/1024)/100).toString() + 'KB/s'; } document.getElementById('speed').innerHTML = iSpeed; document.getElementById('remaining').innerHTML = '| ' + secondsToTime(secondsRemaining); } function uploadProgress(e) { // upload process in progress if (e.lengthComputable) { iBytesUploaded = e.loaded; iBytesTotal = e.total; var iPercentComplete = Math.round(e.loaded * 100 / e.total); var iBytesTransfered = bytesToSize(iBytesUploaded); document.getElementById('progress_percent').innerHTML = iPercentComplete.toString() + '%'; document.getElementById('progress').style.width = (iPercentComplete * 4).toString() + 'px'; document.getElementById('b_transfered').innerHTML = iBytesTransfered; if (iPercentComplete == 100) { var oUploadResponse = document.getElementById('upload_response'); oUploadResponse.innerHTML = '<h1>Please wait...processing</h1>'; oUploadResponse.style.display = 'block'; } } else { document.getElementById('progress').innerHTML = 'unable to compute'; } } function uploadFinish(e) { // upload successfully finished var oUploadResponse = document.getElementById('upload_response'); oUploadResponse.innerHTML = e.target.responseText; oUploadResponse.style.display = 'block'; document.getElementById('progress_percent').innerHTML = '100%'; document.getElementById('progress').style.width = '400px'; document.getElementById('filesize').innerHTML = sResultFileSize; document.getElementById('remaining').innerHTML = '| 00:00:00'; clearInterval(oTimer); } function uploadError(e) { // upload error document.getElementById('error2').style.display = 'block'; clearInterval(oTimer); } function uploadAbort(e) { // upload abort document.getElementById('abort').style.display = 'block'; clearInterval(oTimer); }
Most of code is already commented. So I will hope that you will understand all this code. Anyway – how it working: when we select file – function ‘fileSelected’ is executing. We filter all unnecessary formats (allow to upload next formats: bmp, gif, jpg, png, tif), in case of huge file – we will draw warning message. Then, through FileReader::readAsDataURL we will draw live preview of selected file. Plus, we will display another information about image: its name, size, type, and dimensions. Process of uploading is a little complicated. But generally, we have to prepare XMLHttpRequest object, add event listeners to next events: progress, load, error and abort. And after – post form data (I have used FormData class) to our ‘upload.php’ receiver.
Step 4. PHP
upload.php
<?php function bytesToSize1024($bytes, $precision = 2) { $unit = array('B','KB','MB'); return @round($bytes / pow(1024, ($i = floor(log($bytes, 1024)))), $precision).' '.$unit[$i]; } $sFileName = $_FILES['image_file']['name']; $sFileType = $_FILES['image_file']['type']; $sFileSize = bytesToSize1024($_FILES['image_file']['size'], 1); echo <<<EOF <p>Your file: {$sFileName} has been successfully received.</p> <p>Type: {$sFileType}</p> <p>Size: {$sFileSize}</p> EOF;
As you can see – I’m not uploading file. But, ‘echo’ back all info about accepted file. This information will appear in our <div id="upload_response"></div> element.
Live Demo
Conclusion
Welcome back to read new awesome and unique articles about HTML5. Good luck!
when i run the demo in ie9. im getting the following error .
SCRIPT5009: ‘FormData’ is undefined
script.js, line 94 character 5
var vFD = new FormData(document.getElementById(‘upload_form’));
the upload gets stuck.
Hi pl4g4,
Yes, FormData haven’t supported in IE browser yet. Here are small tester: http://ahedg.es/webkit/formdata.html
Plus, you can read more about sending POSt date with XMLHttpRequest here: http://forums.asp.net/t/1424687.aspx
I think this is a great display and the demo works fine but when I try to run the downloaded code, I get an ‘upload error’ message.
Hi Philip,
Why? link https://script-tutorials.com/demos/199/source.zip always available, re-check it.
Where are the files being uploaded to? I can’t find them anywhere after successful upload!
Hi Will,
Anything depend only on you, by default, you have to accept and pass (into necessary places) that uploaded file in upload.php
If I try to upload a filetype that is not specified in the filter, the warning message pops up but clicking upload still allows a forbidden filetype to be uploaded successfully.
Great code but how do I get it to do the actual upload and dictate the path to where the file is uploaded. As per the tutorial you say it is only echoing the actual transfer but not transferring the file. How do I get it yo upload to a given director? Thanks.
Hello German,
This is pretty easy – focus your attention to PHP receiver: our upload.php.
In current state – it display just some information about our uploaded file.
Now, you can follow: http://de.php.net/manual/en/features.file-upload.post-method.php
Here you can see example of accepting of files
Hey.. Where does this script upload to, and how do I configure it?
Hello Tux,
Actual upload code is in upload.php only. This is server side (to accept file)
Hi, thanks for the tutorial and file download. Can this be made to fall back somehow for IE to avoid the error message? Maybe conditional statement or javascript browser check? If so, what would you recommend?
Thanks
Hi Ian,
Yes, it is possible that we will have error at line 38 for IE browser.
mmm, you can try this instruction:
http://msdn.microsoft.com/en-us/library/ms537509%28v=vs.85%29.aspx
to add that js browser check.
How to change rFilter to work with text files (txt, csv, doc)?
var rFilter = /^(image\/bmp|image\/gif|image\/jpeg|image\/png|image\/tiff)$/i;
Thanks in advance !!!
Hello Dido,
About filter. You can add ‘text/plain’ for TXT files and ‘text/csv’ for CSV files.
How do I configure the upload path ?
Can’t find it in the scipt…
Thank you for your help !
Hello Alexandre,
You need pay attention to our upload.php file. $_FILES[‘image_file’] is already available for you, so you can accept it to any your custom folder.
Has anybody figured out a work around for document.getElementById(‘image_file’).files[0], IE9 does not support this. Great uploader by the way.
Hello Mike,
Yes, IE8 doesn’t support similar way,
but, as alternative solution, you can check for file extension (rather than it actual type).
You can use document.getElementById(‘image_file’).value as absolute file path, and then – validate it extension by
http://stackoverflow.com/questions/374930/validating-file-types-by-regular-expression
as example
Since IE doesn’t support FormData or the addEventListener on the XMLHttpRequest object, clicking the upload button doesn’t do anything at all and doesn’t show an error message of any kind. What is your solution for people who use IE?
Is there a way to check for IE and then write some event handlers to grab the form data? Would onreadystatechange work in IE instead (http://www.j2mesalsa.com/ajax/properties.php) or some other attach event code like http://rubayathasan.com/tutorial/ie8-addeventlistener-alternative-example/ ?
Hello Will,
Yes, this is bad that our IE8 was made long ago (in the time when we know quite nothing about HTML5).
This is because that browser still doesn’t support all new features of HTML5 (even FormData).
So, for this browser we can create own workarounds.
This is not a problem with handlers or XMLHttpRequest (as I read – it is available since IE7), but, the problem in using FormData.
So, in the result, for IE, we can search for some ready solutions (but, without using of FormData), as example, you can search for another jQuery based uploaders.
hi,
first : thx !!
and how can I do to make multiple file, please?
cause :
var oFile = document.getElementById(‘fichier_upload’).files[0];
var oFile = document.getElementById(‘fichier_upload’).files[1];
var oFile = document.getElementById(‘fichier_upload’).files[2];
doesn’t work :s
Nice tutorial…
how do can I MySQL database to upload image for each user, when using username and password
Hi Ernie,
As the first, you have to prepare some logging functionality for your script, and then – link upload functionality to your project.
Hi, i am trying to upload large files over 30MB and it’s seems that the file stop uploading after 12%…
is there anything i can change to make it work?
Hello Kobi,
You should check your upload-related params of your hosting. This is such variables like: upload_max_filesize, post_max_size and set_time_limit
How to change rFilter to work with text files (xls, csv, xlxs)?
var rFilter = /^(image\/bmp|image\/gif|image\/jpeg|image\/png|image\/tiff)$/i;
Thanks
Hello Shobha,
in case of XLS and CSV files, you can use next mime types for the filter: application/vnd.ms-excel and text/csv
Anyway to get this to upload as soon as you drop the file ?
Without the need to press the upload button ?
Hello Tom,
In this case you just should evoke ‘startUploading’ function in the end of ‘fileSelected’
How to add Captcha and other required fields like Name, Email, Message and Payment Option and then send all the details with attachment to the specified email address?
Hi Junaid,
In your case you should prepare HTML form (with all your fields), add PHP processing (to send emails). But, is your question is related to our tutorial?
Yeah because just an upload option is not enough for a contact form.
Hi Junaid,
Current article is not about making contact form. This is about HTML5 files upload. This is why I didn’t need to create any contact form for this article. But if you’d like to ask us about new tutorial – how to create ContactForm in html5 – you can ask us about it
How to preview ms office files the same way as image files are previewed in this article?
Hello catimos,
In case if you want to preview MS Office files in html, you have to convert your Office files into HTML of Image format to preview it.
I am not complaining – this code looks amazing, but am I wrong for wondering WHY the script is incomplete? Only the most important part is left out, is this some sort of joke? After hours of studying the code, troubleshooting, and referencing php.net’s bare bone guides, I’m left with nothing but missing puzzle pieces. I may be one character or two off, but if the author made this beautiful uploader, I’m sure he could give us the remaining 5% that would finish the job – not just produce the pretty [and pretend] file uploaded messages.
Hi ScottAgen,
But what were you looking for? What was these 5% for you? Few more lines to save custom files to some folder at our hosting? Basically – this is very easy, you should combine just two functions: is_uploaded_file and move_uploaded_file, that’s all. You can find whole upload code here:
https://script-tutorials.com/html5-image-uploader-with-jcrop/
Just look at upload.php file of step4, it is easy to understand and implement.
LOL – It happens every time! I was searching and trying to get the script, files and directory to work, and about 10 minutes after writing my post (asking for the missing code), I figured it out. But my question mark still stands – only several hours might have been saved had the script been complete. Thanks for providing the 95%
Hi there thank you very much for the very nice tutorial.
Just I cannot solve:
If I try to upload a filetype that is not specified in the filter, NO warning message pops up and clicking upload allows a forbidden filetype to be uploaded successfully.
In the same way, if a file is greater than “iMaxFilesize” I have no error at all and it allows uploads anyway.
Any suggestions
Thank you very much,
Giggio :)
Hello Giggio,
Please pay attention, that we check for file type and size only in function fileSelected, this function evokes only in case of onchange for FILE element, but, you are talking about ‘upload’ button (I suppose), When we click this button we evoke another event: startUploading, where we don’t check for file format and size, if you want- you always can repeat related functionality from ‘fileSelected’ function into ‘startUploading’
Hi,
Looks great.
However I am not a developer but more like front-end designer. Where does the uploaded file go? How to set the upload path?
And any support of Multiple file upload?
Thanks
Hello UAK,
As I answered before – this script doesn’t upload result file into server, if you need it – you always can enhance a bit your server side with is_uploaded_file and move_uploaded_file functions.
I want to add cancel button while uploading file .
I have added this but having a problem file upload canceled only after 100% uploading.
Can you tell me how to add cancel button.
Regards,
Hello Monika,
As I remember, I already added ‘abort’ event handler in our script – just use it.
In case if your file has already uploaded (100%), I suggest that you remove this file when you click ‘cancel’ button in this case.
where will the uploaded file will go
where we can get the file
can i know the dictonary name
Hi avinash,
You have to pay your attention to our ‘upload.php’ file where we accept incoming files. As you can see, this is: $_FILES[‘image_file’]
Helps a lot. Life saving tutorial. :-)
Simple & Clean , straightforward.
Thank you so much..
In the HTML5 script sample you did not include the upload file.
I have so far no success in getting the XMLHttpRequest object to function.
What would be the file format for the uploaded file?
Is below script adequate to get files?
Anyone there to help me with this issue?
tnx
Hi pieter,
Why I didn’t include upload file? It is here : upload.php. I’ve just checked – it is available in the download package too.
As you see – we already us $_FILES[‘image_file’] in this file. This is your uploaded file, you can handle it as you need (save it somewhere as example).
Please replay in js/script.js l 102
oXHR.open(‘POST’, ‘upload.php’); by
oXHR.open(‘POST’, document.getElementById(“upload_form”).getAttribute(‘action’));
Hello caledo,
Yes, it is acceptable too. In this case we don’t need to point which exactly PHP file is in use. Thank you for your suggestion.
How to save (pure-html5-file-upload) file in a folder.
Hello jagabandhu,
You can do it in upload.php
You can use standard functions: is_uploaded_file, move_uploaded_file)
How to change rFilter to work with video and audio files (mp3, mp4, avi)?
var rFilter = /^(image\/bmp|image\/gif|image\/jpeg|image\/png|image\/tiff)$/i;
Thanks in advance !!!
Hello Sammy,
All mime types you can find here: http://www.webmaster-toolkit.com/mime-types.shtml