java大檔案分塊上傳分享
先說說大檔案上傳種用的點以及原理,也希望各位指正。
思路以及大部分原始碼來自於 haohao123naa博主,我只是在他的基礎上進行完善點選開啟連結,在此表示感謝。
1.檔案分塊:一個超過幾個G的檔案上傳到伺服器,如果你只使用最簡單上傳、接收、處理、還能成功的話,我只能說你家伺服器真好。就算伺服器夠好,這種操作也時不被允許的。所以我們要想辦法解決這種困難。
首先我們要解決大檔案的問題,沒有辦法只能切成幾M的 byte小檔案多次傳送至伺服器,並儲存下來。然後給這些檔案用原始檔的MD5+index進行命名,當然也有朋友用UUID+index進行命名,這兩種的區別會在下文詳細講述。當你把這些小檔案分別上傳到伺服器上時,最好把這些記錄儲存到資料庫。
(1)當第一個分塊上傳完成時,將原始檔的名稱、型別、原始檔的MD5、上傳日期、地址、未完成的狀態寫入到一張表,等拼接完成改狀態為完成。暫時命名為file表
(2)每一個分塊上傳完成後記錄儲存到資料庫裡面,原始檔的MD5+index名字、分塊的MD5(這是一個重點)、上傳時間、檔案地址。儲存進資料庫 命名為file__tem表
2.秒傳功能:很多網盤都實現了這個功能,上傳開始時傳送Ajax請求,查詢本次要上傳的檔案,是否存在,這裡我們H5提供了獲取檔案MD5的方法,然後用ajax去file裡請求這個md5是否存在、狀態是否時完成,存在的情況下,也要驗證本地這個檔案是不是還存在這。同時存在的情況下。就可以給前臺返回存在狀態了,然後你就可以傲嬌的告訴客戶,秒傳了。
3.斷點續傳:這裡就為什麼我們為什麼不用簡單的UUID進行命名,而是使用MD5的原因了,大檔案上傳失敗是很常見的一種情況,現在分成一塊塊的小檔案了,失敗了我們就從失敗的那個小檔案開始繼續上傳不就可以了。
(1)Ajax 請求file 表、存在記錄、狀態未完成。(狀態完成就是秒傳了)
(2)檔案分塊 Ajax用MD5請求file_temp表是否存在,存在遞迴下一塊的MD5請求,
(3)第二步不存在的情況下、這裡再用form提交MultipartFile以及相關資訊,上傳成功後,寫入資料庫。
4.所有分塊完成拼接到一起,改file表狀態。
5.原始碼部分(本人程式碼寫的不是很好,希望各位指正)
(HTML)
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title></title>
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="./plugins/layui/css/layui.css" media="all">
<style>
.form-wrap {
padding: 10px;
}
</style>
</head>
<body>
<!-- 新增使用者表單 -->
<form id="videofile_form" class="layui-form" style="display: none"
method="post" enctype="multipart/form-data">
<div class="layui-form-item" style="display: none">
<label class="layui-form-label">檔案上傳</label>
<!-- name="expertfile" -->
<div class="layui-input-block">
<input type="text" id="parentId" name="parentId" >
<input type="text" id="videoId" name="videoId" >
<input type="text" id="videoSize" name="videoSize" > <!-- //大小 -->
<input type="text" id="timeLength" name="timeLength" > <!-- //時長 -->
<input type="text" id="videoPath" name="videoPath" > <!-- //路徑-->
<input type="text" id="type" name="type" > <!-- //型別 -->
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">視訊上傳</label>
<input type="file" id="file" />
</div>
<div class="layui-form-item">
<label class="layui-form-label">視訊名稱</label>
<div class="layui-input-block">
<input type="text" name="videoName" lay-verify="title" placeholder="請輸入" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">教師名稱</label>
<div class="layui-input-block">
<input type="text" name="teacherName" lay-verify="title"
placeholder="請輸入" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">圖書簡介</label>
<div class="layui-input-block">
<textarea name="introduction" id="introduction_textarea"></textarea>
</div>
</div>
<div class="layui-form-item" style="display: none">
<div class="layui-input-block">
<button type=" " class="layui-btn layui-btn-primary"
id="deleteclean">重置</button>
</div>
</div>
</form>
<div class="layui-progress layui-progress-big" lay-showpercent="true"
lay-filter="demo" id="progress" style="display: none">
<div class="layui-progress-bar layui-bg-red" lay-percent="0%"></div>
</div>
<div class="layui-form-item" id="expert_submit" style="display: block">
<div class="layui-input-block">
<div class="site-demo-button"
style="margin-top: 20px; margin-bottom: 0;">
<!-- data-type="loading" -->
<!-- <button id="upload">上傳</button> -->
<button class="layui-btn site-demo-active" onclick="window.active.loading();">提交</button>
<button type=" " class="layui-btn layui-btn-primary" id="cleanbu"
onclick="clean.deleteclean();">重置</button>
</div>
</div>
</div>
<script src="./plugins/layui/layui.js"></script>
<script src="../publicjs/jquery-1.8.0.min.js"></script>
<script src="../publicjs/hxwmutil.js"></script>
<script src="../publicjs/md5.js"></script>
<script type="text/javascript">
var i = -1;
var succeed = 0;
var databgein; //開始時間
var dataend; //結束時間
var paid;
var id ;
var Percent=0;
var action=false; //false檢驗分片是否上傳過(預設); true上傳檔案
$(function() {
var moudle = commonUtil.getRequestParam("moudle");
$('#videofile_form').show();
// 展示當前的表單
switch (moudle) {
case "expert":
formObj.expertForm.operType = "insert";
}
});
layui.use([ 'form','upload','tree', 'layer', 'element', 'table','layedit' ],function() {
var form = layui.form,
layer = layui.layer,
table = layui.table,
layedit = layui.layedit,
element = layui.element;
var $ = layui.jquery,upload = layui.upload;
id = commonUtil.getRequestParam("id"); //節點Id
paid = commonUtil.getRequestParam("paid"); //課程ID
$("#parentId").val(id)
var introductionEditor = layedit.build('introduction_textarea');
//觸發事件
window.active = {
setPercent: function(){
//設定50%進度
element.progress('demo', '0%')
},
loading : function() {
var othis = $(this), type = $(this).data('type');
active[type] ? active[type].call(this,othis) : '';
$("#progress").attr('style','disply:block');
var DISABLED = 'layui-btn-disabled';
if (othis.hasClass(DISABLED))
return;
databgein=new Date();
var file = $("#file")[0].files[0]; //檔案物件
isUpload(file);
//模擬loading
active.setPercent();
var n = 0, timer = setInterval(function() {
// 請求ajax成功之後 將課程列表放到課程下拉框裡
if (Percent>=100 ) {
Percent= 100;
clearInterval(timer);
othis.removeClass(DISABLED);
}
element.progress('demo',Percent+"%");
// n = n + Math.random()*10|0;
}, 300 + Math.random() * 1000);
othis.addClass(DISABLED);
}
}
});
var clean = {
deleteclean : function() {
$("#deleteclean").click()
}
}
function isUpload (file) {
//構造一個表單,FormData是HTML5新增的
var form = new FormData();
var r = new FileReader();
r.readAsBinaryString(file);
$(r).load(function(e){
var bolb = e.target.result;
var strmd5 = hex_md5(bolb);
form.append("filemd5", strmd5);
//Ajax提交
$.ajax({
url: "bigfile/isUpload",
type: "POST",
data: form,
async: true, //非同步
processData: false, //很重要,告訴jquery不要對form進行處理
contentType: false, //很重要,指定為false才能形成正確的Content-Type
success: function(data){
var uuid = data.fileId;
if (data.flag == "1") {
//沒有上傳過檔案
var a = upload(file,uuid,strmd5,data.date);
} else if(data.flag == "2") {
//已經上傳部分
upload(file,uuid,strmd5,data.date);
}else if(data.flag == "3") {
//檔案已經上傳過
Percent=99;
update(data);
}
},error: function(XMLHttpRequest, textStatus, errorThrown) {
alert("伺服器出錯!");
}
})
})
};
/*
* file 檔案物件
* uuid 後端生成的uuid
* filemd5 整個檔案的md5
* date 檔案第一個分片上傳的日期(如:20170122)
*/
function upload (file,uuid,filemd5,date) {
name = file.name; //檔名
size = file.size; //總大小
var shardSize = 512 * 1024, //以1MB為一個分片
shardCount = Math.ceil(size / shardSize); //總片數
if (i > shardCount) {
i = -1;
i = shardCount;
} else {
if(!action){
i += 1; //只有在檢測分片時,i才去加1; 上傳檔案時無需加1
}
}
//計算每一片的起始與結束位置
var start = i * shardSize,
end = Math.min(size, start + shardSize);
//構造一個表單,FormData是HTML5新增的
var form = new FormData();
if(!action){
form.append("action", "check"); //檢測分片是否上傳
$("#param").append("action==check ");
}else{
form.append("action", "upload"); //直接上傳分片
form.append("data", file.slice(start,end)); //slice方法用於切出檔案的一部分
$("#param").append("action==upload ");
}
form.append("uuid", uuid);
form.append("filemd5", filemd5);
form.append("date", date);
form.append("name", name);
form.append("size", size);
form.append("total", shardCount); //總片數
form.append("index", i+1); //當前是第幾片
var ssindex = i+1;
//按大小切割檔案段
var data = file.slice(start, end);
var r = new FileReader();
r.readAsBinaryString(data);
$(r).load(function(e){
var bolb = e.target.result;
var md5 = hex_md5(bolb);
form.append("md5", md5);
//var formData = new FormData(form);
//Ajax提交
$.ajax({
url: "bigfile/upload",
type: "POST",
data: form,
async: false, //非同步
processData: false, //很重要,告訴jquery不要對form進行處理
contentType: false, //很重要,指定為false才能形成正確的Content-Type
success: function(data){
var fileuuid = data.fileId;
var flag = data.flag;
//伺服器返回該分片是否上傳過
if(!action){
if (flag == "1") {
//未上傳
action = true;
} else if(flag == "3") {
//已上傳
action = false;
++succeed;
}
upload(file,uuid,filemd5,date);
//遞迴呼叫
}else{
if(flag == "4") {
alert("上傳失敗,請檢查網路");
return false;
}else if(flag == "5") {
$('#videoPath').val(data.path);
$('#timeLength').val(data.timelength);
$('#videoSize').val(data.size);
$('#type').val(data.type);
update(data);
}
//伺服器返回分片是否上傳成功
//改變介面
++succeed;
$("#output").text(succeed + " / " + shardCount);
var a = GetPercent(succeed,shardCount);
if(!a){
alert("抱歉,請上傳正確視訊");
return false;
}
if (i+1 == shardCount) {
dataend=new Date();
} else {
//已上傳成功,然後檢測下一個分片
action= false;
//遞迴呼叫
upload(file,uuid,filemd5,date);
}
}
},error: function(XMLHttpRequest, textStatus, errorThrown) {
alert("抱歉,請重試");
}
});
})
}
function update(data){
var parap = {
videoCourseId : paid
}
var userForm = document.getElementById("videofile_form");
var formData = new FormData(userForm);
var url ="";
if($("#parentId").val()!=""){
url = "video/update";
}else{
layer.alert("請選擇節點");
return;
}
var ajaxParam = {
url : url,
async : false
}
ajaxUtil.submitForm(formData,ajaxParam,function(data) {
if (data.code = 200) {
// 關閉當前的表單彈窗 重新整理父級頁面的資料表格
var parentHtml = window.parent;
parentHtml.layer.closeAll();
parentHtml.refresh(parap);
}else if(res.code=='400'){
layer.alert(res.msg);
}
})
}
function GetPercent(num, total) {
num = parseFloat(num);
total = parseFloat(total);
if (isNaN(num) || isNaN(total)) {
return false;
}
Percent = (Math.round(num / total * 10000) / 100.00);
return true;
}
</script>
</body>
</html>
( controller部分)
package com.folkestone.hxwm.controller.base;
import com.folkestone.hxwm.service.commom.ResService;
import com.folkestone.hxwm.service.commom.FileResService;
import com.folkestone.hxwm.bean.bean_dto.common.FileRes;
import com.folkestone.hxwm.common.util.bigfileup.DataUntil;
import com.folkestone.hxwm.common.util.bigfileup.FileMd5Util;
import com.folkestone.hxwm.common.util.bigfileup.FileOpera;
import com.folkestone.hxwm.common.util.bigfileup.NameUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
/**
* Created by Administrator on 2015/10/9.
*/
@RestController
@RequestMapping(value ="/admin/bigfile")
public class BFileUController {
@Autowired
private FileResService fileResService;
@Autowired
private ResService resService;
/**
* 上傳檔案
*
* @param request
* @return
* @throws IllegalStateException
* @throws IOException
*/
@RequestMapping(value = "/upload")
public Map<String, Object> upload(
HttpServletRequest request, @RequestParam(value = "data",required = false)
MultipartFile multipartFile) throws IllegalStateException, IOException, Exception {
String action = request.getParameter("action");
String uuid = request.getParameter("uuid");
String fileName = request.getParameter("name");
int size = Integer.valueOf(request.getParameter("size"));//總大小
int total = Integer.valueOf(request.getParameter("total"));//總片數
int index = Integer.valueOf(request.getParameter("index"));//當前是第幾片
String fileMd5 = request.getParameter("filemd5"); //整個檔案的md5
String date = request.getParameter("date"); //檔案第一個分片上傳的日期(如:20170122)
String md5 = request.getParameter("md5"); //分片的md5
//資料夾路徑
String saveDirectory= FileOpera.CreatePath(date,uuid);
//分片檔案
File path = new File(saveDirectory);
if (!path.exists()) {
path.mkdirs();
}
File file = new File(saveDirectory, uuid + "_" + index);
//根據action不同執行不同操作. check:校驗分片是否上傳過; upload:直接上傳分片
Map<String, Object> map = null;
if("check".equals(action)){
String md5Str = FileMd5Util.getFileMD5(file);
//已經上傳部分檔案 斷點續傳
if (md5Str != null && md5Str.length() == 31) {
//查詢臨時表 當前片段MD5是否存在
List<FileRes> li= resService.select(md5,file.getPath());
//檢視當前片段是否上傳
map = new HashMap<>();
map=FileOpera.FileExit(li);
//返回執行下一段
return map;
}else {
//重新上傳一次
map = new HashMap<>();
map.put("flag", "1");
map.put("fileId", uuid);
map.put("status", true);
return map;
}
}else if("upload".equals(action)){
map = new HashMap<>();
int a = -1;
//未上傳
//刪除檔案後上傳
if (file.exists()) {
file.delete();
}
multipartFile.transferTo(new File(saveDirectory, uuid + "_" + index));
//新增進資料庫
FileRes fileRes = new FileRes();
fileRes.setUuid(uuid);
fileRes.setPath(file.getPath());
fileRes.setSize(size);
fileRes.setMd5(md5);
fileRes.setStatus(1);
fileRes.setCreateTime(DataUntil.Tidata());
a=resService.insert(fileRes);
if(a<0) {//片段失敗重新上傳
map = new HashMap<>();
map.put("flag", "4");
map.put("fileId", uuid);
map.put("status", false);
return map;
}
}
if (path.isDirectory()) {
File[] fileArray = path.listFiles();
if (fileArray != null) {
if (fileArray.length == total) {
int success=-1;
//分塊全部上傳完畢,合併
String suffix = NameUtil.getExtensionName(fileName);
File newFile = new File(FileOpera.Createfolder(date), uuid + "." + suffix);
FileOutputStream outputStream = new FileOutputStream(newFile, true);//檔案追加寫入
byte[] byt = new byte[10 * 1024 * 1024];
int len;
FileInputStream temp = null;//分片檔案
for (int i = 0; i < total; i++) {
int j = i + 1;
temp = new FileInputStream(new File(saveDirectory, uuid + "_" + j));
while ((len = temp.read(byt)) != -1) {
outputStream.write(byt, 0, len);
}
}
//關閉流
temp.close();
outputStream.close();
//修改FileRes記錄為上傳成功
FileRes fileRes = new FileRes();
fileRes.setStatus(1);
fileRes.setMd5(fileMd5);
fileRes.setPath(newFile.getPath());
fileRes.setSize((int) newFile.length());
fileRes.setSuffix(NameUtil.getExtensionName(fileName));
success =fileResService.update(fileRes);
map=new HashMap<>();
map.put("fileId", uuid);
map.put("flag", "5");
if(success>0) {
map.put("size",fileRes.getSize());
map.put("timelength",fileRes.getTimeLength());
map.put("type",fileRes.getSuffix());
map.put("status", true);
map.put("path",fileRes.getPath());
}else {
map.put("status", false);
}
return map;
}else if(index == 1){
//檔案第一個分片上傳時記錄到資料庫
FileRes fileRes = new FileRes();
fileRes.setMd5(fileMd5);
List<FileRes> list = fileResService.selectByMd(fileRes);
String name = NameUtil.getFileNameNoEx(fileName);
if (name.length() > 50) {
name = name.substring(0, 50);
}
fileRes.setName(name);
fileRes.setSuffix(NameUtil.getExtensionName(fileName));
fileRes.setUuid(uuid);
fileRes.setPath(FileOpera.Createfolder(date) + File.separator + uuid + "." + fileRes.getSuffix());
fileRes.setSize(size);
fileRes.setStatus(0);
fileRes.setCreateTime(DataUntil.Tidata());
if (list == null || list.size() == 0) {
this.fileResService.insert(fileRes);
}else {
this.fileResService.update(fileRes);
}
}
}
}
map = new HashMap<>();
map.put("flag", "3");
map.put("fileId", uuid);
map.put("status", true);
return map;
}
/**
* 上傳檔案前校驗
*
* @param request
* @return
* @throws IOException
*/
@RequestMapping(value = "/isUpload")
public Map<String, Object> isUpload(HttpServletRequest request,MultipartFile multipartFile) throws Exception {
String md5 = request.getParameter("md5");
//檢視本檔案是否存在
FileRes fi = new FileRes();
fi.setMd5(md5);
fi.setStatus(1);
List<FileRes> list = fileResService.selectByMd(fi);
return FileOpera.FileExit(list);
}
}
工具類部分:package com.folkestone.hxwm.common.util.bigfileup; import java.io.File; import java.io.FileInputStream; import java.math.BigInteger; import java.security.MessageDigest; /** * @author cuihao * @create 2017-01-20-15:13 */ public class FileMd5Util { public static final String KEY_MD5 = "MD5"; public static final String CHARSET_ISO88591 = "ISO-8859-1"; /** * Get MD5 of one file:hex string,test OK! * * @param file * @return */ public static String getFileMD5(File file) { if (!file.exists() || !file.isFile()) { return null; } MessageDigest digest = null; FileInputStream in = null; byte buffer[] = new byte[1024]; int len; try { digest = MessageDigest.getInstance("MD5"); in = new FileInputStream(file); while ((len = in.read(buffer, 0, 1024)) != -1) { digest.update(buffer, 0, len); } in.close(); } catch (Exception e) { e.printStackTrace(); return null; } BigInteger bigInt = new BigInteger(1, digest.digest()); return bigInt.toString(16); } /*** * Get MD5 of one file!test ok! * * @param filepath * @return */ public static String getFileMD5(String filepath) { File file = new File(filepath); return getFileMD5(file); } /** * MD5 encrypt,test ok * * @param data * @return byte[] * @throws Exception */ public static byte[] encryptMD5(byte[] data) throws Exception { MessageDigest md5 = MessageDigest.getInstance(KEY_MD5); md5.update(data); return md5.digest(); } public static byte[] encryptMD5(String data) throws Exception { return encryptMD5(data.getBytes(CHARSET_ISO88591)); } /*** * compare two file by Md5 * * @param file1 * @param file2 * @return */ public static boolean isSameMd5(File file1,File file2){ String md5_1= FileMd5Util.getFileMD5(file1); String md5_2= FileMd5Util.getFileMD5(file2); return md5_1.equals(md5_2); } /*** * compare two file by Md5 * * @param filepath1 * @param filepath2 * @return */ public static boolean isSameMd5(String filepath1,String filepath2){ File file1=new File(filepath1); File file2=new File(filepath2); return isSameMd5(file1, file2); } }
package com.folkestone.hxwm.common.util.bigfileup;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.springframework.web.multipart.MultipartFile;
import com.folkestone.hxwm.bean.bean_dto.common.FileRes;
import com.folkestone.hxwm.common.util.ContantFinalUtil;
public class FileOpera {
/**
* 根據MD5檢視檔案是否已經上傳
* 檔案片段或者檔案均可
*
* */
public static Map<String, Object> FileExit(List<FileRes> li) {
Map<String, Object> map = null;
if (li == null || li.size() == 0) {
//沒有上傳過檔案
String uuid = UUID.randomUUID().toString();
map = new HashMap<>();
map.put("flag", "1");
map.put("fileId", uuid);
map.put("date", DataUntil.StrDta());
map.put("status", true);
} else {
FileRes fileRes = li.get(0);
//判斷檔案是否刪除 本地檔案是否存在
File file=new File(fileRes.getPath());
if(file.exists()) {
if(fileRes.getStatus()==0){
//檔案上傳部分
map = new HashMap<>();
map.put("flag", "2");
map.put("fileId", fileRes.getUuid());
map.put("date",DataUntil.StrDta());
map.put("status", true);
}else if(fileRes.getStatus()==1){
//檔案上傳成功
map = new HashMap<>();
map.put("flag", "3");
map.put("path", fileRes.getPath());
map.put("fileId", fileRes.getUuid());
map.put("date",DataUntil.StrDta());
map.put("status", true);
map.put("size",fileRes.getSize());
map.put("timelength",fileRes.getTimeLength());
map.put("type",fileRes.getSuffix());
}
}else {
//重新上傳
//刪除表
String uuid = UUID.randomUUID().toString();
map = new HashMap<>();
map.put("flag", "1");
map.put("fileId", uuid);
map.put("date", DataUntil.StrDta());
map.put("status", true);
}
}
return map;
}
/**
* 建立檔案路徑
* @param string
* @param date
*
* */
public static String CreatePath(String date, String uuid) {
//生成上傳檔案的路徑資訊,按天生成
String saveDirectory = ContantFinalUtil.BASE_PATH+
ContantFinalUtil.VIDEO_PATH + File.separator + date + File.separator + uuid;
//驗證路徑是否存在,不存在則建立目錄
File path = new File(saveDirectory);
if (!path.exists()) {
path.mkdirs();
}
return saveDirectory;
}
/**
* 建立資料夾路徑
* @param string
* @param date
*
* */
public static String Createfolder(String date) {
//生成上傳檔案的路徑資訊,按天生成
String saveDirectory = ContantFinalUtil.BASE_PATH+
ContantFinalUtil.VIDEO_PATH + File.separator + date;
//驗證路徑是否存在,不存在則建立目錄
File path = new File(saveDirectory);
if (!path.exists()) {
path.mkdirs();
}
return saveDirectory;
}
public static Map<String, Object> upload(MultipartFile multipartFile, String saveDirectory, String Pname) {
Map<String, Object> map = null;
try {
multipartFile.transferTo(new File(saveDirectory,Pname));
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//寫入 比較與前臺傳過來的MD5是否一致
return map;
}
公司前端框架是layui,所以改成了這個樣子,大家使用的時候自己修改吧。
下面說說這裡的幾個要點以及與完善原博主的地方:
(前端部分)
1、md5.js ,你只需要下載一個 MD5.js即可,(md5.js和jQuery.md5.js是部分相同的兩種東西,我當時以為功能會相同,然後前臺報錯了,特此記錄一下)
2、添加了一個進度條,直接用 當前成功分塊/總分塊,求出的parcent,layui每500ms輪詢一次請求這個值,自動生成的進度條。一個G,5M一塊, 一共241塊,所以這個進度條看著也可以,不知道大家有沒有更好的進度條方式哪?請指點。謝謝。
(後端部分)
1、我這裡添加了資料操作,原博主是個大神不屑這些小地方。我補全了,留個記錄方便自己。
2、添加了斷點續傳的功能,理由同上。
3、抽取了一個FileOpera為了斷點上傳的方便。
4、在資料庫種找到記錄之後,有添加了一下驗證本地。因為我們這邊總會有不嚴謹的刪除,很神奇。
最後說說需要改進的地方,以後有時間進行改進:
1、後臺接受form提交,我直接使用的 getParamerter,應該使用bean自動的。
2、新增一個執行緒,可以一邊上傳,一邊自動拼接,最後一次分塊上傳就會快很多。
3、加一個分塊檔案七天刪除的功能,每上傳一次檔案,伺服器就會存了兩份檔案。你要是想用斷點續傳,就不能立刻刪除這些分塊檔案。但是時間長了,就會對伺服器造成壓力。所以應該定時刪除。
第一次寫部落格,希望大家支援一下,哪裡不足的也希望各位指點一下。謝謝。