java后端文件分片上传,断点续传
程序员文章站
2024-02-19 11:01:46
...
java后端文件分片上传,断点续传
原由
最近公司在做一个大文件上传的需求,由于使用的框架有限制,所以最大文件不能超过100M,所以如果使用之前的方式直接将整个文件直接上传的话,会直接报错,所以就想到能不能把大文件进行分片,然后逐个文件上传到后端,文件上传完后,后台在进行文件的合并,然后传到指定的服务器中。
解决方法
使用大文件的分片断点续传,也就是前端将一个大文件切割,分成多个小文件,然后依次发送,后台收到请求的时候,将每一个请求发送过来的文件内容生成一个小文件,然后存放在临时目录中,当前端上传完文件后,后端再合并生成的小文件,然后上传到指定服务(ftp或oss等文件存储服务器上)。
实现
1、写一个controller接口(只贴方法)
/**
*
* @param request name 文件名, md5 大文件的md5, current 当前分片数,
* total 总的分片数 , file 是小文件的数据,这边是前端传过来的blob类型字段
* @return
* @throws IOException
*/
@RequestMapping(value = "/uploadFile", method = {RequestMethod.POST})
public R uploadFile(HttpServletRequest request) throws IOException {
R r = new R();
String fileName = request.getParameter("name");
String md5 = request.getParameter("md5");
String basePath = request.getSession().getServletContext().getRealPath("/") + "temp" + File.separatorChar + md5;
File file= new File(basePath);
if(!file.exists()) {
file.mkdirs();
}
System.out.println("文件目录为:"+ basePath);
File[] files = file.listFiles();
Integer current = Integer.parseInt(request.getParameter("current"));
// 总分片数
Integer total = Integer.parseInt(request.getParameter("total"));
int combineFlag = 0;
// 判断文件是否已经存在相关切片,如果存在跳过已有的切片,开始新的切片上传
if(files.length >= current) {
// 文件中切片的总数量和前端传过来切片的数据相同,则直接合并文件
if(file.length() >= total) {
String fileUrl = null;
try {
// 合并文件
fileUrl = combineFiles(fileName, basePath);
if(StringUtils.isNotBlank(fileUrl)) {
r.put("filePath", fileUrl);
combineFlag = 1;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(combineFlag == 0) {
// 合并失败
r.put("errMessage", "合并文件失败");
r.put("code", 500);
} else {
r.put("code", 0);
}
r.put("next", current + 1);
}
} else {
r.put("code", 0);
r.put("next", files.length + 1);
}
} else {
// 原始文件的大小
Long size = Long.parseLong(request.getParameter("size"));
// 当前文件的路径
String fileNamePath = basePath + File.separatorChar+ current;
OutputStream out = new FileOutputStream(fileNamePath);
// 获取文件的相关数据,然后写入到文件中
MultipartFile data = ((MultipartHttpServletRequest) request).getFile("data");
byte[] bytes = data.getBytes();
out.write(bytes);
out.flush();
out.close();
// 上传最后一张后,对图片进行合并
if(current.equals(total)) {
// 合并操作
try {
String fileUrl = combineFiles(fileName, basePath);
if(StringUtils.isNotBlank(fileUrl)) {
combineFlag = 1;
r.put("filePath", fileUrl);
}
} catch (Exception e) {
e.printStackTrace();
logger.error(e.toString());
} finally {
if(combineFlag == 0) {
// 合并失败
r.put("errMessage", "合并文件失败");
r.put("code", 500);
} else {
r.put("code", 0);
}
r.put("next", current + 1);
}
} else {
r.put("code", 0);
r.put("next", current + 1);
}
}
return r;
}
private String combineFiles(String fileName, String basePath) throws Exception {
int returnFlag = FileUtil.CombineFile(fileName, basePath);
String fileUrl = null;
if(returnFlag == 1) {
// 将文件上传到oss或者是ftp,然后返回一个文件路径
// TODO 当前的代码需要你去补充,就是上传到某个文件服务气的逻辑
fileUrl = ""
// 将本地的文件删除
File deleteFile = new File(basePath);
FileUtil.delTempChild(deleteFile);
}
return fileUrl;
}
文件工具类 fileUtil
public class FileUtil {
/**
* 合并文件,
* @param tar 合成目标的文件名称
* @param baseFilePath 你要合并哪个文件夹的文件,里面必须是要合并的文件
* @return
* @throws Exception
*/
public static int CombineFile(String tar, String baseFilePath) throws Exception {
File dirFile = new File(baseFilePath);
FileInputStream fis;
// 一次读取2M数据,将读取到的数据保存到byte字节数组中
byte buffer[] = new byte[1024 * 1024 * 2];
int len;
// 判断file是否为目录
if(dirFile.isDirectory()) {
String[] fileNames = dirFile.list();
FileOutputStream fos = new FileOutputStream(baseFilePath + File.separatorChar + tar);
// 实现目录自定义排序
Arrays.sort(fileNames, new StringComparator());
for (int i = 0;i<fileNames.length ;i++){
fis = new FileInputStream(baseFilePath + File.separatorChar + fileNames[i]);
len = 0;
while ((len = fis.read(buffer)) != -1) {
// buffer从指定字节数组写入。buffer:数据中的起始偏移量,len:写入的字数。
fos.write(buffer, 0, len);
}
fos.flush();
fis.close();
}
fos.close();
}
return 1;
}
/**
* 删除某个文件或者是某个目录
* @param file 文件路径或者是文件夹路径
*/
public static void delTempChild(File file){
if (file.isDirectory()) {
// 获取文件夹下所有子文件夹
String[] children = file.list();
//递归删除目录中的子目录下
for (int i=0; i<children.length; i++) {
delTempChild(new File(file, children[i]));
}
}
// 目录空了,进行删除
file.delete();
}
}
比较类
import java.util.Comparator;
/**
* @author yanming.gu
* @ClassName: StringComparator
* @Description
* @since 1.0
*/
public class StringComparator implements Comparator<String> {
@Override
public int compare(String s1, String s2) {
if (returnDouble(s1) < returnDouble(s2)){
return -1;
} else if (returnDouble(s1) > returnDouble(s2)) {
return 1;
} else {
return 0;
}
}
public static double returnDouble(String str){
StringBuffer sb = new StringBuffer();
for(int i=0;i<str.length();i++){
if(Character.isDigit(str.charAt(i))) {
sb.append(str.charAt(i));
} else if(str.charAt(i)=='.'&&i<str.length()-1&&Character.isDigit(str.charAt(i+1))) {
sb.append(str.charAt(i));
} else {
break;
}
}
if(sb.toString().isEmpty()){
return 0;
} else {
return Double.parseDouble(sb.toString());
}
}
}
上面就是实现断点续传的代码,如果有什么不足之处或者觉得缺少了上面,可以直接评论哦,然后我会准时补上,或者给你们解决问题。
这是我掘金账号,欢迎大家先来浏览。
掘金
上一篇: php文件的读取方式