java分片上傳,斷點續傳

1,是基於webUploader的前端開源插件實現的大大文件 分片上傳功能:
javascript

  1. 四種文件上傳格式(之後要改進的地方)css

    1. 普通按鈕點擊上傳html

    2. 拖拽上傳前端

    3. 複製粘貼上傳java

    4. 拖拽+按鈕+複製粘貼上傳jquery

    5. 多線程上傳文件
      web


2,簡單的文件分片合併上傳原理:ajax

借鑑的博客:  地址
json

前段頁面
後端

<input type="file" id="file6" multiple>
<button type="button" class="btnFile6">分片上傳6</button>
<div class="result"></div>
//方式6
 $(".btnFile6").click(function () { 
     var upload = function (file, skip) {
         var formData = new FormData();//初始化一個FormData對象
         var blockSize = 1000000;//每塊的大小
         var nextSize = Math.min((skip + 1) * blockSize, file.size);//讀取到結束位置             
         var fileData = file.slice(skip * blockSize, nextSize);//截取 部分文件 塊
         formData.append("file", fileData);//將 部分文件 塞入FormData
         formData.append("fileName", file.name);//保存文件名字
         $.ajax({
             url: "/Home/SaveFile6",
             type: "POST",
             data: formData,
             processData: false,  // 告訴jQuery不要去處理髮送的數據
             contentType: false,   // 告訴jQuery不要去設置Content-Type請求頭
             success: function (responseText) {
                 $(".result").html("已經上傳了" + (skip + 1) + "塊文件");
                 if (file.size <= nextSize) {//若是上傳完成,則跳出繼續上傳
                     alert("上傳完成");
                     return;
                 }
                 upload(file, ++skip);//遞歸調用
             }
         });
     };
 
     var file = $("#file6")[0].files[0];
     upload(file, 0);
 });

後端代碼

public string SaveFile6()
{
    //保存文件到根目錄 App_Data + 獲取文件名稱和格式
    var filePath = Server.MapPath("~/App_Data/") + Request.Form["fileName"];
    //建立一個追加(FileMode.Append)方式的文件流
    using (FileStream fs = new FileStream(filePath, FileMode.Append, FileAccess.Write))
    {
        using (BinaryWriter bw = new BinaryWriter(fs))
        {
            //讀取文件流
            BinaryReader br = new BinaryReader(Request.Files[0].InputStream);
            //將文件留轉成字節數組
            byte[] bytes = br.ReadBytes((int)Request.Files[0].InputStream.Length);
            //將字節數組追加到文件
            bw.Write(bytes);
        }
    }
    return "保存成功";
}

3,監控文件上傳的三個時間點:(上傳)本地項目實例

  1. 時間點1: 全部分塊進行上傳以前(1,算文件的惟一標識,2,判斷文件是否秒傳)

    1. 計算分片文件的MD5惟一標識,

    2. 請求後臺是否保存過該文件,存在跳過該文件,不存在則繼續上傳.

  2. 時間點2: 若是分片上傳, 每一個分片上傳以前(1, 通知詢問後臺是否已經保存成功, 用於斷點續傳)

    1. 每一個每一個分塊有每一個分塊的下標和大小.

    1. 請求後臺是否保存過當前分塊,存在跳過該分片文件,實現斷點續傳

    2. 請求後臺是否保存完成該文件信息,若是保存過,則跳過; 若是不存在或者不完整則發送該分塊內容

    3. 攜帶當前文件的惟一標識到後臺, 用於讓後臺建立保存該文件分塊的目錄

  3. 時間點3: 全部分塊上傳成功以後(1, 通知後臺進行分塊文件的合併工做)

    1. 前臺通知後臺合併文件


3,後臺文件的處理:

FileChannel outChannel  = FileOutputStream(file).getChannel()FileChannel inChannel(File tmp : fileList) {
   inChannel = FileInputStream(tmp).getChannel()inChannel.transferTo(inChannel.size()outChannel)inChannel.close()file.delete()}
(outChannel != ) {
   (f)outChannel.close()}







文件上傳的前端代碼:






<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<title>webuploaderDemo</title>

<link rel="stylesheet" href="staticresource/layui/css/layui.css">

