您的位置:首页 > 编程语言 > C语言/C++

基于H5浏览器通过JS前端和C++后端实现的上传服务器器,支持断点续传和4GB以上大文件上传

2019-06-22 17:54 549 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/mediadev/article/details/93344806

随着视频网站和大数据应用的普及,特别是高清视频和4K视频应用的到来,超大文件上传已经成为了日常的基础应用需求。

但是在很多情况下,平台运营方并没有大文件上传和断点续传的开发经验,往往在网上找一些简单的PHP或者Java程序来实现基本的上传功能,然而在实际使用中会发现,这些基于脚本语言实现的上传功能模块性能很弱,一是不支持2GB以上的内容上传;二是无法支持断点续传;三是效率极低,单台服务器最多支持几十个并发上传连接。

我们经过2年多时间开发的这款大文件上传服务器,综合考虑了各种核心需求,采用C++语言编码实现,真正做到了基于H5技术实现断点续传,支持4GB以上大文件上传,支持常见的各种浏览器,支持1000以上并发上传,性能强劲,运行稳定。

项目地址:

https://github.com/liufeihong/Hyper-Upload-Server

Hyper Upload Server 超级上传服务器简介

这是一款超级文件上传服务器,采用异步I/O架构,采用C++语言编码实现。它支持4GB以上超大文件上传和断点续传,支持Windows和Linux服务器平台,支持任意格式的文件上传,尤其适合大的视频网站应用。单台服务器支持1000并发上传进程,支持PC端和智能手机端主流的浏览器。

主要特性

1. 服务器端采用异步I/O架设设计,具有高性能I/O处理能力,尤其适用于超大文件上传;

2. 服务器端采用高效内存分配技术确保在运行过程中服务器的内存开销最小化;

3. 完全采用标准协议实现,因此兼容几乎所有的PC端和移动端浏览器;

4. 服务器端采用C++语言自主实现,对上传文件的尺寸无限制,天生支持超大文件上传。

   而基于PHP、JAVA等技术实现的文件上传服务天生无法支持超大文件上传,无法逾越2GB的最大文件尺寸瓶颈;

5. 服务器端采用无缓冲即时写入方式,上传数据写入一步到位。不同于PHP、JAVA等技术实现方式需要两步写入;

6. 服务器端可跨平台编译运行,支持Windows和Linux平台;

7. 高性能,单台服务器支持1000个并发上传进程;

8. 支持4GB以上超大文件上传,文件大小不受限制;

9. 客户端支持采用HTTP标准协议上传;

10.支持断点续传,断网、关机重启均不受影响;

11.支持HTML5浏览器上传进度实时显示;

12.支持IE8及以上浏览器上传进度显示;

13.支持查看客户端在线连接, 查看方法: http://ip:port/lists

14.多浏览器兼容,包括Chrome,Firefox,Safari,IE,Opera,Edge;

前端JS代码

;
/*--jquery.md5.js begin--*/
(function($){

var rotateLeft = function(lValue, iShiftBits) {
return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
}

var addUnsigned = function(lX, lY) {
    var lX4, lY4, lX8, lY8, lResult;
    lX8 = (lX & 0x80000000);
    lY8 = (lY & 0x80000000);
    lX4 = (lX & 0x40000000);
    lY4 = (lY & 0x40000000);
    lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);
    if (lX4 & lY4) return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
    if (lX4 | lY4) {
    if (lResult & 0x40000000) return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
    else return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
    } else {
    return (lResult ^ lX8 ^ lY8);
    }
}

var F = function(x, y, z) {
    return (x & y) | ((~ x) & z);
}

var G = function(x, y, z) {
    return (x & z) | (y & (~ z));
}

var H = function(x, y, z) {
    return (x ^ y ^ z);
}

var I = function(x, y, z) {
    return (y ^ (x | (~ z)));
}

var FF = function(a, b, c, d, x, s, ac) {
    a = addUnsigned(a, addUnsigned(addUnsigned(F(b, c, d), x), ac));
    return addUnsigned(rotateLeft(a, s), b);
};

var GG = function(a, b, c, d, x, s, ac) {
    a = addUnsigned(a, addUnsigned(addUnsigned(G(b, c, d), x), ac));
    return addUnsigned(rotateLeft(a, s), b);
};

var HH = function(a, b, c, d, x, s, ac) {
    a = addUnsigned(a, addUnsigned(addUnsigned(H(b, c, d), x), ac));
    return addUnsigned(rotateLeft(a, s), b);
};

var II = function(a, b, c, d, x, s, ac) {
    a = addUnsigned(a, addUnsigned(addUnsigned(I(b, c, d), x), ac));
    return addUnsigned(rotateLeft(a, s), b);
};

var convertToWordArray = function(string) {
    var lWordCount;
    var lMessageLength = string.length;
    var lNumberOfWordsTempOne = lMessageLength + 8;
    var lNumberOfWordsTempTwo = (lNumberOfWordsTempOne - (lNumberOfWordsTempOne % 64)) / 64;
    var lNumberOfWords = (lNumberOfWordsTempTwo + 1) * 16;
    var lWordArray = Array(lNumberOfWords - 1);
    var lBytePosition = 0;
    var lByteCount = 0;
    while (lByteCount < lMessageLength) {
        lWordCount = (lByteCount - (lByteCount % 4)) / 4;
        lBytePosition = (lByteCount % 4) * 8;
        lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition));
        lByteCount++;
    }
    lWordCount = (lByteCount - (lByteCount % 4)) / 4;
    lBytePosition = (lByteCount % 4) * 8;
    lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
    lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
    lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
    return lWordArray;
};

var wordToHex = function(lValue) {
    var WordToHexValue = "", WordToHexValueTemp = "", lByte, lCount;
    for (lCount = 0; lCount <= 3; lCount++) {
    lByte = (lValue >>> (lCount * 8)) & 255;
    WordToHexValueTemp = "0" + lByte.toString(16);
    WordToHexValue = WordToHexValue + WordToHexValueTemp.substr(WordToHexValueTemp.length - 2, 2);
    }
    return WordToHexValue;
};

