原生分片文件上传、合并的具体步骤

前端是把文件分片传来的,获得第一片文件的时候要进行下特殊的判断:数据库中是否已经有了该文件(通过文件的MD5值进行查询),如果有的话就重命名,并且实现秒传。
//如果上传的是第一个分片
if (chunkIndex == 0) {
​
    //封装一个查询,查询数据库中是否已经有
    FileInfoQuery infoQuery = new FileInfoQuery();
    infoQuery.setFileMd5(fileMd5);//设置md5
    infoQuery.setStatus(FileStatus.USING.getStatus()); //必须是using
    List<FileInfo> dbFileList = fileInfoMapper.selectList(infoQuery); //查询这个分片是否上传了
​
    if (!dbFileList.isEmpty()) { //已经有了,就需要重新命名
        FileInfo fileInfo = dbFileList.get(0);
        if (fileInfo.getFileSize() < 0) //TODO:到了后面修改,判断现有空间是否可以装下,此分片
            throw new BusinessException(ResponseCodeEnum.CODE_904);
​
        /*封装信息*/
        fileInfo.setFileId(fileId);
        fileInfo.setFilePid(filePid);
        fileInfo.setUserId(userInfo.getUserId());
        fileInfo.setLastUpdateTime(LocalDateTime.now());
        fileInfo.setDelFlag(FileDelFlagEnums.USING.getFlag());
        fileInfo.setStatus(FileStatus.USING.getStatus());
        fileInfo.setFileMd5(fileMd5);
​
        //进行重新命名操作
        fileName = autoRename(filePid, userInfo.getUserId(), fileName);
        fileInfo.setFileName(fileName);
​
        //更新数据信息
        fileInfoMapper.insert(fileInfo);
​
        resultDTO.setStatus(UploadStatus.UPLOAD_SECONDS.getStatus());  //设置秒传
​
        //更新用户使用空间
        updateUserSpace(userInfo, fileInfo.getFileSize());
        return resultDTO;
    }
}

除了最后一个文件片段,其余的片段都要获取chunkIndex, 放到临时的目录下面去,返回信息到前端,以便前端可以继续上传其余片段。

//判断是不是上传最后一次切片
if (chunkIndex < chunks - 1) {
    resultDTO.setStatus(UploadStatus.UPLOADING.getStatus());
    //TODO:更新Redis中的信息
    return resultDTO;
}

到了最后一个片段,就需要获得文件的一些信息:后缀名、文件类型的枚举、不重复的文件名称等,封装好对应的信息,放到数据库中去。

String suffix = StringTools.getFileSuffix(fileName);
//真实的文件名
String realFileName = currentUserFolderName + suffix;
​
// 文件类型的枚举
FileTypeEnums fileTypeEnums = FileTypeEnums.getFileTypeBySuffix(suffix);
​
//自动重命名
fileName = autoRename(filePid,userInfo.getUserId(),fileName);
​
​
FileInfo fileInfo = new FileInfo();  //Entity
fileInfo.setFileId(fileId);
fileInfo.setUserId(userInfo.getUserId());
fileInfo.setFileName(fileName);
fileInfo.setFileMd5(fileMd5);
fileInfo.setFileName(getYearAndMouth() + "/" + realFileName );
fileInfo.setCreateTime(LocalDateTime.now());  //设置时间
fileInfo.setLastUpdateTime(LocalDateTime.now()); //最后一次更新时间
fileInfo.setFileCategory(fileTypeEnums.getType());  //设置种类
fileInfo.setFileType(fileTypeEnums.getType());   //设置种类
​
fileInfo.setStatus(FileStatus.TRANSFER.getStatus());  //设置转码中
fileInfo.setFolderType(FileFolderTypeEnums.FILE.getType());  //文件
fileInfo.setDelFlag(FileDelFlagEnums.USING.getFlag());  //使用中
​
​
//插入到数据库中去
this.fileInfoMapper.insert(fileInfo);
​
//TODO:更新redis中容量,Mysql中数据的占用
resultDTO.setStatus(UploadStatus.UPLOAD_FINISH.getStatus());  //设置文件上传完毕