<link rel="stylesheet" href="staticresource/webuploader/upload.media.css">

<link rel="stylesheet" href="staticresource/css/style.css">

<script src="staticresource/js/jquery-3.2.1.min.js"></script>

<script src="staticresource/layui/layui.all.js"></script>

<script src="staticresource/webuploader/webuploader.js"></script>

<style type="text/css">





.upload-table{

padding: 2px 12px;

}



.fileQueue{

height: 300px;

overflow-x: hidden;

overflow-y: auto;

background-color: #f8f8f8;

width: 100%;

}

.hiden{

display: none

}

</style>



<script type="text/javascript">

var base = "/upload";

var element;

var layer;

var index=0;

var batchWebUpload;

var fileAllNum=0;

var fileAllSize=0;

var successNum=0;

var successSize=0;

var  percentages = {}; // 全部文件的進度信息,key爲file id

$(function(){

layui.use(['layer','element','code'], function(){

element = layui.element;

layer = layui.layer;

layui.code({

elem: 'pre', //默認值爲.layui-code

encode: true, //是否轉義html標籤。默認不開啓

about: false

});

layer.ready(function(){

showWindow()

});

});



var fileCheckUrl=base+"/file/checkFile";//檢測文件是否存在url

var checkChunkUrl=base+"/file/checkChunk";//檢測分片url

var mergeChunksUrl=base+"/file/mergeChunks";//合併文件請求地址



//監控文件上傳的三個時間點(注意:該段代碼必須放在WebUploader.create以前)

//時間點1::全部分塊進行上傳以前(1.能夠計算文件的惟一標記;2.能夠判斷是否秒傳)

//時間點2: 若是分塊上傳,每一個分塊上傳以前(1.詢問後臺該分塊是否已經保存成功,用於斷點續傳)

//時間點3:全部分塊上傳成功以後(1.通知後臺進行分塊文件的合併工做)

WebUploader.Uploader.register({

"before-send-file":"beforeSendFile",

"before-send":"beforeSend",

"after-send-file":"afterSendFile"

},{

//時間點1::全部分塊進行上傳以前調用此函數

    //時間點1::全部分塊進行上傳以前調用此函數

beforeSendFile:function(file){//利用md5File()方法計算文件的惟一標記符

        //建立一個deffered

var deferred = WebUploader.Deferred();

//1.計算文件的惟一標記,用於斷點續傳和秒傳,獲取文件前2m的md5值,越小越快,防止碰撞,把文件名文件大小和md5拼接做爲文件惟一標識

(new WebUploader.Uploader()).md5File(file,0,2*1024*1024).progress(function(percentage){

           

           

}).then(function(val){

fileMd5 = file.size+"-"+val+"-"+file.name;//防止碰撞,把文件名文件大小和md5拼接做爲文件惟一標識

file.fileMd5=fileMd5;

//2.請求後臺是否保存過該文件,若是存在,則跳過該文件,實現秒傳功能

$.ajax({

type:"POST",

url:fileCheckUrl,

data:{

fileMd5:fileMd5//文件惟一標記

},

dataType:"json",

success:function(response){

                        console.log(response);

if(response.success){

                            console.log($("#"+file.id).find('.percent').html());

                            $("#"+file.id).find('.percent').html("100%");

                            $("#"+file.id).find(".layui-progress-bar").removeClass('layui-bg-blue');

                            element.progress('progress_'+file.id, '100%');

                            batchWebUpload.skipFile(file);

                            successNum++;

                            successSize+=file.size;

//若是存在,則跳過該文件,秒傳成功

deferred.reject();



}else{

//繼續上傳

deferred.resolve();



}

}

}

);



});

//返回deffered

return deferred.promise();

},

//時間點2:若是有分塊上傳,則 每一個分塊上傳以前調用此函數

//block:表明當前分塊對象

beforeSend:function(block){//向後臺發送當前文件的惟一標記,用於後臺建立保存分塊文件的目錄

    //1.請求後臺是否保存過當前分塊,若是存在,則跳過該分塊文件,實現斷點續傳功能

var deferred = WebUploader.Deferred();

//請求後臺是否保存完成該文件信息,若是保存過,則跳過,若是沒有,則發送該分塊內容

$.ajax({

type:"POST",

url:checkChunkUrl,

data:{

//文件惟一標記,敲黑板劃重點了,不要用file.fileMd5,否則服務器上的文件會出錯

fileMd5: block.file.fileMd5,

//當前分塊下標

chunk:block.chunk,

//當前分塊大小

chunkSize:block.end-block.start

},

dataType:"json",

success:function(response){

if(response.success){

                //分塊存在,跳過該分塊

deferred.reject();



}else{

                //分塊不存在或者不完整,從新發送該分塊內容

deferred.resolve();

}

}

}

);

//攜帶當前文件的惟一標記到後臺,用於讓後臺建立保存該文件分塊的目錄

// this.owner.options.formData.fileMd5 = block.file.fileMd5;

return deferred.promise();

},

//時間點3:全部分塊上傳成功以後調用此函數

afterSendFile:function(file){//前臺通知後臺合併文件

    //1.若是分塊上傳,則經過後臺合併全部分塊文件

        //請求後臺合併文件

$.ajax({

type:"POST",

url:mergeChunksUrl,

data:{

//文件惟一標記

fileMd5:file.fileMd5,

//文件名稱

fileName:file.name

},

dataType:"json",

success:function(response){

              console.log("合併分片完成");

              console.log(response);

              $("#"+file.id).find(".layui-progress-bar").removeClass('layui-bg-blue');

                element.progress('progress_'+file.id, '100%');

                $("#"+file.id).find('.percent').html("100%");

                successNum++;

                successSize+=file.size;

}

});

}

});



var batchWebUpload_btn = $("#batchWebUpload");



batchWebUpload = WebUploader.create({

auto:false,

pick: {

id: batchWebUpload_btn,//指定選擇文件的按鈕容器,不指定則不建立按鈕。注意 這裏雖然寫的是 id, 不只支持 id, 還支持 class, 或者 dom 節點。

//label : title, 官方建議採用 innerHTML 代替

//innerHTML : title,

multiple :true //開啓文件多選

},

flash:base+'/staticresource/webuploader/Uploader.swf',//ie9一下會自動使用flash上傳

/** accept:{//不驗證文件類型了

    title: '不驗證了',//字符串類型,文字描述

extensions: '*',//容許的文件後綴,不帶點,多個用逗號分割。

mimeTypes: 'application/*,'//多個用逗號分割,怎麼不知道咋寫的,參考w3c  MIME 參考手冊,傳送門 http://www.w3school.com.cn/media/media_mimeref.asp

},**/

server: base+"/file/uploadChunks",

//壓縮圖片,若是圖片尺寸超過設置的尺寸,會自動壓縮圖片,必要時會裁剪

compress:{

width: 600,

height: 600,



// 圖片質量,只有type爲`image/jpeg`的時候纔有效。

quality: 90,



// 是否容許放大,若是想要生成小圖的時候不失真,此選項應該設置爲false.

allowMagnify: false,



// 是否容許裁剪

crop: false,



// 是否保留頭部meta信息。

preserveHeaders: true,



// 若是發現壓縮後文件大小比原來還大,則使用原來圖片

// 此屬性可能會影響圖片自動糾正功能

noCompressIfLarger: false

},

// 單位字節,若是圖片大小小於此值,不會採用壓縮。512k  512*1024,若是設置爲0,原圖尺寸大於設置的尺寸就會壓縮;若是大於0,只有在原圖尺寸大於設置的尺寸,而且圖片大小大於此值,纔會壓縮

compressSize: 0,

fileNumLimit : 10,//驗證文件總數量, 超出則不容許加入隊列,默認值:undefined,若是不配置,則不限制數量

fileSizeLimit : 100*1024*1024*1024, //1kb=1024*1024,驗證文件總大小是否超出限制, 超出則不容許加入隊列。

fileSingleSizeLimit :10*1024*1024*1024, //驗證單個文件大小是否超出限制, 超出則不容許加入隊列。

chunked:true,//是否開啓分片上傳

threads:3,

chunkSize:5*1024*1024,//若是要分片,每一片的文件大小

prepareNextFile:false//在上傳當前文件時,準備好下一個文件,請設置成false,否則開啓文件多選你瀏覽器會卡死

});

//當文件上傳成功時觸發。file {File} File對象, response {Object}服務端返回的數據

batchWebUpload.on('uploadSuccess',function(file,response){

layer.msg("上傳完成,服務端返回信息請按F12,看控制檯:");

console.log(file);

if(response.success){

console.log(response);

}

})

//錯誤類型。文件驗證不經過時觸發

//錯誤類型說明:Q_EXCEED_NUM_LIMIT 上傳文件超過限制的數量

//Q_EXCEED_SIZE_LIMIT文件總大小超出限制

//Q_TYPE_DENIED 當文件類型不對

batchWebUpload.on("error",function(type,file){

console.log(type);

if (type=="Q_TYPE_DENIED"){

layer.msg("只能上傳gif,jpg,jpeg,bmp,png格式文件");

}else if(type=="Q_EXCEED_SIZE_LIMIT"){

    layer.msg("全部的文件大小總和不能超過10M");

}else if(type=='F_EXCEED_SIZE'){

    layer.msg("單個文件大小不能超過1M");

}else if(type=='Q_EXCEED_NUM_LIMIT'){

    layer.msg(typeName+"最多隻能上傳10個");

}else if(type=='F_DUPLICATE'){

    layer.msg(file.name+"已經在上傳隊列,請勿重複上傳");

}else{

    layer.msg("上傳出錯");

}

})





//文件加入隊列

batchWebUpload.on("fileQueued",function(file){

fileAllNum++;

fileAllSize+=file.size;





var fileSize = (file.size/1024/1024.0).toFixed(2)+"M";

var progress = '<div class="layui-progress " lay-filter="progress_'+file.id

+'" lay-showPercent="yes" ><div class="layui-progress-bar layui-bg-blue" lay-percent="0"></div></div>';

var buttons = '<a id="pause_Btn_'+file.id+'" class="layui-btn layui-btn-xs  layui-btn-primary">暫停</a>';

buttons+='<a id="continue_Btn_'+file.id+'" class="layui-btn layui-btn-xs  layui-btn-primary hiden">繼續上傳</a>';

buttons+='<a id="delete_Btn_'+file.id+'" class="layui-btn layui-btn-xs  layui-btn-primary">刪除</a>';

var htm = '<tr id="'+file.id+

'"><td>'+file.name+

'</td> <td>'+file.ext+

'</td><td>'+fileSize+

'</td><td>'+progress+  '</td><td class="percent">0%</td><td class="status">等待上傳</td><td>'+buttons+'</td></tr>';

$("#fileList").append(htm);

//綁定事件

$("#pause_Btn_"+file.id).bind('click',function(){

batchWebUpload.stop(file);

})



$("#continue_Btn_"+file.id).bind('click',function(){

batchWebUpload.upload(file);

})



$("#delete_Btn_"+file.id).bind('click',function(){

batchWebUpload.removeFile( file, true );//從文件隊列移除

})



percentages[ file.id ] = [ file.size, 0 ];

file.on('statuschange', function( cur, prev ) {



// 成功

if ( cur === 'error' || cur === 'invalid' ) {

percentages[ file.id ][ 1 ] = 1;

}  else if ( cur === 'queued' ) {

percentages[ file.id ][ 1 ] = 0;

}

})

$("#"+file.id).find('.percent').html("0%");



element.progress('progress_'+file.id, '0%');

});



/**從文件隊列移除**/

batchWebUpload.on('fileDequeued', function( file ) {

fileAllNum--;

fileAllSize-=file.size;

delete percentages[ file.id ];

});

/**上傳以前**/

batchWebUpload.on('uploadBeforeSend', function( block, data, headers ) {

data.fileMd5 = block.file.fileMd5;

//block.file.chunks = block.chunks;//當前文件總分片數量

data.chunks = block.file.chunks;

});





/**上傳過程當中觸發,攜帶上傳進度**/

batchWebUpload.on('uploadProgress',function(file ,percentage){

var percent=(percentage*100 ).toFixed(2)+"%";

element.progress('progress_'+file.id, percent);//設置進度條百分比



if(JSON.stringify(percentages) != "{}"){

percentages[ file.id ][ 1 ] = percentage;

}

if(percentage==1){

percentages[ file.id ][ 1 ] = 1;

}

$("#"+file.id).find(".status").text("正在上傳");

$("#"+file.id).find('.percent').html(percent);

})

batchWebUpload.on( 'all', function( type ,file) {

updateTotalProgress();



switch( type ) {

case 'uploadComplete':

parentId = file.id;

percentages[ file.id ][ 1 ] = 1;

break;

case 'uploadFinished':

break;



case 'uploadProgress':

break;

case 'uploadStart':

break;



case 'stopUpload':

break;



}

})

})