var uTF8Encode = function(string) {
    string = string.replace(/\x0d\x0a/g, "\x0a");
    var output = "";
    for (var n = 0; n < string.length; n++) {
    var c = string.charCodeAt(n);
    if (c < 128) {
    output += String.fromCharCode(c);
    } else if ((c > 127) && (c < 2048)) {
    output += String.fromCharCode((c >> 6) | 192);
    output += String.fromCharCode((c & 63) | 128);
    } else {
    output += String.fromCharCode((c >> 12) | 224);
    output += String.fromCharCode(((c >> 6) & 63) | 128);
    output += String.fromCharCode((c & 63) | 128);
    }
    }
    return output;
};

$.extend({
md5: function(string) {
var x = Array();
var k, AA, BB, CC, DD, a, b, c, d;
var S11=7, S12=12, S13=17, S14=22;
var S21=5, S22=9 , S23=14, S24=20;
var S31=4, S32=11, S33=16, S34=23;
var S41=6, S42=10, S43=15, S44=21;
string = uTF8Encode(string);
x = convertToWordArray(string);
a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476;
for (k = 0; k < x.length; k += 16) {
AA = a; BB = b; CC = c; DD = d;
a = FF(a, b, c, d, x[k+0], S11, 0xD76AA478);
d = FF(d, a, b, c, x[k+1], S12, 0xE8C7B756);
c = FF(c, d, a, b, x[k+2], S13, 0x242070DB);
b = FF(b, c, d, a, x[k+3], S14, 0xC1BDCEEE);
a = FF(a, b, c, d, x[k+4], S11, 0xF57C0FAF);
d = FF(d, a, b, c, x[k+5], S12, 0x4787C62A);
c = FF(c, d, a, b, x[k+6], S13, 0xA8304613);
b = FF(b, c, d, a, x[k+7], S14, 0xFD469501);
a = FF(a, b, c, d, x[k+8], S11, 0x698098D8);
d = FF(d, a, b, c, x[k+9], S12, 0x8B44F7AF);
c = FF(c, d, a, b, x[k+10], S13, 0xFFFF5BB1);
b = FF(b, c, d, a, x[k+11], S14, 0x895CD7BE);
a = FF(a, b, c, d, x[k+12], S11, 0x6B901122);
d = FF(d, a, b, c, x[k+13], S12, 0xFD987193);
c = FF(c, d, a, b, x[k+14], S13, 0xA679438E);
b = FF(b, c, d, a, x[k+15], S14, 0x49B40821);
a = GG(a, b, c, d, x[k+1], S21, 0xF61E2562);
d = GG(d, a, b, c, x[k+6], S22, 0xC040B340);
c = GG(c, d, a, b, x[k+11], S23, 0x265E5A51);
b = GG(b, c, d, a, x[k+0], S24, 0xE9B6C7AA);
a = GG(a, b, c, d, x[k+5], S21, 0xD62F105D);
d = GG(d, a, b, c, x[k+10], S22, 0x2441453);
c = GG(c, d, a, b, x[k+15], S23, 0xD8A1E681);
b = GG(b, c, d, a, x[k+4], S24, 0xE7D3FBC8);
a = GG(a, b, c, d, x[k+9], S21, 0x21E1CDE6);
d = GG(d, a, b, c, x[k+14], S22, 0xC33707D6);
c = GG(c, d, a, b, x[k+3], S23, 0xF4D50D87);
b = GG(b, c, d, a, x[k+8], S24, 0x455A14ED);
a = GG(a, b, c, d, x[k+13], S21, 0xA9E3E905);
d = GG(d, a, b, c, x[k+2], S22, 0xFCEFA3F8);
c = GG(c, d, a, b, x[k+7], S23, 0x676F02D9);
b = GG(b, c, d, a, x[k+12], S24, 0x8D2A4C8A);
a = HH(a, b, c, d, x[k+5], S31, 0xFFFA3942);
d = HH(d, a, b, c, x[k+8], S32, 0x8771F681);
c = HH(c, d, a, b, x[k+11], S33, 0x6D9D6122);
b = HH(b, c, d, a, x[k+14], S34, 0xFDE5380C);
a = HH(a, b, c, d, x[k+1], S31, 0xA4BEEA44);
d = HH(d, a, b, c, x[k+4], S32, 0x4BDECFA9);
c = HH(c, d, a, b, x[k+7], S33, 0xF6BB4B60);
b = HH(b, c, d, a, x[k+10], S34, 0xBEBFBC70);
a = HH(a, b, c, d, x[k+13], S31, 0x289B7EC6);
d = HH(d, a, b, c, x[k+0], S32, 0xEAA127FA);
c = HH(c, d, a, b, x[k+3], S33, 0xD4EF3085);
b = HH(b, c, d, a, x[k+6], S34, 0x4881D05);
a = HH(a, b, c, d, x[k+9], S31, 0xD9D4D039);
d = HH(d, a, b, c, x[k+12], S32, 0xE6DB99E5);
c = HH(c, d, a, b, x[k+15], S33, 0x1FA27CF8);
b = HH(b, c, d, a, x[k+2], S34, 0xC4AC5665);
a = II(a, b, c, d, x[k+0], S41, 0xF4292244);
d = II(d, a, b, c, x[k+7], S42, 0x432AFF97);
c = II(c, d, a, b, x[k+14], S43, 0xAB9423A7);
b = II(b, c, d, a, x[k+5], S44, 0xFC93A039);
a = II(a, b, c, d, x[k+12], S41, 0x655B59C3);
d = II(d, a, b, c, x[k+3], S42, 0x8F0CCC92);
c = II(c, d, a, b, x[k+10], S43, 0xFFEFF47D);
b = II(b, c, d, a, x[k+1], S44, 0x85845DD1);
a = II(a, b, c, d, x[k+8], S41, 0x6FA87E4F);
d = II(d, a, b, c, x[k+15], S42, 0xFE2CE6E0);
c = II(c, d, a, b, x[k+6], S43, 0xA3014314);
b = II(b, c, d, a, x[k+13], S44, 0x4E0811A1);
a = II(a, b, c, d, x[k+4], S41, 0xF7537E82);
d = II(d, a, b, c, x[k+11], S42, 0xBD3AF235);
c = II(c, d, a, b, x[k+2], S43, 0x2AD7D2BB);
b = II(b, c, d, a, x[k+9], S44, 0xEB86D391);
a = addUnsigned(a, AA);
b = addUnsigned(b, BB);
c = addUnsigned(c, CC);
d = addUnsigned(d, DD);
}
var tempValue = wordToHex(a) + wordToHex(b) + wordToHex(c) + wordToHex(d);
return tempValue.toLowerCase();
}
});
})(jQuery); 
/*--jquery.md5.js end--*/