上传完成所有的文件片段后,使用异步的方式,完成文件片段的合成。

TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
    @Override
    public void afterCommit() {
        fileInfoService.transferFile(fileInfo.getFileId(),userInfo);  //异步请求
    }
});

这里的fileInfoService 也得在fileInfoServiceImpl中注入,而且要加上@Lazy,避免循环引用。

在transferFile中,需要根据传入的信息,进行前面insert数据的查询,如果不存在就直接返回,因为合并的文件不存在。

if(fileInfos == null || fileInfos.size() == 0 ||! FileStatus.TRANSFER.getStatus().equals(fileInfos.get(0).getStatus())){  //文件不是转码中或者文件为空
    return;
}

拼装临时目录、真实文件路径和真实文件名称。

//临时目录
String tempFolderName = appconfiguration.getProjectFolder() + Constants.FILE_FOLDER_TEMP;
String currentUserFolderName = sessionWebDTO.getUserId() + fileId;
File fileFolder = new File(tempFolderName + currentUserFolderName);
​
String fileSuffix = StringTools.getFileSuffix(fileInfo.getFileName());  //获得文件后缀
String date = getYearAndMouth();  //就是为了获得目标目录
String targetFolderName = appconfiguration.getProjectFolder() + Constants.FILE_FOLDER_FILE; //真实目录

获得上一步存入的临时文件夹。

File targetFolder = new File(targetFolderName + "/" + date);

调用union方法,合并文件。

union(fileFolder.getPath(),targetFilePath,fileInfo.getFileName(),true);

在union中进行文件的合并,根据文件路径,获得对应的临时文件夹,使用writeFile(RandomAccessFile)写入的最最终的文件,使用readFile(RandomAccessFile)读入每个临时文件,放到最终的文件中去。

try{
    writeFile = new RandomAccessFile(targetFile,"rw");
    byte[] b = new byte[1024 * 10];
    for(int i = 0;i < fileList.length; i++){
        int len = -1;
        File chunkFile = new File(dirPath + "/" + i);  //分片文件
        RandomAccessFile readFile = null;
        try{
            readFile = new RandomAccessFile(chunkFile,"r");  //读取
            while((len = readFile.read(b))!= -1){
                writeFile.write(b,0,len);
            }
        }catch (Exception e){
            System.err.printf("-----------------------文件读取错误:%s :-------------------------",e.getMessage());
            throw new BusinessException("文件读取失败!");
        }finally {
            readFile.close();
        }
    }
}catch(Exception e){
    log.error("合并文件失败 :{}",fileName);
    throw new BusinessException("上传文件失败 !");
}finally {
    if(writeFile != null){
        try{
            writeFile.close();  //关闭对应的文件流
        }catch (IOException e){
            e.printStackTrace();
        }
    }
    if(delSource && dir.exists()){
        try {
            FileUtils.deleteDirectory(dir);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

最后关闭各种流,更新file_info中的文件大小、状态信息。

finally {
​
    //更新转码状态
    FileInfoUpdateDto updateInfo = new FileInfoUpdateDto();
​
    updateInfo.setFileSize(new File(targetFilePath).length());   //获得目标文件的大小
    updateInfo.setFileCover(cover);
    updateInfo.setStatus(transferSuccess ? FileStatus.USING.getStatus() : FileStatus.TRANSFER_FAIL.getStatus() );
    updateInfo.setFileId(fileId);
    updateInfo.setUserId(sessionWebDTO.getUserId());
    updateInfo.setNewValue(updateInfo.getStatus());
    updateInfo.setOldStatus(FileStatus.TRANSFER.getStatus());
​
    //封装信息
    fileInfoMapper.updateFileStatusWithOldStatus(updateInfo);
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值