原理如:3G的大文件分1500個2M二進度文件,通post方法發送給node服務,服務器全部接收到文件後,進組裝生成你上文件。
需要了解以下node庫,:
async: http://caolan.github.io/async/
multiparty:https://www.npmjs.com/package/multiparty
客戶端代碼:
<div class="hei-bg" style="display:block;" >
<div class="user-info" style="display:block;" >
<div class="tc">請上傳大文件</div>
<div class="user-pic picw320"><input id="uppic" type="file" ><img id="upimg" src="/img/bbb.jpg"></div>
<div id="jd" class="jdb">進度</div>
<div><div id="userbtn" class="bg-main tc userbtn">確定</div></div>
</div>
</div>
<script src="./res/js/lib/jquery.min.js"></script>
<script src="./res/js/lib/async.min.js"></script>//異步庫
<script>
$(function(){
$('#userbtn').on('click',function(){
var file= $("#uppic")[0].files[0],//上傳文件主體
name = file.name, //文件名
size = file.size, //總大小
succeed = 0; //當前上傳數
var shardSize = 2 *1024*1024, //以2MB爲一個分片
shardCount = Math.ceil(size / shardSize); //總片數
/*生成上傳分片文件順充,通過async.eachLimit()進行同步上傳
attr裏面是[0,1,2,3...,最後一位]
*/
var attr=[];
for(var i=0;i<shardCount;++i){
attr.push(i);
}
async.eachLimit(attr,1,function(item,callback){
var i=item;
var start = i * shardSize,//當前分片開始下標
end = Math.min(size, start + shardSize);//結束下標
//構造一個表單,FormData是HTML5新增的
var form = new FormData();
form.append("data", file.slice(start,end)); //slice方法用於切出文件的一部分
form.append("name", name);//文件名字
form.append("total", shardCount); //總片數
form.append("index", i + 1); //當前片數
//Ajax提交
$.ajax({
url: "/dafile",
type: "POST",
data: form,
timeout:120*1000,
async: false, //同步
processData: false, //很重要,告訴jquery不要對form進行處理
contentType: false, //很重要,指定爲false才能形成正確的Content-Type
success: function(data){
++succeed;
var data=eval('('+data+')');
/*返回code爲0是成功上傳,1是請繼續上傳*/
if(data.code==0){
console.log(data.msg);
}else if(data.code==1){
console.log(data.msg);
}
//生成當前進度百分比
var jd=Math.round(succeed/shardCount*100)+'%';
$('.jdb').html(jd);
/*如果是線上,去掉定時,直接callback(),
這樣寫是爲方便,本地測試看到進度條變化
因爲本地做上傳測試是秒傳,沒有時間等待*/
setTimeout(callback,50);
}
});
},function(err){
console.log('上傳成功');
});
});
});
</script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
服務器代碼:
function user(req,res,config){
var path=require('path');
var fs=require('fs');
var multiparty = require('multiparty');//文件上傳模塊
var async = require('async');//異步模塊
var form = new multiparty.Form();//新建表單
//設置編輯
form.encoding = 'utf-8';
//設置文件存儲路徑
form.uploadDir = "Uploads/img/";
//設置單文件大小限制
// form.maxFilesSize = 200 * 1024 * 1024;
/*form.parse表單解析函數,fields是生成數組用獲傳過參數,files是bolb文件名稱和路徑*/
form.parse(req, function (err,fields,files) {
files=files['data'][0];//獲取bolb文件
var index=fields['index'][0];//當前片數
var total=fields['total'][0];//總片數
var name=fields['name'][0];//文件名稱
var url='Uploads/img/'+name+index;//臨時bolb文件新名字
fs.renameSync(files.path,url);//修改臨時文件名字
var pathname='Uploads/img/'+name;//上傳文件存放位置和名稱
if(index==total){//當最後一個分片上傳成功,進行合併
/*
檢查文件是存在,如果存在,重新設置名稱
*/
fs.access(pathname,fs.F_OK,(err) => {
if(!err){
var myDate=Date.now();
pathname='Uploads/img/'+myDate+name;
console.log(pathname);
}
});
//這裏定時,是做異步串行,等上執行完後,再執行下面
setTimeout(function(){
/*進行合併文件,先創建可寫流,再把所有BOLB文件讀出來,
流入可寫流,生成文件
fs.createWriteStream創建可寫流
aname是存放所有生成bolb文件路徑數組:
['Uploads/img/3G.rar1','Uploads/img/3G.rar2',...]
*/
var writeStream=fs.createWriteStream(pathname);
var aname=[];
for(var i=1;i<=total;i++){
var url='Uploads/img/'+name+i;
aname.push(url);
}
//async.eachLimit進行同步處理
async.eachLimit(aname,1,function(item,callback){
//item 當前路徑, callback爲回調函數
fs.readFile(item,function(err,data){
if(err)throw err;
//把數據寫入流裏
writeStream.write(data);
//刪除生成臨時bolb文件
fs.unlink(item,function(){console.log('刪除成功');})
callback();
});
},function(err){
if (err) throw err;
//後面文件寫完,關閉可寫流文件,文件已經成生完成
writeStream.end();
//返回給客服端,上傳成功
var data=JSON.stringify({'code':0,'msg':'上傳成功'});
res.writeHead(200, {'Content-Type': 'text/html;charset=utf-8'});
res.end(data);//返回數據
});
},50);
}else{//還沒有上傳文件,請繼續上傳
var data=JSON.stringify({'code':1,'msg':'繼續上傳'});
res.writeHead(200, {'Content-Type': 'text/html;charset=utf-8'});
res.end(data);//返回數據
}
});
return user;
};
exports.init = user;
這是初步設計方案,後期加異步上傳和斷點續傳功能。。
有什麼疑問或更好解決方案,可以加QQ羣和我交流: 293851491