/**左下角彈出框**/

function showWindow(){

if(index!=0){

try{

layer.restore(index);

}catch(err){

//console.log("彈出層已經打開");

return index;

}

return index;



}



//iframe窗

index=layer.open({

type: 1,

title:  ['上傳隊列', 'font-size:14px;'],

closeBtn: 0, //不顯示關閉按鈕

shade: false,

area: ['700px', '400px'],

offset: 'rb', //右下角彈出

anim: 2,

maxmin :true,

content: $("#fileListDom"), //iframe的url,no表明不顯示滾動條

});

return index;

}





function startAll(file){

batchWebUpload.upload(file);

}



function stopUpload(file){

if(file==undefined){

batchWebUpload.stop();

}else{

batchWebUpload.stop(file);

}

}



function cancelFile(file){

batchWebUpload.cancelFile(file);

}





/**設置總百分比**/

function updateTotalProgress() {





var fize = (fileAllSize/1024/1024.0).toFixed(2);

var sSize = (successSize/1024/1024.0).toFixed(2);



$("#fileQueueNum").text(fileAllNum)

$("#fileQueueSize").text(fize);

var loaded = 0;

var total = 0;



var percent = 0;;



$.each( percentages, function( k, v ) {

total += v[ 0 ];

loaded += v[ 0 ] * v[ 1 ];

} );

percent = total ? loaded / total : 0;

$("#successNum").text(successNum);

$("#successSize").text(sSize);

var show_pe= Math.round( percent * 100 ) + '%';

element.progress('sumProgress', show_pe);

$("#sumPercent").text(show_pe);

}

