最近在做视频上传展示的相关业务!但是因为最开始使用的是单文件上传所以一旦遇到大文件上传的速度就非常慢!为此在网上一直找寻分片的方法!得到了思路!
直接讲一下我这边看了那么多文档加上自己理解写的demo(虽然前端大部分代码是网上的)!
在看demo之前需要理解这个分片的思路。
1、假设现在有一个很大的石头搬到某个地方你直接搬的话那么就是走的非常慢还有可能因为外部因素搬到一般摔了那就做了无用功了。
那么现在把石头切割成多个小块你搬小部分石头是不是就很快,然后当全部小石头搬完后在目的地把这些小石头拼接起来是不是还是原来的样
子(别抬杠)。
2、那么分片的原理就是这样文件太大切割成多个小文件在后台接收这些分片然后创建临时文件。在所有分片传完之后调用后台合并接口,将
刚才的分片合并成完整的文件。
直接上前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JS分片上传-极速上传</title>
</head>
<body>
<input type="file" name="slice" id="slice" >
<br/>
</body>
<script src="http://libs.baidu.com/jquery/1.8.3/jquery.min.js"></script>
<script type="text/javascript">
$("#slice").change(function(event) {
var file = $("#slice")[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); //当前是第几片
$.ajax({
url: "http://127.0.0.1:8080/index/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.status == 201) {
form = '';
i++;
PostFile(file, i, uuid);
} else if (msg.status == 502) {
form = '';
/* 失败后,每2秒继续传一次分片文件 */
setInterval(function () { PostFile(file, i, uuid) }, 2000);
} else if (msg.status == 200) {
merge(uuid, name)
console.log("上传成功");
} else if (msg.status == 500) {
console.log('第'+msg.i+'次,上传文件有误!');
} else {
console.log('未知错误');
}
}
})
}
function merge(uuid, fileName) {
$.ajax({
url: "http://127.0.0.1:8080/index/merge",
type: "GET",
data: {uuid: uuid, newFileName: fileName},
//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);
});
}
</script>
</html>
后台代码:
private static String fileUploadTempDir = "D:/portalupload/fileuploaddir";
private static String fileUploadDir = "D:/portalupload/file";
@RequestMapping("/doPost")
@ResponseBody
public Map fragmentation(HttpServletRequest req, HttpServletResponse resp) {
resp.addHeader("Access-Control-Allow-Origin", "*");
Map<String, Object> 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("."));
// 前端uuid,作为标识
String uuid = multipartRequest.getParameter("uuid");
File uploadFile = new File(fileUploadTempDir + "/" + uuid, uuid + name + index + ".tem");
if (!uploadFile.getParentFile().exists()) {
uploadFile.getParentFile().mkdirs();
}
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;
}
}
}
@RequestMapping(value = "/merge", method = RequestMethod.GET)
@ResponseBody
public Map merge(String uuid, String newFileName) {
Map retMap = new HashMap();
try {
File dirFile = new File(fileUploadTempDir + "/" + uuid);
if (!dirFile.exists()) {
throw new RuntimeException("文件不存在!");
}
//分片上传的文件已经位于同一个文件夹下,方便寻找和遍历(当文件数大于十的时候记得排序用冒泡排序确保顺序是正确的)
String[] fileNames = dirFile.list();
// 创建空的合并文件
File targetFile = new File(fileUploadDir, newFileName);
RandomAccessFile writeFile = new RandomAccessFile(targetFile, "rw");
int position = 0;
for (String fileName : fileNames) {
System.out.println(fileName);
File sourceFile = new File(fileUploadTempDir + "/" + uuid, fileName);
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();
FileUtils.deleteQuietly(sourceFile);//删除缓存的临时文件
}
writeFile.close();
retMap.put("code", "200");
}catch (IOException e){
e.printStackTrace();
retMap.put("code", "500");
}
return retMap;
}
大概就是这样的思路。
如有更好的方法请告诉小弟,小弟也在学习中。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。