大文件分片上传
项目源码 包含前后端
分片上传来源:github完整项目demo (前端webUploader+后端接口 )
完整地址:https://github.com/zyt1272999061/webuploader_demo
分片上传接口交互说明
-
分片上传 前端通过webUploader组件 将文件分片,
-
后端接口接收分片文件,保存到临时路径下uuid做为文件夹保证唯一性,同一个文件每次分片的 uuid都是一样。
接口参数如下: -
前端分片都上传完毕,调用后端合并分片接口,
-
后端找到临时文件路径下对应的uuid的文件夹进行读取合并到真实的路径下进行文件的合并保存文件。
接口参数如下:
核心代码
上传分片
/**
* 上传分片
*
* @param file
* @param chunk
* @param guid
* @throws IOException
*/
@PostMapping("save")
@ResponseBody
public void upload(@RequestParam MultipartFile file, Integer chunk, String guid) throws IOException {
String filePath = uploadPath + File.separator + "temp" + File.separator + guid;
File tempfile = new File(filePath);
if (!tempfile.exists()) {
tempfile.mkdirs();
}
RandomAccessFile raFile = null;
BufferedInputStream inputStream = null;
if (chunk == null) {
chunk = 0;
}
try {
File dirFile = new File(filePath, String.valueOf(chunk));
//以读写的方式打开目标文件
raFile = new RandomAccessFile(dirFile, "rw");
raFile.seek(raFile.length());
inputStream = new BufferedInputStream(file.getInputStream());
byte[] buf = new byte[1024];
int length = 0;
while ((length = inputStream.read(buf)) != -1) {
raFile.write(buf, 0, length);
}
} catch (Exception e) {
throw new IOException(e.getMessage());
} finally {
if (inputStream != null) {
inputStream.close();
}
if (raFile != null) {
raFile.close();
}
}
}
合并文件
/**
* 合并文件
*
* @param guid 全局唯一id
* @param fileName 文件名包含文件类型 test.xlsx
*/
@PostMapping("combine111")
@ResponseBody
public void combineBlock(String guid, String fileName) {
//分片文件临时目录
File tempPath = new File(uploadPath + File.separator + "temp" + File.separator + guid);
//真实上传路径
File realPath = new File(uploadPath + File.separator + "real");
if (!realPath.exists()) {
realPath.mkdirs();
}
File realFile = new File(uploadPath + File.separator + "real" + File.separator + fileName);
FileOutputStream os = null;
// 文件追加写入
FileChannel fcin = null;
FileChannel fcout = null;
try {
log.info("合并文件——开始 [ 文件名称:" + fileName + " ,MD5值:" + guid + " ]");
os = new FileOutputStream(realFile, true);
fcout = os.getChannel();
if (tempPath.exists()) {
//获取临时目录下的所有文件
File[] tempFiles = tempPath.listFiles();
//按名称排序
Arrays.sort(tempFiles, (o1, o2) -> {
if (Integer.parseInt(o1.getName()) < Integer.parseInt(o2.getName())) {
return -1;
}
if (Integer.parseInt(o1.getName()) == Integer.parseInt(o2.getName())) {
return 0;
}
return 1;
});
//每次读取10MB大小,字节读取
//byte[] byt = new byte[10 * 1024 * 1024];
//int len;
//设置缓冲区为10MB
ByteBuffer buffer = ByteBuffer.allocate(10 * 1024 * 1024);
for (int i = 0; i < tempFiles.length; i++) {
FileInputStream fis = new FileInputStream(tempFiles[i]);
/*while ((len = fis.read(byt)) != -1) {
os.write(byt, 0, len);
}*/
fcin = fis.getChannel();
if (fcin.read(buffer) != -1) {
buffer.flip();
while (buffer.hasRemaining()) {
fcout.write(buffer);
}
}
buffer.clear();
fis.close();
//删除分片
tempFiles[i].delete();
}
os.close();
//删除临时目录
if (tempPath.isDirectory() && tempPath.exists()) {
System.gc(); // 回收资源
tempPath.delete();
}
log.info("文件合并——结束 [ 文件名称:" + fileName + " ,MD5值:" + guid + " ]");
}
} catch (Exception e) {
log.error("文件合并——失败 " + e.getMessage(), e);
}
}