/*--- HYUpload.js-begin--*/

function toast(msg,duration)
{
    var toastDiv = document.getElementById('hyupload-toast');
    if (msg) {
        toastDiv.style.display = 'block';
        toastDiv.innerHTML = msg;
        duration = parseInt(duration);
        if (!duration) duration = 3000;
        if (!window.uploadtoast) {
            window.clearTimeout(window.uploadtoast);
        }
        window.uploadtoast = window.setTimeout(function() {
            toastDiv.style.display = 'none';
            delete window.uploadtoast;
        },duration);
    }
    else {
        toastDiv.style.display = 'none';
        toastDiv.innerHTML = '';
    }
}

Array.prototype.in_array=function(e){
    var S=String.fromCharCode(2);
    var r=new RegExp(S+e+S);
    return (r.test(S+this.join(S)+S));
};

var HYFileUploader = function (options) {
    var _options = {
        filelist:'hyupload-filelist',
        notice_url:'',
        savepaths:{'video':'.mp4,.mkv,.avi,.wmv','document':'.zip,.rar','image':'.jpg,.jpeg,.png,.gif','audio':'.aac,.mp3'},
        ontaskfinish:function(taskobj) {
        
        },
        ontaskprogress:function(taskobj) {
        
        },
        ontaskprogress:function(progress) {
        
        },
        ontaskstart:function(taskobj) {
            return true;
        }
    };
    
    $.extend(_options, options);
    
    //简单的Cookie帮助函数
    var setCookie = function(cname,cvalue,exdays) {
        var d = new Date();
        d.setTime(d.getTime()+(exdays*24*60*60*1000));
        var expires = "expires="+d.toGMTString();
        document.cookie = cname + "=" + cvalue + "; " + expires;
    };

    var getCookie = function(cname) {
        var name = cname + "=";
        var ca = document.cookie.split(';');
        for(var i=0; i<ca.length; i++) 
        {
            var c = ca[i].trim();
            if (c.indexOf(name)==0) return c.substring(name.length,c.length);
        }
        return "";
    };
    
    //
    //简单的文件HASH值计算,如果您不是十分考究,应该可以用于产品。
    //由于计算文件HASH值用到了多种数据,因此在HYFileUploader系统范围内发生HASH冲突的可能性应该非常小,应该可以放心使用。
    //获取文件的ID可以用任何算法来实现,只要保证做到同一文件的ID是相同的即可,获取的ID长度不要超过32字节
    //
    var getFileId = function (file) {
        //给浏览器授予一个唯一的ID用于区分不同的浏览器实例
        var clientid = getCookie("HUAYIUPLOAD");
        if (clientid == "") {
            //用一个随机值来做浏览器的ID,将作为文件HASH值的一部分
            var rand = parseInt(Math.random() * 1000);
            var t = (new Date()).getTime();
            clientid =rand+'T'+t;
            
            setCookie("HUAYIUPLOAD",clientid,365);
        }
        
        var info = clientid;
        if (file.lastModified)
            info += file.lastModified;
        if (file.name)
            info += file.name;
        if (file.size)
            info += file.size;
        //https://cdn.bootcss.com/blueimp-md5/2.10.0/js/md5.min.js
        var fileid = $.md5(info);
        return fileid;
    };
    
    var get_file_mimetype = function(filename) {
        var mimeType;
        var extPos = filename.lastIndexOf('.');
        if (extPos != -1) {
            var extName = filename.substr(extPos).toLowerCase();
            if (extName == '.rm' || extName == '.rmvb') {
                mimeType = 'video/realvideo';
            }
            else if (extName == '.dat') {
                mimeType = 'video/vcd';
            }
            else if (extName == '.ra') {
                mimeType = 'audio/realaudio';
            }
        }
        
        if (!mimeType) mimeType = 'bin/unknown';
        return mimeType;
    };

    //savepaths:{'video':'.mp4,*.mkv,.avi,.wmv','document':'.zip,*.rar','image':'.jpg,.jpeg,.png,.gif','audio':'.aac,.mp3'}
    var get_save_path = function(filename,savepaths) {
        var extPos = filename.lastIndexOf('.');
        if (extPos != -1) {
            var extName = filename.substr(extPos).toLowerCase();
            
            for (path in savepaths) {
                var extList = savepaths[path].split(',');
                if (extList.indexOf(extName) != -1) {
                    return path;
                }
            }
        }
        
        return '';
    }
    
    var bytesToSize = function (bytes) {
        if (bytes === 0) return '0 B';
        if (bytes < 1024) return (bytes + 'B');
        var k = 1024,sizes = ['B','KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
        var i = Math.floor(Math.log(bytes) / Math.log(k));
        return (bytes / Math.pow(k, i)).toFixed(2) + ' ' + sizes[i];
    }

    var _addFileToList = function (listViewer,fileobj) {
        var fileSources = '';
        var fileid = '';
        var filename = '',filesize = 0;
        var fileitemId = '';
        
        var $listViewer = $(listViewer);
        
        if (fileobj instanceof Array) {
            fileSources = '<filesources>';
            
            for(var i = 0; i < fileobj.length; i ++) {
                if (filename != '') filename += '|';
                var file = fileobj[i];
                
                filename += file.name;
                filesize += file.size;
                
                fileid = getFileId(file);
                
                fileSources += '<file id="'+fileid+'" fname="'+file.name+'" fsize="'+file.size+'"></file>';
                
                fileitemId += fileid;
                
                file.fileid = fileid;
            }
            fileSources += '</filesources>';
            
            fileitemId = $.md5(fileitemId);
        }
        else {
            var typename = typeof fileobj;
            
            if (typename != 'object') {
                return false;
            }
            
            if (fileobj.size == 0) {
                toast('文件 “'+fileobj.name+'” 长度为0无法上传,忽略此文件。');
                return false;
            }
            fileid = getFileId(fileobj);
            
            filename = fileobj.name;
            filesize = fileobj.size;
            fileSources = '<filesources><file id="'+fileid+'" fname="'+fileobj.name+'" fsize="'+fileobj.size+'"></file></filesources>';
            
            fileitemId = fileid;
            
            fileobj.fileid = fileid;
        }
        
        var file_existed = false;
            
        $listViewer.children('div.upload-fileitem').each(function(index,obj) {
            if (obj.id == fileitemId) {
                file_existed = true;
                return false;
            }
        });
        
        if (file_existed) {
            toast('添加的文件或文件组合 “'+filename+'” 已经存在,不重复添加');
            
            return false;
        }
            
        if (!listViewer.uploadOptions) {
            toast('添加的文件 “'+filename+'” 失败,文件列表未绑定上传对象。');
            
            return false;
        }
        
        //移除提示元素
        $listViewer.children('div.fileupload-help').remove();
        $listViewer.children('.notice').remove();
                        
        var server = listViewer.uploadOptions.server;
        var cid = listViewer.uploadOptions.cid;
        var serverid = listViewer.uploadOptions.serverid;
        var userid = listViewer.uploadOptions.userid;
        
        var html = '<div class="upload-fileitem" id="'+fileitemId+'" data-server="'+server+'" data-cid="'+cid+'" data-serverid="'+serverid+'" data-userid="'+userid+'">';
        html += fileSources;
        html += '<a class="remove_icon enabled" href="javascript:void(0);" title="移除"></a><a class="edit_icon enabled" href="javascript:void(0);" title="编辑"><i class="icon-edit"></i></a><i class="icon-ok-circle"></i>';
        html += '<div class="fileinfo"><div class="filename"><span title="'+filename+'">'+filename+'</span></div>';
        html += '<div class="upload-status"><div class="item"><span class="title">长度:</span><span class="value filesize" data-id="filesize">';
        html += bytesToSize(filesize);
        html += '</span></div>';
        html += '<div class="item"><span class="title">已传:</span><span class="value finish" data-id="finish" title="实际已经上传的文件数据长度">0</span></div>';
        html += '<div class="item"><span class="title">速率:</span><span class="value bitrate" data-id="bitrate">0</span></div>';
        html += '</div></div>';
        html += '<div class="progressbar"><div class="xprogressbar progress-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">';
        html += '<div class="progress-track progress-success" style="width: 0%;"></div></div><span class="percent-label text" data-id="percent-label">0%</span></div></div>';
        
        var $fileItem = $(html);
        $fileItem.appendTo($listViewer);
        
        //$('#upload-filelistview').mCustomScrollbar('update');
        
        $fileItem.find('a.remove_icon').click(function(evt) {
            evt.preventDefault();
            if (!$(this).hasClass('enabled')) {
                return;
            }
            var $item = $(this).parent();
            if ($item.attr('data-upload')=='finished') {
                toast('文件已经上传完成,不必移除,如要清空队列,请强制刷新浏览器。');
                return;
            }
            //remove_icon 的外层 upload-fileitem
            var fileItemDomObj = $item.get(0);
            
            var fileViewer = $item.get(0).fileViewer;
            if (fileViewer.uploader) {
                var uploader = fileViewer.uploader;
                
                if ($item.attr('data-upload')=='uploading') {
                    if (!uploader.options.forceremove) {
                        if (!confirm('视频文件正在上传,确定要放弃吗?')) {
                            return;
                        }
                    }
                                
                    //再次检查是否还在上传,如果没有启动上传uploader 属性无值
                    
                    if (uploader.curFileitemElement && uploader.curFileitemElement == fileItemDomObj) {
                        //仅仅执行取消上传操作
                        if (uploader.currentFile) {
                            if (uploader.currentXHR && typeof uploader.currentXHR.abort == 'function') {
                                if (uploader.options.forceremove) {
                                    uploader.taskIsForceRemove = true;
                                }
                                
                                uploader.currentXHR.abort();
                                return ;
                            }
                        }
                    }
                }
            }
                        
            if (fileItemDomObj.fileObject) {
                fileItemDomObj.fileObject = null;
                delete fileItemDomObj.fileObject;
            }
            $item.remove();
            
            var $fileitems = $(fileViewer).children('.upload-fileitem');
            if ($fileitems.length == 0) {
                $(fileViewer).html('<p class="notice">暂无上传任务</p>');
            }
            
            //$('#upload-filelistview').mCustomScrollbar('update');
        });
        
        $fileItem.find('a.edit_icon').click(function(evt) {
            evt.preventDefault();
        });
            
        var fileItemDom = $fileItem.get(0);
        var fileObject = {
            taskid:fileitemId,
            files:fileobj,
            totalfilesize:filesize,
            uploadedsize:0,
            finished:0
        }
        
        fileItemDom.fileObject = fileObject;
        fileItemDom.fileViewer = listViewer;
        
        return fileid;
    };
    //fileInfo 是上传服务器返回的
    var _onUploadedFileFinished = function(fileInfo) {
        var uploader = this;
        
        //fileInfo 对象的值可以用于保存到CMS里,而不仅仅是用于显示
        if (uploader.options.show_output && uploader.uploadResult) {
            var html = '<div class="file-object" >';
                html += '<div class="info-row"><span class="rlabel">文件名:</span><span class="rvalue">'+fileInfo.name+'</span></div>';
                html += '<div class="info-row"><span class="rlabel">保存路径:</span><span class="rvalue">'+fileInfo.path+'</span></div>';
                html += '<div class="info-row"><span class="rlabel">文件尺寸:</span><span class="rvalue">'+fileInfo.filesize+'</span></div>';
                html += '<div class="info-row"><span class="rlabel">访问URL:</span><span class="rvalue">'+fileInfo.url+'</span></div>';
            html += '</div>';
            //将文件信息显示在列表里
            uploader.uploadResult.innerHTML +=html;
        }
        
        uploader.currentXHR = null;
        
        if (uploader.curFileitemElement) {
            var $fileNode = $(uploader.curFileitemElement);
            
            var cid = $fileNode.attr('data-cid');
            var serverid = $fileNode.attr('data-serverid');
            var userid = $fileNode.attr('data-userid');
            
            uploader.currentFile.uploadInfo = fileInfo;
            
            var taskid = uploader.curFileitemElement.id;
            var mimeType=mimeType = uploader.currentFile.type;
                
            if (!mimeType) {
                mimeType = get_file_mimetype(fileInfo.name);
            }
                        
            var title = uploader.curFileitemElement.videotitle ? curFileitemElement.videotitle:'';
            var desc = uploader.curFileitemElement.videodesc ? curFileitemElement.videodesc:'';
            //记录文件完成上传
            
            var uploadEvent = {
                "event":"filefinished",
                "taskid":uploader.curFileitemElement.id,
                "fileid":uploader.currentFile.fileid,
                "cid":cid,
                "serverid":serverid,
                "userid":userid,
                "mimetype":mimeType,
                "filepath":fileInfo.path,
                "filename":fileInfo.name,
                "filesize":fileInfo.filesize,
                "url":fileInfo.url,
                "title":title,
                "desc":desc
            };
                        
            if (uploader.options.notice_url) {
                $.post(uploader.options.notice_url,uploadEvent,function(data) {
                    if (data.code != 1) {
                        toast(data.msg);
                    }
                },'json');
            }
            
            if (typeof uploader.options.onevent == 'function') {
                uploader.options.onevent.call(uploader, uploadEvent);
            }
                        
            var isTaskfinish = 1;
            
            if (uploader.curFileitemElement.fileObject.files instanceof Array) {
                for(var i = 0; i < uploader.curFileitemElement.fileObject.files.length; i ++) {
                    if (!uploader.curFileitemElement.fileObject.files[i].uploadInfo) {
                        isTaskfinish = 0;
                        break;
                    }
                }
            }
            else {
                isTaskfinish = 1;
            }
            
            uploader.currentFile = null;
            uploader.upload_start = false;
            
            if (isTaskfinish) {
                $fileNode.attr('data-upload','finished').addClass('finished');
                $fileNode.find('.remove_icon').removeClass('enabled');
                
                uploader.curFileitemElement.fileObject.finished = 1;
                                
                if (typeof uploader.options.ontaskfinish == 'function') {
                    uploader.options.ontaskfinish.call(uploader,uploader.curFileitemElement.fileObject);
                }
                
                uploader.curFileitemElement = null;
                //接着启动下一个任务
                if (!_startNextTaskupload.call(uploader)) {
                    if (uploader.startbutton) {
                        uploader.startbutton.disabled=false;
                        $(uploader.startbutton).removeClass('disabled');
                        uploader.startbutton = null;
                    }
                }
            }
            else {
                //继续上传本任务的下一个文件
                _startFileupload.call(uploader,false);
            }                
        }
    }

    /*
        文件上传处理代码
        fileObj : html5 File 对象
        start_offset: 上传的数据相对于文件头的起始位置
        fileid: 文件的ID,这个是上面的getFileId 函数获取的,
    */
    var _do_upload_file = function (fileObj,start_offset,fileid) {
        var uploader = this;
        
        var xhr = new XMLHttpRequest();
        var formData = new FormData();
        
        var blobfile;
        
        if(start_offset >= fileObj.size){
            return false;
        }
        
        var bitrateDiv = null;
        var finishDiv = null;
        var progressBar = null;
        var progressDiv = null;

        if (uploader.curFileitemElement) {
            var $fileItem = $(uploader.curFileitemElement);
            
            bitrateDiv = $fileItem.find('.bitrate').get(0);
            finishDiv = $fileItem.find('.finish').get(0);
            progressBar = $fileItem.find('.progress-track').get(0);
            progressDiv = $fileItem.find('.percent-label').get(0);
        
        }
        else {
            bitrateDiv = document.getElementById("bitrate");
            finishDiv = document.getElementById("finish");
            progressBar = document.getElementById('progressbar');
            progressDiv = document.getElementById('percent-label');
        }

        var oldTimestamp = 0;
        var oldLoadsize = 0;
        var totalFilesize = uploader.curFileitemElement.fileObject.totalfilesize;
        if (totalFilesize == 0) {
            
            return false;
        }
        //将fileObj 设置为当前正在上传的文件
        uploader.currentFile = fileObj;
        //已经完成了的文件的总尺寸
        var totalUploadedsize = uploader.curFileitemElement.fileObject.uploadedsize;
        
        var uploadProgress = function (evt) {
            if (evt.lengthComputable) {
                var uploadedSize = totalUploadedsize + evt.loaded + start_offset; 
                var percentComplete = Math.round(uploadedSize * 100 / totalFilesize);

                var timestamp = (new Date()).valueOf();
                var isFinish = evt.loaded == evt.total;

                if (timestamp > oldTimestamp || isFinish) {
                    var duration = timestamp - oldTimestamp;
                    if (duration > 500 || isFinish) {
                        var size =  evt.loaded - oldLoadsize;

                        var bitrate = (size * 8 / duration /1024) * 1000; //kbps
                        if (bitrate > 1000)
                            bitrate = Math.round(bitrate / 1000) + 'Mbps';
                        else
                            bitrate = Math.round(bitrate) + 'Kbps';

                        var finish = totalUploadedsize + evt.loaded + start_offset;

                        if (finish > 1048576)
                            finish = (Math.round(finish / (1048576/100)) / 100).toString() + 'MB';
                        else
                            finish = (Math.round(finish / (1024/100) ) / 100).toString() + 'KB';

                        progressBar.style.width = percentComplete+'%';
                        progressDiv.innerHTML = percentComplete.toString() + '%';
                        bitrateDiv.innerHTML = bitrate;
                        finishDiv.innerHTML = finish;

                        oldTimestamp = timestamp;
                        oldLoadsize = evt.loaded;
                        
                        if (typeof uploader.options.ontaskprogress == 'function') {
                            var progress = {
                                'bitrate':bitrate,
                                'finish':finish,
                                'percent':percentComplete
                            };
                            
                            uploader.options.ontaskprogress.call(uploader,progress);
                        }
                    }
                }
            }
            else {
                progressDiv.innerHTML = 'N/A';
            }
        }
        
        xhr.onreadystatechange = function(){
            if (xhr.readyState == 4 && xhr.status == 200) {
                if (console)
                    console.log( xhr.responseText );
            }
            else if (xhr.status == 400) {
            
            }
        };

        var uploadComplete = function (evt) {
            progressDiv.innerHTML = '100%';
            
            uploader.curFileitemElement.fileObject.uploadedsize += uploader.currentFile.size;
            
            var result = JSON.parse(evt.target.responseText);
            if (result.result == 'success') {
                _onUploadedFileFinished.call(uploader,result.files[0]);
            }
            else {
                uploader.currentFile = null;
                uploader.upload_start = false;
            
                toast(result.msg);
            }
        }

        var uploadFailed = function (evt) {
            if (!uploader.currentFile) return;
            if (uploader.reconnectId) return;
            uploader.upload_start = false;
            
            toast("检测到网络故障!两秒后尝试重连...");
                    
            uploader.reconnectId = window.setTimeout(function() {
                _startFileupload.call(uploader,true);
            },2000);
            
            if (typeof uploader.options.ontaskfail == 'function') {
                uploader.options.ontaskfail.call(uploader,uploader.curFileitemElement.fileObject);
            }
        }

        var uploadCanceled = function (evt) {
                        
            if (uploader.curFileitemElement) {
                $(uploader.curFileitemElement).attr('data-upload','aborted');
                
                
                var fileid = uploader.currentFile.fileid;
                //记录文件取消上传
                
                var uploadEvent = {
                    "event":'abortupload',
                    "taskid":uploader.curFileitemElement.id,
                    "fileid":fileid
                };
            
                if (uploader.options.notice_url) {
                    $.post(uploader.options.notice_url,uploadEvent,function(data) {
                        if (data.code != 1) {
                            toast(data.msg);
                        }
                    },'json');
                }
                
                if (typeof uploader.options.onevent == 'function') {
                    uploader.options.onevent.call(uploader, uploadEvent);
                }
                if (uploader.taskIsForceRemove) {
                    delete uploader.curFileitemElement.fileObject;
                    $(uploader.curFileitemElement).remove();
                    uploader.curFileitemElement = null;
                    
                    var $fileitems = $(uploader.fileViewer).children('.upload-fileitem');
                    if ($fileitems.length == 0) {
                        $(uploader.fileViewer).html('<p class="notice">暂无上传任务</p>');
                    }
                }
            }
            
            uploader.currentFile = null;
            uploader.upload_start = false;
            
            if (uploader.startbutton) {
                uploader.startbutton.disabled=false;
                $(uploader.startbutton).removeClass('disabled');
                uploader.startbutton = null;
            }
            
            if (uploader.taskIsForceRemove) {
                uploader.currentXHR = null;
                
                delete uploader.taskIsForceRemove;
                //在可以强制删除的情况下继续执行下一个任务,但不提示toast
                _startNextTaskupload.call(uploader,true);
            }
            else {
                toast("上传已经被暂停取消或者浏览器断开了连接!");
                if (typeof uploader.options.onpause == 'function') {
                    uploader.options.onpause.call(uploader);
                }
            }
        }
        
        
        //设置超时时间,由于是上传大文件,因此千万不要设置超时
        //xhr.timeout = 20000;
        //xhr.ontimeout = function(event){
        //        alert('文件上传时间太长,服务器在规定的时间内没有响应!');
        //}         

        xhr.overrideMimeType("application/octet-stream"); 

        var mimeType = fileObj.type;
                
        if (!mimeType) {
            mimeType = get_file_mimetype(fileObj.name);
        }
                
        //附加的文件数据应该放在请求的前面
        var cid = uploader.curFileitemElement.getAttribute('data-cid');
        var serverid = uploader.curFileitemElement.getAttribute('data-serverid');
        formData.append('channelid', cid);
        formData.append('filename', fileObj.name);
        //必须将fileid信息传送给服务器,服务器只有在获得了fileid信息后才对文件做断点续传处理
        formData.append('fileid', fileid);
        //请将文件数据放在最后的域
        var filesize = fileObj.size;
        var blob = fileObj.slice(start_offset,filesize);
        if('msSaveOrOpenBlob' in navigator){
            formData.append("file",blob, fileObj.name);
        }
        else {
            var fileOfBlob = new File([blob], fileObj.name);
            formData.append('file', fileOfBlob);
        }
            
        xhr.upload.addEventListener("progress", uploadProgress, false);
        
        xhr.addEventListener("load", uploadComplete, false);
        xhr.addEventListener("error", uploadFailed, false);
        xhr.addEventListener("abort", uploadCanceled, false);
        
        var upload_url_full = uploader.upload_file_url + get_save_path(fileObj.name,uploader.options.savepaths);
        
        xhr.open('POST', upload_url_full);
        //
        xhr.send(formData);
        uploader.currentXHR = xhr;
        uploader.upload_start = true;
        toast('');
        if (start_offset == 0) {
            
            var uploadEvent = {
                "event":'startupload',
                "fileid":fileid,
                "cid":cid,
                "serverid":serverid,
                "filename":fileObj.name,
                "mimetype":mimeType
            };
            
            //开始文件上传
            if (uploader.options.notice_url) {
                $.post(uploader.options.notice_url,uploadEvent,function(data) {
                    if (data.code != 1) {
                        toast(data.msg);
                    }
                },'json');
            }
            
            if (typeof uploader.options.onevent == 'function') {
                uploader.options.onevent.call(uploader, uploadEvent);
            }
        }
        else {
            //记录文件断点续传
            var uploadEvent = {
                "event":'resumeupload',
                "fileid":fileid,
                "cid":cid,
                "serverid":serverid,
                "filename":fileObj.name,
                "mimetype":mimeType
            };
            
            if (uploader.options.notice_url) {
                $.post(uploader.options.notice_url,uploadEvent,function(data) {
                    if (data.code != 1) {
                        toast(data.msg);
                    }
                },'json');
            }
            
            if (typeof uploader.options.onevent == 'function') {
                uploader.options.onevent.call(uploader, uploadEvent);
            }
        }
    }
    /*
        处理上传服务器直接返回的上传完成信息
    */
    var _do_finish_file = function (fileObj,fileInfo) {
        var uploader = this;
        
        var bitrateDiv = null;
        var finishDiv = null;
        var progressBar = null;
        var progressDiv = null;

        if (!uploader.curFileitemElement) {
            return;
        }
        
        var $fileItem = $(uploader.curFileitemElement);
        
        bitrateDiv = $fileItem.find('.bitrate').get(0);
        finishDiv = $fileItem.find('.finish').get(0);
        progressBar = $fileItem.find('.progress-track').get(0);
        progressDiv = $fileItem.find('.percent-label').get(0);
        
        //模拟进度完成
        
        var totalFilesize = uploader.curFileitemElement.fileObject.totalfilesize;
        if (totalFilesize == 0) {
            return false;
        }
        //将fileObj 设置为当前正在上传的文件
        uploader.currentFile = fileObj;
        //已经完成了的文件的总尺寸
        var totalUploadedsize = uploader.curFileitemElement.fileObject.uploadedsize;
        
        var uploadedSize = totalUploadedsize + fileObj.size; 
        var percentComplete = Math.round(uploadedSize * 100 / totalFilesize);
                
        var finish = uploadedSize;

        if (finish > 1048576)
            finish = (Math.round(finish / (1048576/100)) / 100).toString() + 'MB';
        else
            finish = (Math.round(finish / (1024/100) ) / 100).toString() + 'KB';

        progressBar.style.width = percentComplete+'%';
        progressDiv.innerHTML = percentComplete.toString() + '%';
        bitrateDiv.innerHTML = '';
        finishDiv.innerHTML = finish;
        
        uploader.curFileitemElement.fileObject.uploadedsize += fileObj.size;
        
        _onUploadedFileFinished.call(uploader,fileInfo);
    }

    //doupload
    var _startFileupload = function (bReconnect) {
        var uploader = this;
        
        if (!this.options) {
            alert("发生未知错误,没有正确绑定上传对象。")
            return false;
        }
        
        if (!this.curFileitemElement) {
            alert("请选择文件后再试!")
            return false;
        }
        
        if (!bReconnect) {
            if (uploader.currentFile) {
                alert("上传对象已经有文件在上传了,不要重复调用!")
                return false;
            }
        }
        
        var fileObject = this.curFileitemElement.fileObject;
        if (fileObject.finished) {
            return false;
        }
        
        var fileObj = null;
        
        if (!this.currentFile) {
                        
            if (this.curFileitemElement.fileObject.files instanceof Array) {
                for(var i = 0; i < this.curFileitemElement.fileObject.files.length; i ++) {
                    if (!this.curFileitemElement.fileObject.files[i].uploadInfo) {
                        fileObj = this.curFileitemElement.fileObject.files[i];
                        break;
                    }
                }
            }
            else {
                if (!this.curFileitemElement.fileObject.files.uploadInfo) {
                    fileObj = this.curFileitemElement.fileObject.files;
                }
            }
            
            if (!fileObj) {
                alert("任务文件已经上传完毕!")
                return false;
            }
            
            //this.currentFile = fileObj;
        }
        else {
            fileObj = this.currentFile;
        }
        
        var fileid = getFileId(fileObj);
        var t = (new Date()).getTime();
        //通过以下URL获取文件的断点续传信息,必须的参数为fileid,后面追加t参数是避免浏览器缓存
        var url = this.resume_info_url + '?fileid='+fileid + '&t='+t;
        
        var ajax = new XMLHttpRequest();
        
        ajax.onerror = function (e) {
            if (uploader.reconnectId) 
                return;
            
            uploader.reconnectId = window.setTimeout(function() {
                uploader.currentXHR = null;
                
                _startFileupload.call(uploader,true);
            },2000);
        };
        
        ajax.onabort = function (evt) {
            uploader.currentFile = null;
            uploader.upload_start = false;
            
            if (uploader.curFileitemElement) {
                $(uploader.curFileitemElement).attr('data-upload','aborted');
                
                var fileid = uploader.curFileitemElement.id;
                        
                uploader.curFileitemElement = null;
            }
            if (uploader.startbutton) {
                uploader.startbutton.disabled=false;
                $(uploader.startbutton).removeClass('disabled');
                uploader.startbutton = null;
            }
                        
            toast("上传被取消或者浏览器断开了连接!");
        }
        
        ajax.onreadystatechange = function () {
            
            if(this.readyState == 4){
                if (bReconnect) {
                    //目前是重连状态,清除重连标志
                    uploader.reconnectId = 0;
                }
            
                if (this.status == 200){
                    var response = this.responseText;
                    
                    var result = JSON.parse(response);
                    if (!result) {
                        alert('服务器返回的数据不正确,可能是不兼容的服务器,上传无法继续!');
                        return;
                    }
                    //断点续传信息返回的文件对象包含已经上传的尺寸
                    var uploadedBytes = result.file && result.file.size;
                    if (!result.file.finished && uploadedBytes < fileObj.size) {
                        _do_upload_file.call(uploader,fileObj,uploadedBytes,fileid);
                    }
                    else {
                        //文件已经上传完成了,就不要再上传了,直接返回结果就可以了
                        _do_finish_file.call(uploader,fileObj,result.file);
                    }
                }else {
                    uploader.currentFile = null;
                    uploader.currentXHR = false;
            
                    toast('获取文件断点续传信息失败,服务器错误码:'+this.status+', 上传无法继续!');
                }  
            }
        }

        if (typeof this.options.ontaskstart == 'function') {
            if (false === this.options.ontaskstart.call(this,this.curFileitemElement.fileObject)) {
                return false;
            }
        }
        
        ajax.open('get',url,true);
        ajax.send(null);
        this.currentXHR = ajax;
        
        return true;
    }

    //任务上传完成后,开启下一个任务
    function _startNextTaskupload(isAfterAbort) {
        var uploader = this;
        var $fileitems = $(this.fileViewer).children('.upload-fileitem');
        $fileitems.each(function(index,elm) {
            if ($(elm).attr('data-upload') != 'finished') {
                if (elm.fileObject) {
                    $(elm).attr('data-upload','uploading');
                    uploader.curFileitemElement = elm;
                    return false;
                }
            }
        });
        
        if (!uploader.curFileitemElement) {
            
            if (typeof this.options.onend == 'function') {
                this.options.onend.call(this);
            }
            if (!isAfterAbort) {            
                toast('所有文件已经上传完毕!');
            }
            return false;
        }
        
        _startFileupload.call(uploader,false);
        
        return true;
    }

    
    var fileViewer = document.getElementById(_options.filelist);
    if (!fileViewer) {
        return null;
    }
    
    fileViewer.uploadOptions = _options;
    
    if (_options.enabledrop) {
        //禁止浏览器打开文件的默认行为
        document.addEventListener("drop",function(e){//拖离 
            e.preventDefault();
        });
        document.addEventListener("dragleave",function(e){//拖后放 
            e.preventDefault();
        });
        document.addEventListener("dragenter",function(e){//拖进
            e.preventDefault();
        });
        document.addEventListener("dragover",function(e){//拖来拖去  
            e.preventDefault();
        });

        fileViewer.addEventListener("drop",function (e) {
            var fileList = e.dataTransfer.files; //获取文件对象
            if (console) console.log(fileList)
            //检测是否是拖拽文件到页面的操作
            if(fileList.length == 0){
                return false;
            }
            
            for(var i = 0; i < fileList.length; i++) {
                _addFileToList(this,fileList[i]);
            }
        },false);
    }
    
    if (_options.fileselector) {
        var fileSelector = document.getElementById(_options.fileselector);
        if (fileSelector) {
            fileSelector.fileViewer = fileViewer;
            
            fileSelector.addEventListener("change",function (e) {
                var fileViewer = this.fileViewer;
                
                if (window.File && window.FileList) {
                    if (this.files.length) {
                        
                        for(var i = 0; i < this.files.length; i++) {
                            _addFileToList(fileViewer,this.files[i]);
                        }
                        
                        this.value = '';
                    }
                }
                else {
                    window.alert('抱歉,你的浏览器不支持FileAPI,请升级浏览器!');
                }
            },false);
        }
    }
    
    return {
        options:_options,
        fileViewer: fileViewer,
        resume_info_url:'',
        upload_file_url:'',
        upload_start:false,
        startbutton:null,
        currentFile: null,  //当前正在上传的文件对象
        curFileitemElement : null, //当前正在上传的 文件 DOM元素
        
        start:function(btnObj) {
            if (this.upload_start) {
                alert("文件上传正在进行中,请稍候再点击重复长传!")
                return false;
            }
        
            var $fileitems = $(this.fileViewer).children('.upload-fileitem');
            if ($fileitems.length == 0) {
                toast('没有任何可以上传的文件,请先添加文件再试!');
                return false;
            }
            
            var uploader = this;
            this.fileViewer.uploader = this;
            
            $fileitems.each(function(index,elm) {
                if ($(elm).attr('data-upload') != 'finished') {
                    if (elm.fileObject) {
                        $(elm).attr('data-upload','uploading');
                        uploader.curFileitemElement = elm;
                        
                        var uploadsrv_addr = 'http://'+uploader.options.server;
                        uploader.resume_info_url = uploadsrv_addr+'/resume/';
                        uploader.upload_file_url = uploadsrv_addr+'/upload/';
            
                        return false;
                    }
                }
            });
            
            if (!this.curFileitemElement) {
                toast('所有文件已经上传完毕,请添加新文件后再试!');
                return false;
            }

            this.startbutton = btnObj;
            
            if (this.options.authize_url) {
                //如果提供了授权验证URL,则向CMS平台进行验证
                $.get(this.options.authize_url,{"t":(new Date).getTime()},function(result) {
                    if (result.code != 1) {
                        if (typeof this.options.onend == 'function') {
                            this.options.onend.call(this);
                        }
                        toast(result.msg);
                        return;
                    }
                    
                    if (_startFileupload.call(uploader,false)) {
                        if (uploader.startbutton) {
                            uploader.startbutton.disabled=true;
                            $(uploader.startbutton).addClass('disabled');
                        }
                    }
                    else {
                        if (typeof this.options.onend == 'function') {
                            this.options.onend.call(this);
                        }
                    }
                },'json');
                
                return true;
            }
            else {
                if (_startFileupload.call(uploader,false)) {
                    if (uploader.startbutton) {
                        uploader.startbutton.disabled=true;
                        $(uploader.startbutton).addClass('disabled');
                    }
                    return true;
                }
                return false;
            }
        },
        
        pause:function () {
            if (this.currentFile) {
                if (this.currentXHR && typeof this.currentXHR.abort == 'function') {
                    this.currentXHR.abort();
                }
            }
        },
        
        addtask:function(fileobjs) {
            _addFileToList(this.fileViewer,fileobjs);
        },
        
        removetask:function(taskid) {
            var $fileItemElm = $('#'+taskid);
            if ($fileItemElm.length == 1 && $fileItemElm.hasClass('upload-fileitem')) {
                if ($fileItemElm.attr('data-upload')=='uploading') {
                    alert('任务正在进行中,请停止任务后再删除!');
                    return;
                }
                
                var itemDom = $fileItemElm.get(0);
                if (itemDom.fileObject) {
                    delete itemDom.fileObject;
                }
                
                $fileItemElm.remove();
            }
        },
        
        clear:function() {
            var $fileitems = $(this.fileViewer).children('.upload-fileitem');
            if ($fileitems.length == 0) {
                return false;
            }
            
            if (!confirm('确实要清除任务列表吗?')) {
                return false;
            }
            
            if (this.currentFile) {
                if (this.currentXHR && typeof this.currentXHR.abort == 'function') {
                    this.currentXHR.abort();
                }
                
                if (typeof this.options.onend == 'function') {
                    this.options.onend.call(this);
                }
            }
            
            $fileitems.each(function(index,elm) {
                if (elm.fileObject) delete elm.fileObject;
            });
            
            $fileitems.remove();
            this.curFileitemElement = null;
            this.currentFile = null;
            this.currentXHR = null;
                        
            $(this.fileViewer).html('<p class="notice">暂无上传任务</p>');
        },
                
        version: "2.0"
    };
};

 

 

 

 

 

 

 

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