</script>

</head>

<body>



<a class="layui-btn layui-btn-danger layui-btn-sm" id="batchWebUpload">

<i class="layui-icon">&#xe857;</i>劃重點,多選,分片批量上傳+斷點續傳

</a>



</body>



<div id="fileListDom" class="upload-table" style="display: none">

<a class="layui-btn layui-btn-danger layui-btn-sm" id="startAll" onclick="startAll()">

所有開始

</a>

<a class="layui-btn layui-btn-danger layui-btn-sm" id="stopAll" onclick="stopUpload()">

所有暫停

</a>

<table class="layui-table" lay-size="sm">

<colgroup>

<col width="200">

<col width="150">

<col width="150">

<col width="350">

<col width="150">

<col width="200">

<col width="450">

<col>

</colgroup>

<thead>

<tr>

<th>文件名</th>

<th>文件類型</th>

<th>文件大小</th>

<th>進度</th>

<th>百分比</th>

<th>狀態</th>

<th>操做</th>

</tr>

</thead>

<tbody id="fileList" class="fileQueue">



</tbody>

<tfoot id="fileInfo">

  <tr>

  <td colspan="4">

  <span>選中<span id="fileQueueNum">0</span>個文件,共<span id="fileQueueSize">0</span>M。</span><br>

  <span>上傳成功<span id="successNum">0</span>個文件,共<span id="successSize">0</span>M。</span>

  </td>

  <td >

  <span id="sumPercent"></span>

 

  </td>

  <td colspan="2">

  <div class="layui-progress" lay-showPercent="yes" lay-filter="sumProgress">

<div class="layui-progress-bar" lay-percent="0%" id="sumProgress_bar" ></div>

</div>

  </td>

  </tr>

</tfoot>

</table>



</div>

</html>


後端代碼

相關文章
相關標籤/搜索