用HTML5实现断点续传、上传进度条、图片预览 【2】

用HTML5实现断点续传、上传进度条、图片预览 【2】 · Oct 10, 2014 clicks

上一篇文章中,已经基本实现了文件上传的所有功能,还遗留了2个问题:一是计算文件hash时一次性读取了文件内容,存在内存限制; 二是只实现了单文件上传

事实上,Spark MD5给出了分片读取文件,计算md5值的过程。

照着那个例子,把代码重新修改了下

<!doctype html>
<html>
<head>
    <title>Upload File via XMLHttpRequest 2</title>
    <script src="spark_md5.min.js"></script>
<script>
var fr_supported = typeof FileReader != 'undefined';
var fd_supported = typeof FormData != 'undefined';
if ( !fr_supported ){
    alert('you browser doesnt support FileReader!');
}
if ( !fd_supported ){
    alert('you browser doesnt support FormData!');
}
 
function get(id){
    return document.getElementById(id);
}
var pos  = 0;
var slice_size = 1024 * 1024;
var upload_data=null;
var upload_finished = false;
var upload_file_size = 0;
var upload_file = null;
var upload_file_hash= '';
 
function fileSelected(){
    var file = get('fileToUpload').files[0];   
    upload_file = file;
    pos = 0;   
    upload_data = null ;
    upload_finished = false;
    upload_file_hash = '';
    if ( file ){
        upload_file_size = file.size;
        var fileSize = 0;
        if (file.size > 1024*1024){
            fileSize = (Math.round(file.size * 100 / (1024 * 1024)) / 100).toString() + 'MB';
        }else{
            fileSize = (Math.round(file.size * 100 / 1024) / 100).toString() + 'KB';
        }
        get('fileName').innerHTML = file.name;
        get('fileSize').innerHTML = fileSize;
        get('fileType').innerHTML = file.type;
        //preview image
        if ( file.type.split('/')[0] == 'image' ){
            var rd = new FileReader();
            rd.addEventListener('load',function(evt){
                var im = new Image();
                im.src = evt.target.result;
                get('preview').innerHTML = '';
                get('preview').appendChild(im);
            },false);
            rd.readAsDataURL(file);
        }
         
         
    }
}
function checkFileMd5(file,callback)
{
    var blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
    var chunk_size = 1024 * 1024 *10; //10M
    var current_chunk = 0;
    var chunk_count = Math.ceil(file.size / chunk_size);
    var spark = new SparkMD5.ArrayBuffer();
    var rd = new FileReader();
    rd.addEventListener('load',function(evt){
        spark.append(evt.target.result);
        current_chunk ++;
        if (current_chunk < chunk_count){
            loadNext();
        }else{
            callback(file, spark.end() );
        }
    });
    rd.addEventListener('error',function(evt){
        console.log(['err ocurs while computing hash']);
    })
    function loadNext(){
        var start = current_chunk * chunk_size,
            end = ((start+chunk_size)>=file.size) ? file.size : start+chunk_size;
        rd.readAsArrayBuffer(blobSlice.call(file,start,end))
    };
    loadNext();
}
function uploadFile()
{
    var file = upload_file;
    //get md5 hash
    checkFileMd5(file,afterCheckhash);
}
function afterCheckhash(file,hash_val)
{
    upload_file_hash = hash_val;
        //console.log(['md5', upload_file_hash ]);
        //get size of uploaded data
        var xhr = new XMLHttpRequest();
        var fd = new FormData();
        fd.append('act','query');
        fd.append('qry_fname', file.name );    
        fd.append('file_hash', upload_file_hash);
        xhr.addEventListener('load',function(stat){
            pos = parseInt(this.responseText);
            //console.log([upload_file,pos,upload_file_size]);
            if ( pos < upload_file_size){
                upload_data = upload_file.slice(pos, pos+slice_size);
                upload_finished = (pos + slice_size) >= upload_file_size;
                get('progressNumber').innerHTML =  Math.round(pos*100/upload_file_size)+'%';
                 
                //upload file
                do_upload_file();
                             
            }else{
                upload_finished = true;
                get('progressNumber').innerHTML = '100%';
                alert('file already upladed');
            }
             
        },false);
        xhr.open("POST",'upload.php');
        xhr.send(fd);
}
function do_upload_file(){ 
    if ( upload_data==null ){
        alert( 'nothing to upload' );
        return;
    }
    var xhr = new XMLHttpRequest();
    var fd = new FormData();
    fd.append('act','upload');
    fd.append('uploadfile', upload_data );
    fd.append('filename', upload_file.name);
    fd.append('finished', upload_finished?'1':'0' );
    fd.append('position', pos);
    fd.append('file_hash', upload_file_hash);
    xhr.upload.addEventListener("progress", uploadProgress,false);
    xhr.addEventListener("load", uploadComplete,false);
    xhr.addEventListener("error", uploadFailed,false);
    xhr.addEventListener("abort", uploadCanceled, false);
    xhr.open("POST", "upload.php");
    xhr.send(fd);
}
function uploadProgress(evt){
    var percent = 'unable to compute progress';
    if (evt.lengthComputable){
        var percent = Math.round( (evt.loaded+pos)*100/upload_file_size ) + '%';       
    }
    get('progressNumber').innerHTML = percent;
}
function uploadComplete(evt){
    //alert( evt.target.responseText );
    //console.log([evt.target.responseText]);
    if ( !upload_finished ){
        pos += slice_size;
        upload_data = upload_file.slice(pos, pos+slice_size);
        upload_finished = (pos + slice_size) >= upload_file_size;
        do_upload_file();
    }
}
function uploadFailed(evt){
    alert('Failed to upload');
}
function uploadCanceled(evt){
    alert('the upload has been canceled');
}
</script>
 
<body>
    <form action="upload.php" id="form1" method="post" enctype="multipart/form-data">
    <div class="row">
        <label for="fileToUpload">Select a File to Upload</label> <br />
        <input type="file" name="fileToUpload" id="fileToUpload" onchange="fileSelected()" />
    </div>
    <p>FileName: <span id="fileName"></span></p>
    <p>FileSize: <span id="fileSize"></span></p>
    <p>FileType: <span id="fileType"></span></p>
    <p id="preview"></p>
    <div class="row">
        <input type="button" onclick="uploadFile()" value="Upload" />
    </div>
    <p>Progress: <span id="progressNumber"></span></p>
    </form>