Vue分片上传于后端接收分片文件

前言

        做毕设时需要将大文件进行上传到后端进行保存,结合多方面的资料搜集完成了这个功能,但是还是没有做到文件分片上传时统计总上传进度。

前端部分

        实现需要得到一个文件对象,这里不作出叙述了。先上总代码,因为很多部分涉及到了我的具体业务,很多都是多余的,后面慢慢解释重要的片段。

uploadFile(file){
                const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB  
                const chunkSize = 10 * 1024 * 1024; // 每片10MB
                // 创建一个FileReader实例
                const reader = new FileReader();  
                reader.onload = (e) => {
                    const arrayBuffer = e.target.result;  
                    const spark = new SparkMD5.ArrayBuffer();  
                    spark.append(arrayBuffer);  
                    const md5Hash = spark.end();
                    var uploadItem={
                        file_name:file.name,
                        percentage:0,
                        state:0
                    }
                    this.uploadListState.push(uploadItem)        
                    if(md5Hash){
                        uploadItem.state=1
                    }
                    const param1 = this.$route.query.path;
                    let file_pid='root'
                    if(param1!=null){
                        file_pid=param1
                    }
                    apis.uploadPreview({
                        user_id:this.UserInfo.user_id,
                        file_pid:file_pid,
                        file_md5:md5Hash
                    })
                    .then(res=>{
                        if(res.data.code==0){
                            if (file.size > MAX_FILE_SIZE) {
                                // 文件大小超过10MB,执行分片上传  
                                let file_name=file.name
                                let file_type=file.type
                                const totalChunks = Math.ceil(file.size / chunkSize);
                                for (let i = 0; i < totalChunks; i++) {
                                    const start = i * chunkSize;
                                    const end = Math.min(start + chunkSize, file.size);
                                    const chunk = file.slice(start, end);
                                    this.chunkUpLoad(chunk,i,totalChunks,file_name,uploadItem,file_type,md5Hash)
                                }
                            } else {
                                this.minFileUpLoad(file,uploadItem,md5Hash)
                            }
                        }else{
                            uploadItem.percentage=100
                            uploadItem.state=3
                            this.$message({
                                message: '上传成功',
                                type: 'success'
                            })
                            console.info('已经完成上传')
                        }
                    })
                    .catch(error=>{
                        console.info(error)
                    })
                };
                // 读取文件为ArrayBuffer  
                reader.readAsArrayBuffer(file);
            },

当选取到的文件大小超过了限制进入分片逻辑时。

let file_name=file.name
let file_type=file.type
const totalChunks = Math.ceil(file.size / chunkSize);
for (let i = 0; i < totalChunks; i++) {
    const start = i * chunkSize;
    const end = Math.min(start + chunkSize, file.size);
    const chunk = file.slice(start, end);
    this.chunkUpLoad(chunk,i,totalChunks,file_name,uploadItem,file_type,md5Hash)
}

在这里将文件分成chunkSize个字节大小,并且通过chunkUpLoad将分片后的文件chunk上传到后端。

后端部分

后端在控制层用MultipartFile 类型将该文件接收,在fileService类中进行具体处理。

    @PostMapping("/chunkUpLoad")
    public ResponseResult chunkUpLoad(@RequestParam("file") MultipartFile file,
                                      @RequestParam String user_id,
                                      @RequestParam String file_pid,
                                      @RequestParam String file_type,
                                      @RequestParam Integer index,
                                      @RequestParam Integer chunkNum,
                                      @RequestParam String file_name,
                                      @RequestParam String file_md5,
                                      @RequestParam Integer file_category){
        String Extension=UUIDUtils.getFileExtension(file_name);
        if ("exe".equals(Extension)||"bat".equals(Extension)){
            ResponseResult result=new ResponseResult();
            result.setCode(-1);
            result.setErrorMessage("该文件类型不允许上传");
        }
        return fileService.chunkUpLoad(file,user_id,file_pid,file_type,index,chunkNum,file_name,file_md5,file_category);
    }

在后端的处理逻辑为这样的,即用户将带有总分片和该文件的分片号传来后,将该MultipartFile文件作为字节文件存储在一个临时文件夹并将存储的文件路径存储在chunkStoreList列表中,当最后一片文件成功完成后,检查之前的文件是否上传成功,没有成功则循环等待1秒,反之则将已经上传的分片文件拼接得到一个临时文件并获取该文件的InputStream,在我的业务中需要用InputStream存入minio中。

代码部分:

