分片上传需要跟前端配合
1,前端代码
<dody>
<input type="file" name="upload" id="file"/>
<button id="slice">上传</button>
</dody>
<script>
$("#slice").change(function(event) {
var file = $("#slice")[0].files[0];
PostFile(file,0);
});
$("#slice").on('click',function() {
console.log('点击');
var file = $("#file")[0].files[0];
PostFile(file, 0);
});
//执行分片上传
function PostFile(file,i, uuid){
var name = file.name, //文件名
size = file.size, //总大小shardSize = 2 * 1024 * 1024,
shardSize = 10 * 1024 * 1024, //以2MB为一个分片,每个分片的大小
shardCount = Math.ceil(size / shardSize); //总片数
if(i >= shardCount){
return;
}
//判断uuid是否存在
if (uuid == undefined || uuid == null) {
uuid = guid();
}
//console.log(size,i+1,shardSize); //文件总大小,第一次,分片大小//
var start = i * shardSize;
var end = start + shardSize;
var packet = file.slice(start, end); //将文件进行切片
/* 构建form表单进行提交 */
var form = new FormData();
form.append("uuid", uuid);// 前端生成uuid作为标识符传个后台每个文件都是一个uuid防止文件串了
form.append("data", packet); //slice方法用于切出文件的一部分
form.append("name", name);
form.append("totalSize", size);
form.append("total", shardCount); //总片数
form.append("index", i + 1); //当前是第几片
form.append("moduleName", 'test'); //模块名
form.append("recordId", '111'); //记录id
form.append("fileType", 'aaa'); //文件类型
$.ajax({
url: "http://localhost:9888/inspactData/file/doPost",
type: "POST",
data: form,
//timeout:"10000", //超时10秒
async: true, //异步
dataType:"json",
processData: false, //很重要,告诉jquery不要对form进行处理
contentType: false, //很重要,指定为false才能形成正确的Content-Type
success: function (msg) {
console.log(msg);
/* 表示上一块文件上传成功,继续下一次 */
if (msg.data.status == 201) {
form = '';
i++;
PostFile(file, i, uuid);
} else if (msg.data.status == 502) {
form = '';
/* 失败后,每2秒继续传一次分片文件 */
setInterval(function () { PostFile(file, i, uuid) }, 2000);
} else if (msg.data.status == 200) {
merge(uuid, name)
console.log("上传成功");
} else if (msg.data.status == 500) {
console.log('第'+msg.i+'次,上传文件有误!');
} else {
console.log('未知错误');
}
}
})
}
function merge(uuid, fileName) {
$.ajax({
url: "http://localhost:9888/inspactData/file/merge",
type: "GET",
data: {uuid: uuid, newFileName: fileName,moduleName:'test',recordId:'111',fileType:'aaa'},
//timeout:"10000", //超时10秒
async: true, //异步
dataType:"json",
success: function (msg) {
console.log(msg);
}
})
}
function guid() {
return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0,
v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
2,controller
/**
* 分片
* @param: req
* @return: resp
*/
@RequestMapping("/doPost")
@ResponseBody
public Map fragmentation(HttpServletRequest req, HttpServletResponse resp,@RequestParam(required = false) String moduleName,@RequestParam(required = false) String recordId) {
resp.addHeader("Access-Control-Allow-Origin", "*");
return fileUploadBiz.fragmentation(req,resp,moduleName,recordId);
}
/**
* 合并
* @param: uuid
* @param: newFileName
* @param: moduleName
* @param: recordId
* @param: fileType
* @param: resp
* @return Map
*/
@RequestMapping(value = "/merge", method = RequestMethod.GET)
@ResponseBody
public Map merge(String uuid, String newFileName,@RequestParam(required = false) String moduleName,@RequestParam(required = false) String recordId ,HttpServletResponse resp) {
resp.addHeader("Access-Control-Allow-Origin", "*");
return fileUploadBiz.merge(uuid,newFileName,moduleName,recordId);
}
3,service
service两行代码此处省略
4,impl
private final String[] arrSuffix = {"jpg", "jpeg", "jpe", "bmp", "gif", "png", "svg", "rar", "zip", "7z", "pdm", "doc", "xls", "ppt", "tif", "txt", "chm", "docx", "xlsx", "pptx", "pdf", "vsd", "mpp", "jpg", "jpeg", "jpe", "bmp", "gif", "png", "avi", "asx", "asf", "mpg", "wmv", "3gp", "mp3", "mp4", "mov", "flv", "wmv9", "rm", "rmvb", "et", "wps", "dps", "pmp", "bdc", "ceb", "wmf", "ofd", "ttf", "woff", "woff2", "ttc", "fon", "font", "otf", "eot"};
@Value("${file.path}")
private String filePath;
/**
* @param req
* @param resp
* @return
*/
@Override
public Map fragmentation(HttpServletRequest req, HttpServletResponse resp,String moduleName,String recordId) {
Map map = new HashMap<>();
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) req;
// 获得文件分片数据
MultipartFile file = multipartRequest.getFile("data");
// 分片第几片
int index = Integer.parseInt(multipartRequest.getParameter("index"));
// 总片数
int total = Integer.parseInt(multipartRequest.getParameter("total"));
// 获取文件名
String fileName = multipartRequest.getParameter("name");
String name = fileName.substring(0, fileName.lastIndexOf("."));
String fileEnd = fileName.substring(fileName.lastIndexOf(".")+1);//文件后缀
//判断上传文件类型是否是指定类型
boolean isContains = Arrays.asList(arrSuffix).contains(fileEnd);
if (isContains == false) {
throw new InspactDataException(InspactDataResultEnum.FILE_TYPE_EXCEEDED);
}
//临时文件存放地址,D:/home/file/attachment/tempDir/uuid/,uuid/文件名/当前分片数/.tem
File uploadFile = new File(filePath +"/tempDir/"+(moduleName!=null?moduleName+ "/":"") + (recordId!=null?recordId+ "/":"") + (fileEnd!=null?fileEnd+ "/":"")+ "/" , name + "("+index + ").tem");
//判断文件父目录是否存在
if (!uploadFile.getParentFile().exists()) {
uploadFile.getParentFile().mkdirs();//这里因为创建的是多级目录,所以需要使用mkdirs()方法。使用mkdir()方法则文件夹创建不成功,会报找不到路径错误。
}
if (index < total) {//当前分片数小于总分片数
try {
file.transferTo(uploadFile);
// 上传的文件分片名称
map.put("status", 201);
return map;
} catch (IOException e) {
e.printStackTrace();
map.put("status", 502);
return map;
}
} else {
try {
file.transferTo(uploadFile);
// 上传的文件分片名称
map.put("status", 200);
return map;
} catch (IOException e) {
e.printStackTrace();
map.put("status", 502);
return map;
}
}
}
/**
* @param uuid
* @param fileName 文件名
* @param moduleName 模块名
* @param recordId 记录id
* @param fileType 文件类型
* @return
*/
@Override
public Map merge(String uuid, String fileName,String moduleName,String recordId) {
Map retMap = new HashMap();
try {
//获取后缀
String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
//临时目录地址 D:/home/file/attachment/tempDir/test/111/aaa/
String tempPath =filePath +"tempDir/"+(moduleName!=null?moduleName+ "/":"") + (recordId!=null?recordId+ "/":"") + (suffix!=null?suffix+ "/":"");
//主目录地址 D:/home/file/attachment/test/111/aaa/
String mainPath =filePath +(moduleName!=null?moduleName+ "/":"") + (recordId!=null?recordId+ "/":"") + (suffix!=null?suffix+ "/":"");
File dirFile = new File(tempPath);
//判断临时目录是否存在
if (!dirFile.exists()) {
throw new RuntimeException("文件不存在!");
}
//分片上传的文件已经位于同一个文件夹下 ,使用map排序 确保合并时文件的完整性
String[] arrFileName = dirFile.list();
Map map = new HashMap();
//存储排序后的文件名
List list = new ArrayList();
for (int i = 0; i <= arrFileName.length -1 ; i++) {
Integer index = Integer.valueOf(arrFileName[i].substring(arrFileName[i].indexOf("(") + 1, arrFileName[i].indexOf(")")));
map.put(index,arrFileName[i]);
}
Set<Integer> keySet = map.keySet();
Iterator<Integer> iter = keySet.iterator();
while (iter.hasNext()) {
Integer key = iter.next();
list.add(map.get(key));
}
//判断主目录是否存在 不存在则创建 RandomAccessFile无法创建多级目录 D:/home/file/attachment/test/111/aaa/
File file = new File(mainPath);
if (!file.exists()) {
file.mkdirs();//这里因为创建的是多级目录,所以需要使用mkdirs()方法。使用mkdir()方法则文件夹创建不成功,会报找不到路径错误。
}
//文件上传名字重复后缀+1 获得+1后的文件名
String newFileName = checkFileName(filePath + (moduleName!=null?moduleName+ "/":"") + (recordId!=null?recordId+ "/":"") + (suffix!=null?suffix+ "/":"") , fileName);
// 拼接后的文件上传地址
fileName = newFileName;
// 创建空的合并文件 D:/home/file/attachment/tempDir/test/111/aaa/simhei_itmop.com.zip
File targetFile = new File(mainPath, fileName);
//将合并的文件写到主目录D:/home/file/attachment/tempDir/test/111/aaa/simhei_itmop.com.zip
RandomAccessFile writeFile = new RandomAccessFile(targetFile, "rw");
int position = 0;
//遍历同一个文件夹下的所有文件
for (Object fileNames : list) {
//源临时文件目录 D:/home/file/attachment/tempDir/test/111/aaa/
File sourceFile = new File(tempPath , String.valueOf(fileNames));
//循环读取临时文件
RandomAccessFile readFile = new RandomAccessFile(sourceFile, "rw");
int chunksize = 1024 * 3;
byte[] buf = new byte[chunksize];
writeFile.seek(position);
int byteCount = 0;
while ((byteCount = readFile.read(buf)) != -1) {
if (byteCount != chunksize) {
byte[] tempBytes = new byte[byteCount];
System.arraycopy(buf, 0, tempBytes, 0, byteCount);
buf = tempBytes;
}
writeFile.write(buf);
position = position + byteCount;
}
readFile.close();
String pathTemp = sourceFile.getPath();
File deleteFile=new File(pathTemp);
deleteFile.delete();//删除缓存的临时文件
}
writeFile.close();
//返回前端的url地址 需要拼接IP端口
String fileUrl="/"+(moduleName!=null?moduleName+ "/":"") + (recordId!=null?recordId+ "/":"") + (suffix!=null?suffix+ "/":"")+fileName;
retMap.put("url", fileUrl);
} catch (IOException e) {
e.printStackTrace();
retMap.put("code", "500");
}
return retMap;
}