关键部分
前端用file.slice()分块前端用FileReader获取每一分块的md5值后端用MultipartFile接受分块文件后端用FileOutputStream拼装分块文件
话不多说,直接上代码,我想这是你们最喜欢的
html
<!DOCTYPE HTML>
<html>
<head>
<meta charset=
"utf-8">
<title>HTML5大文件分片上传示例</title>
<script src=
"http://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
<script src=
"md5.js"></script>
<script>
var i = -
1;
var succeed =
0;
var databgein; //开始时间
var dataend; //结束时间
var action=false; //false检验分片是否上传过(默认); true上传文件
var page = {
init: function(){
$(
"#upload").click(function(){
databgein=new Date();
var file = $(
"#file")[
0].files[
0]; //文件对象
isUpload(file);
});
}
};
$(function(){
page.init();
});
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 md5 = hex_md5(bolb);
form.append(
"md5", md5);
//Ajax提交
$.ajax({
url:
"http://localhost:8080//bookQr/test/isUpload",
type:
"POST",
data: form,
async: true, //异步
processData: false, //很重要,告诉jquery不要对form进行处理
contentType: false, //很重要,指定为false才能形成正确的Content-Type
success: function(data){
var dataObj = eval(
"("+data+
")");
var uuid = dataObj.fileId;
var date = dataObj.date;
if (dataObj.flag ==
"1") {
//没有上传过文件
upload(file,uuid,md5,date);
}
else if(dataObj.flag ==
"2") {
//已经上传部分
upload(file,uuid,md5,date);
}
else if(dataObj.flag ==
"3") {
//文件已经上传过
alert(
"文件已经上传过,秒传了!!");
$(
"#uuid").append(uuid);
}
},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 =
5 *
1024 *
1024, //以
5MB为一个分片
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;
$(
"#param").append(
"index=="+ssindex+
"<br/>");
//按大小切割文件段
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);
//Ajax提交
$.ajax({
url:
"http://localhost:8080//bookQr/test/upload",
type:
"POST",
data: form,
async: true, //异步
processData: false, //很重要,告诉jquery不要对form进行处理
contentType: false, //很重要,指定为false才能形成正确的Content-Type
success: function(data){
var dataObj = eval(
"("+data+
")");
var fileuuid = dataObj.fileId;
var flag = dataObj.flag;
//服务器返回该分片是否上传过
if(!action){
if (flag ==
"1") {
//未上传
action = true;
}
else if(dataObj.flag ==
"2") {
//已上传
action = false;
++succeed;
}
//递归调用
upload(file,uuid,filemd5,date);
}
else{
//服务器返回分片是否上传成功
//改变界面
++succeed;
$(
"#output").text(succeed +
" / " + shardCount);
if (i+
1 == shardCount) {
dataend=new Date();
$(
"#uuid").append(fileuuid);
$(
"#usetime").append(dataend.getTime()-databgein.getTime());
}
else {
//已上传成功,然后检测下一个分片
action = false;
//递归调用
upload(file,uuid,filemd5,date);
}
}
},error: function(XMLHttpRequest, textStatus, errorThrown) {
alert(
"服务器出错!");
}
});
})
}
</script>
</head>
<body>
<input type=
"file" id=
"file" />
<button id=
"upload">上传</button>
<span id=
"output" style=
"font-size:12px">等待</span>
<span id=
"usetime" style=
"font-size:12px;margin-left:20px;">用时</span>
<span id=
"uuid" style=
"font-size:12px;margin-left:20px;">uuid==</span>
<br/>
<br/>
<br/>
<br/>
<span id=
"param" style=
"font-size:12px;margin-left:20px;">param==</span>
</body>
</html>
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
controller
package com.yxtech.sys.controller;
import com.yxtech.common.Constant;
import com.yxtech.sys.domain.FileRes;
import com.yxtech.sys.service.FileResService;
import com.yxtech.sys.service.ResService;
import com.yxtech.utils.file.FileMd5Util;
import com.yxtech.utils.file.NameUtil;
import com.yxtech.utils.file.PathUtil;
import jodd.datetime.JDateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
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 tk.mybatis.mapper.entity.Example;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* Created by Administrator on
2015/
10/
9.
*/
@RestController
@Scope("prototype")
@RequestMapping(value = "/test")
public
class testController {
@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");
String size = 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 savePath = Constant.FILE_PATH + File.separator + date;
String saveDirectory = PathUtil.getAppRootPath(request) + savePath + File.separator + 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) {
System.out.println(
"check length =" + md5.length() +
" md5Str length" + md5Str.length() +
" " + md5 +
" " + md5Str);
md5Str =
"0" + md5Str;
}
if (md5Str != null && md5Str.equals(md5)) {
//分片已上传过
map = new HashMap<>();
map.put(
"flag",
"2");
map.put(
"fileId", uuid);
map.put(
"status", true);
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)){
//分片上传过程中出错,有残余时需删除分块后,重新上传
if (file.exists()) {
file.delete();
}
multipartFile.transferTo(new File(saveDirectory, uuid +
"_" + index));
}
if (path.isDirectory()) {
File[] fileArray = path.listFiles();
if (fileArray != null) {
if (fileArray.length == total) {
//分块全部上传完毕,合并
String suffix = NameUtil.getExtensionName(fileName);
File newFile = new File(PathUtil.getAppRootPath(request) + savePath, 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) {
System.out.println(
"-----" + len);
outputStream.write(byt,
0, len);
}
}
//关闭流
temp.close();
outputStream.close();
//修改FileRes记录为上传成功
Example example = new Example(FileRes.
class);
Example.Criteria criteria = example.createCriteria();
criteria.andEqualTo(
"md5",fileMd5);
FileRes fileRes = new FileRes();
fileRes.setStatus(Constant.ONE);
fileResService.updateByExampleSelective(fileRes,example);
}
else if(index ==
1){
//文件第一个分片上传时记录到数据库
FileRes fileRes = new 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(savePath + File.separator + uuid +
"." + fileRes.getSuffix());
fileRes.setSize(Integer.parseInt(size));
fileRes.setMd5(fileMd5);
fileRes.setStatus(Constant.ZERO);
fileRes.setCreateTime(new Date());
this.fileResService.insert(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) throws Exception {
String md5 = request.getParameter(
"md5");
Example example = new Example(FileRes.
class);
Example.Criteria criteria = example.createCriteria();
criteria.andEqualTo(
"md5", md5);
List<FileRes> list = fileResService.selectByExample(example);
Map<String, Object> map = null;
if (list == null || list.size() ==
0) {
//没有上传过文件
String uuid = UUID.randomUUID().toString();
map = new HashMap<>();
map.put(
"flag",
"1");
map.put(
"fileId", uuid);
map.put(
"date", new JDateTime().toString(
"YYYYMMDD"));
map.put(
"status", true);
}
else {
FileRes fileRes = list.get(
0);
//求文件上传日期
SimpleDateFormat sdf=new SimpleDateFormat(
"YYYYMMDD");
Date date=new Date();
String strDate=sdf.format(date);
if(fileRes.getStatus()==
0){
//文件上传部分
map = new HashMap<>();
map.put(
"flag",
"2");
map.put(
"fileId", fileRes.getUuid());
map.put(
"date",strDate);
map.put(
"status", true);
}
else if(fileRes.getStatus()==
1){
//文件上传成功
map = new HashMap<>();
map.put(
"flag",
"3");
map.put(
"fileId", fileRes.getUuid());
map.put(
"date",strDate);
map.put(
"status", true);
}
}
return map;
}
}
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
FileMd5Util
package com.yxtech.utils.file;
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);
}
public static void main(String[] args) {
//
for(int i=
1;i<
79;i++){
// File file = new File(
"F:\\bookQr_SYS\\target\\bookQr\\files\\20170122\\2124b3f9-a4c8-4297-a182-6249010dcd72\\2124b3f9-a4c8-4297-a182-6249010dcd72_"+i);
File file = new File(
"F:\\bookQr_SYS\\target\\bookQr\\files\\20170122\\2124b3f9-a4c8-4297-a182-6249010dcd72.mp4");
String md5Str = getFileMD5(file);
System.out.println(
""+md5Str.length()+
" "+md5Str);
// }
}
}
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
NameUtil
/**
* Created by Administrator on 2015/10/12.
*/
public class NameUtil {
/**
* Java文件操作 获取文件扩展名
*/
public static String
getExtensionName(String filename) {
if ((filename !=
null) && (filename.length() >
0)) {
int dot = filename.lastIndexOf(
'.');
if ((dot > -
1) && (dot < (filename.length() -
1))) {
return filename.substring(dot +
1);
}
}
return filename.toLowerCase();
}
/**
* Java文件操作 获取不带扩展名的文件名
*/
public static String
getFileNameNoEx(String filename) {
if ((filename !=
null) && (filename.length() >
0)) {
int dot = filename.lastIndexOf(
'.');
if ((dot > -
1) && (dot < (filename.length()))) {
return filename.substring(
0, dot);
}
}
return filename.toLowerCase();
}
public static void main(String[] args) {
String str =
"AAAbb.jpg";
System.out.println(getExtensionName(str).toLowerCase());
System.out.println(getFileNameNoEx(str).toUpperCase());
}
}
1234567891011121314151617181920212223242526272829303132333435363738
1234567891011121314151617181920212223242526272829303132333435363738
PathUtil
import com.yxtech.sys.controller.MailController;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* Created by Administrator on 2015/10/12.
*/
public class PathUtil {
/**
*
* @param request
* @return 返回结果类似于 “F:\workSpace\bookQr\src\main\webapp\”
*/
public static String
getAppRootPath(HttpServletRequest request){
return request.getSession().getServletContext().getRealPath(
"/");
}
/**
*自定义文件保存路径
* @param request
*/
public static String
getCustomRootPath(HttpServletRequest request){
String path =
"";
Properties prop =
new Properties();
InputStream in = MailController.class.getResourceAsStream(
"/config/jdbc.properties");
try {
prop.load(in);
path = prop.getProperty(
"FILE_PATH").trim();
}
catch (IOException e) {
e.printStackTrace();
}
return path;
}
/**
*
* @param request
* @return http://www.qh.com:8080/projectName
*/
public static String
getHttpURL(HttpServletRequest request) {
StringBuffer buff =
new StringBuffer();
buff.append(
"http://");
buff.append(request.getServerName());
buff.append(
":");
buff.append(request.getServerPort());
buff.append(request.getContextPath());
return buff.toString();
}
}