public ResponseResult chunkUpLoad(MultipartFile file,
                                      String user_id,
                                      String file_pid,
                                      String file_type,
                                      Integer index,
                                      Integer chunkNum,
                                      String file_name,
                                      String file_md5,
                                      Integer file_category) {
        ResponseResult result=new ResponseResult();
        java.io.File dest = new java.io.File(diskAddress+"\\"+file_name+"_"+index+"_"+chunkNum);
        // 确保目标文件夹存在
        java.io.File parentDir = dest.getParentFile();
        if (!parentDir.exists()) {
            try {
                Files.createDirectories(Paths.get(parentDir.toURI()));
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
        try {
            file.transferTo(dest);
            System.out.println("File has been saved to " + dest.getAbsolutePath());
        } catch (IOException e) {
            e.printStackTrace();
            result.setCode(1002);
            result.setErrorMessage(e.getMessage());
            //读取文件
            return result;
        }
        List<String> chunkStoreList=new ArrayList<>();
        //这是最后一片了
        if (index+1==chunkNum){
            //读取该文件的全部分片文件
            for (int i=0;i<chunkNum;i++){
                chunkStoreList.add(diskAddress+"\\"+file_name+"_"+i+"_"+chunkNum);
            }
            for (String s:chunkStoreList){
                while (!isFileExist(s)){
                    try {
                        System.out.println(s+",未完成传输,等待1.5s");
                        Thread.sleep(1500);
                    }catch (Exception e){

                    }
                }
            }
            // 创建一个临时文件来存储合并后的内容
            try {
                Path tempFile = Files.createTempFile("merged-", null);
                try (FileOutputStream fos = new FileOutputStream(tempFile.toFile().getAbsolutePath());
                     BufferedOutputStream bos = new BufferedOutputStream(fos)) {
                    for (String chunkPath : chunkStoreList) {
                        java.io.File chunkFile = new java.io.File(chunkPath);
                        if (chunkFile.exists() && chunkFile.isFile()) {
                            try (FileInputStream fis = new FileInputStream(chunkFile);
                                 BufferedInputStream bis = new BufferedInputStream(fis)) {
                                byte[] buffer = new byte[1024];
                                int bytesRead;
                                while ((bytesRead = bis.read(buffer)) != -1) {
                                    bos.write(buffer, 0, bytesRead);
                                }
                            }
                        }
                    }
                }
                long fileSize = Files.size(tempFile);
                InputStream mergedInputStream =new FileInputStream(tempFile.toFile());
                String path=System.currentTimeMillis()+"_"+ file_name;
                //在这里保存
                int res=minioUtils.uploadChunkFile(mergedInputStream,path,file_type);
                //保存到数据库中
                if (res<1){
                    System.out.println("分片保存失败");
                }else {
                    System.out.println("分片保存成功");
                }
                File fileVo=new File();
                fileVo.setFile_id(UUIDUtils.getUUIDString());
                fileVo.setUser_id(user_id);
                fileVo.setFile_md5(file_md5);
                fileVo.setFile_pid(file_pid);
                fileVo.setFile_size(fileSize);
                fileVo.setFile_name(file_name);
                fileVo.setFile_path(path);
                String time=UUIDUtils.getCurrentTime();
                fileVo.setCreate_time(time);
                fileVo.setLast_update_time(time);
                fileVo.setFolder_type(0);
                String fileExtension = getFileExtension(file_name);
                if (File_TYPE_MAP.get(fileExtension)==null){
                    fileVo.setFile_type(10);
                }else {
                    fileVo.setFile_type(File_TYPE_MAP.get(fileExtension));
                }
                if (file_category==0){
                    if (File_TYPE_MAP.get(getFileExtension(file_name))==null){
                        fileVo.setFile_category(5);
                    }else {
                        fileVo.setFile_category(File_CATEGORY.get(fileVo.getFile_type()));
                    }
                }else {
                    fileVo.setFile_category(file_category);
                }
                fileVo.setFile_cover(COVER_MAP.get(fileVo.getFile_type()));//封面
                if (fileVo.getFile_type()==3){//设置图片封面
                    String file_cover=minioUtils.saveImageCover(mergedInputStream,path,0.7f,file_type);
                    fileVo.setFile_cover(file_cover);
                }
                fileVo.setRecovery_time(null);
                fileVo.setDel_flag(3);
                fileMapper.insert(fileVo);//保存到数据库中
                mergedInputStream.close();
            }catch (Exception e){
                e.printStackTrace();
                result.setCode(1002);
                result.setErrorMessage(e.getMessage());
                //读取文件
                return result;
            }
            //删除该文件的全部分片文件
            for (String deletePath:chunkStoreList){
                try {
                    // 创建Path对象
                    Path path = Paths.get(deletePath);
                    // 检查文件是否存在
                    if (Files.exists(path)) {
                        // 删除文件
                        Files.delete(path);
                        System.out.println("文件已成功删除: " + deletePath);
                    } else {
                        System.out.println("文件不存在: " + deletePath);
                    }
                } catch (IOException e) {
                    // 处理可能的异常,如文件不存在、没有权限等
                    e.printStackTrace();
                    result.setCode(1002);
                    result.setErrorMessage(e.getMessage());
                    //读取文件
                    return result;
                }
            }
            result.setCode(1000);
            //读取文件
            return result;
        }
        result.setCode(1001);
        //读取文件
        return result;
    }

希望能够帮助到你。

  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

邻家小妹妹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值