欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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());
        }

    }
}


上面就是实现断点续传的代码,如果有什么不足之处或者觉得缺少了上面,可以直接评论哦,然后我会准时补上,或者给你们解决问题。

这是我掘金账号,欢迎大家先来浏览。
掘金

相关标签: 后台 java