Pure HTML5 file upload

Pure HTML5 file upload

125 14708
Pure HTML5 file upload
Pure HTML5 file upload

Pure HTML5 file upload

Today we will be developing 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

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="http://www.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">&nbsp;</div>
                        <div class="clear_both"></div>
                        <div>
                            <div id="speed">&nbsp;</div>
                            <div id="remaining">&nbsp;</div>
                            <div id="b_transfered">&nbsp;</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!

SIMILAR ARTICLES


125 COMMENTS

  1. 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.

  2. 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 Will,
      Anything depend only on you, by default, you have to accept and pass (into necessary places) that uploaded file in upload.php

  3. 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.

  4. 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.

  5. 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

  6. 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 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.

  7. Has anybody figured out a work around for document.getElementById(‘image_file’).files[0], IE9 does not support this. Great uploader by the way.

  8. 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.

  9. 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

  10. 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.

  11. 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

  12. 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

  13. 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?

    • 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

    • 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.

  14. 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:
      http://www.script-tutorials.com/html5-image-uploader-with-jcrop/
      Just look at upload.php file of step4, it is easy to understand and implement.

  15. 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%

  16. 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’

  17. 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.

  18. 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.

    • 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']

  19. 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).

  20. 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.

  21. 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 !!!

1 2 3

Leave a Reply