webuploader與django進行斷點續傳
- 需要實現的效果如下

-
需要使用的 js
- jquery.js
- webuploader.hs
- hashmap.js
-
路由
from django.urls import path from . import views urlpatterns = [ path('index/',views.index), path('checkChunk/',views.checkChunk,name='checkChunk'), path('mergeChunks/',views.mergeChunks,name='mergeChunks'), path('upload/',views.upload,name='upload'), ] -
視圖
from django.shortcuts import render from django.http import JsonResponse import os def index(request): return render(request,'upload.html') # 檢查上傳分片是否重復,如果重復則不提交,否則提交 def checkChunk(request): # post請求 if request.method == 'POST': # 獲得上傳文件塊的大小,如果為0,就告訴他不要上傳了 chunkSize = request.POST.get("chunkSize") if chunkSize == '0': return JsonResponse({'ifExist': True}) # 如果文件塊大小不為0 ,那么就上傳,需要拼接一個臨時文件 file_name = request.POST.get('fileMd5')+request.POST.get('chunk') # 如果說這個文件不在已經上傳的目錄,就可以上傳,已經存在了就不需要上傳。 if file_name not in get_deep_data(): return JsonResponse({'ifExist': False}) return JsonResponse({'ifExist': True}) # 判斷一個文件是否在一個目錄下 def get_deep_data(path='static/upload/'): result = [] data = os.listdir(path) for i in data: if os.path.isdir(i): get_deep_data(i) else: result.append(i) return result # 前端上傳的分片 保存到 指定的目錄下 def upload(request): if request.method == 'POST': md5 = request.POST.get("fileMd5") chunk_id = request.POST.get("chunk","0") fileName = "%s-%s"%(md5,chunk_id) file = request.FILES.get("file") with open('static/upload/'+fileName,'wb') as f: for i in file.chunks(): f.write(i) return JsonResponse({'upload_part':True}) # 將每次上傳的分片合並成一個新文件 def mergeChunks(request): if request.method == 'POST': # 獲取需要給文件命名的名稱 fileName = request.POST.get("fileName") # 該圖片上傳使用的md5碼值 md5 = request.POST.get("fileMd5") id = request.POST.get("fileId") # 分片的序號 chunk = 0 # 完成的文件的地址為 path = os.path.join('static','upload',fileName) with open(path,'wb') as fp: while True: try: filename = 'static/upload/{}-{}'.format(md5,chunk) with open(filename,'rb') as f: fp.write(f.read()) # 當圖片寫入完成后,分片就沒有意義了,刪除 os.remove(filename) except: break chunk += 1 return JsonResponse({'upload':True,'fileName':fileName,'fileId':id}) -
前端
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>webuploader上傳</title> <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/webuploader/0.1.1/webuploader.css"> <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"> <script src="/static/jquery-2.2.3.js"></script> <script src="/static/webuploader.min.js"></script> <script type="text/javascript" src="/static/hashmap.js"></script> <style type="text/css"> #picker { display: inline-block; line-height: 1.428571429; vertical-align: middle; margin: 0 12px 0 0; } </style> </head> <body> <div id="uploader" class="container"> <!--用來存放文件信息--> <div id="thelist" class="row"> <div class="panel panel-primary"> <div class="panel-heading">webuploader文件上傳</div> <table class="table table-striped table-bordered" id="uploadTable"> <thead> <tr> <th>序號</th> <th>文件名稱</th> <th>文件大小</th> <th>上傳狀態</th> <th>上傳進度</th> <th style="width:15%;">操作</th> </tr> </thead> <tbody></tbody> </table> <div class="panel-footer"> <div id="picker">選擇文件</div> <button id="btn" class="btn btn-default">開始上傳</button> </div> </div> </div> </div> <script type="text/javascript"> var fileMd5; var fileSuffix; var $list=$("#thelist table>tbody"); var state = 'pending';//初始按鈕狀態 var $btn=$("#btn"); var count=0; var map=new HashMap(); //監聽分塊上傳過程中的三個時間點 WebUploader.Uploader.register({ "before-send-file" : "beforeSendFile", "before-send" : "beforeSend", "after-send-file" : "afterSendFile", }, { //時間點1:所有分塊進行上傳之前調用此函數 beforeSendFile : function(file) { var deferred = WebUploader.Deferred(); //1、計算文件的唯一標記,用於斷點續傳 // (new WebUploader.Uploader()).md5File(file, 0, 10 * 1024 * 1024) (new WebUploader.Uploader()).md5File(file, 0, 1024) .progress(function(percentage) { $('#' + file.id).find("td.state").text("正在讀取文件信息..."); }).then(function(val) { fileMd5 = val; $('#' + file.id).find("td.state").text("成功獲取文件信息..."); //獲取文件信息后進入下一步 deferred.resolve(); }); return deferred.promise(); }, //時間點2:如果有分塊上傳,則每個分塊上傳之前調用此函數 beforeSend : function(block) { var deferred = WebUploader.Deferred(); $.ajax({ type : "POST", url : "{% url 'checkChunk'%}", data : { //文件唯一標記 fileMd5 : fileMd5, //當前分塊下標 chunk : block.chunk, //當前分塊大小 chunkSize : block.end - block.start }, dataType : "json", success : function(response) { if (response.ifExist) { //分塊存在,跳過 deferred.reject(); } else { //分塊不存在或不完整,重新發送該分塊內容 deferred.resolve(); } } }); this.owner.options.formData.fileMd5 = fileMd5; deferred.resolve(); return deferred.promise(); }, //時間點3:所有分塊上傳成功后調用此函數 afterSendFile : function(file) { //如果分塊上傳成功,則通知后台合並分塊 $.ajax({ type : "POST", url : "{% url 'mergeChunks'%}", data : { fileId : file.id, fileMd5 : fileMd5, fileSuffix:fileSuffix, fileName:file.name, }, success : function(response) { console.log(response.fileName+" 上傳成功") $('#del'+file.id).hide(); } }); } }); var uploader = WebUploader .create({ // swf文件路徑 swf : 'https://cdnjs.cloudflare.com/ajax/libs/webuploader/0.1.1/Uploader.swf', // 文件接收服務端。 server : "{% url 'upload' %}", // 選擇文件的按鈕。可選。 // 內部根據當前運行是創建,可能是input元素,也可能是flash. pick : { id : '#picker',//這個id是你要點擊上傳文件的id multiple : true }, // 不壓縮image, 默認如果是jpeg,文件上傳前會壓縮一把再上傳! resize : true, auto : false, //開啟分片上傳 chunked : true, chunkSize : 10 * 1024 * 1024, accept : { extensions : "txt,jpg,jpeg,bmp,png,zip,rar,war,pdf,cebx,doc,docx,ppt,pptx,xls,xlsx,iso,flv,mp4", mimeTypes : '.txt,.jpg,.jpeg,.bmp,.png,.zip,.rar,.war,.pdf,.cebx,.doc,.docx,.ppt,.pptx,.xls,.xlsx,.iso,.flv,.mp4' } }); // 當有文件被添加進隊列的時候 uploader.on('fileQueued', function(file) { //保存文件擴展名 fileSuffix=file.ext; fileName=file.source['name']; var fileSize=file.size; var fileSizeStr=""; fileSizeStr=WebUploader.Base.formatSize(fileSize); count++; $list.append( '<tr id="' + file.id + '" class="item" flag=0>'+ '<td class="index">' + count + '</td>'+ '<td class="info">' + file.name + '</td>'+ '<td class="size">' + fileSizeStr + '</td>'+ '<td class="state">等待上傳...</td>'+ '<td class="percentage"></td>'+ '<td class="operate"><button name="upload" id="del'+file.id+'" class="btn btn-warning">開始</button><button name="delete" class="btn btn-error">刪除</button></td></tr>'); map.put(file.id+"",file); }); // 文件上傳過程中創建進度條實時顯示。 uploader.on('uploadProgress', function(file, percentage) { $('#' + file.id).find('td.percentage').text( '上傳中 ' + Math.round(percentage * 100) + '%'); }); uploader.on('uploadSuccess', function(file) { $('#' + file.id).find('td.state').text('已上傳'); }); uploader.on('uploadError', function(file) { $('#' + file.id).find('td.state').text('上傳出錯'); }); uploader.on('uploadComplete', function(file) { uploader.removeFile(file); }); uploader.on('all', function(type) { if (type === 'startUpload') { state = 'uploading'; } else if (type === 'stopUpload') { state = 'paused'; } else if (type === 'uploadFinished') { state = 'done'; } if (state === 'uploading') { $btn.text('暫停上傳'); } else { $btn.text('開始上傳'); } }); $btn.on('click', function(){ if (state === 'uploading'){ uploader.stop(true); } else { uploader.upload(); } }); $("body").on("click","#uploadTable button[name='upload']",function(){ flag=$(this).parents("tr.item").attr("flag")^1; $(this).parents("tr.item").attr("flag",flag); var id=$(this).parents("tr.item").attr("id"); if(flag==1){ $(this).text("暫停"); uploader.upload(uploader.getFile(id,true)); }else{ $(this).text("開始"); uploader.stop(uploader.getFile(id,true)); }); $("body").on("click","#uploadTable button[name='delete']",function(){ var id=$(this).parents("tr.item").attr("id"); $(this).parents("tr.item").remove(); uploader.removeFile(uploader.getFile(id,true)); map.remove(id); }); </script> </body> </html>
