zoukankan      html  css  js  c++  java
  • java:分块上传 和 合并分块

    我们上传文件时,当文件比较大的时候,我们往往采用前端将大文件分割,分块多次上传给后端,全部上传成功再合并分块的方式上传。(这里仅介绍后端操作)

    import com.sundear.model.exception.ServiceException;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.multipart.MultipartFile;

    import java.io.File;
    import java.io.IOException;
    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter;
    import java.util.Calendar;
    import java.util.Date;
    import java.util.Objects;

    /**
    * 文件处理工具类
    *
    * @author luc
    * @date 2020/8/515:52
    */
    @Slf4j
    public class FileUtils {

    /**
    * 校验文件内容不为空
    *
    * @param file 文件对象
    */
    public static void checkContent(MultipartFile file) {
    //校验文件的内容
    if (file.isEmpty()) {
    throw new ServiceException(-1, "文件内容为空");
    }
    }

    /**
    * 判断文件是否在磁盘存在
    *
    * @param filePath 文件路径
    * @return 存在=true;不存在=false
    */
    public static boolean isExist(String filePath) {
    return new File(filePath).exists();
    }

    /**
    * 获取时间文件夹
    *
    * @param dateTime 时间
    * @param pattern 格式
    * @return 文件夹
    */
    public static String dateTime2String(LocalDateTime dateTime, Object... pattern) {
    if (dateTime == null) {
    return "";
    }
    DateTimeFormatter df;
    if (pattern != null && pattern.length > 0) {
    df = DateTimeFormatter.ofPattern(pattern[0].toString());
    } else {
    df = DateTimeFormatter.ofPattern("yyyy/MM/dd/HH");
    }
    return dateTime.format(df);
    }

    /**
    * 创建文件目录
    *
    * @param dirPath 文件地址
    */
    @SuppressWarnings("all")
    public static void createDir(String dirPath) {
    //目录不存在,创建 分块文件目录
    File dirFile = new File(dirPath);
    if (!dirFile.exists()) {
    dirFile.mkdirs();
    }
    }

    /**
    * 上传文件写入磁盘
    *
    * @param file 文件大小
    * @param realPath 真是地址
    * @return 是否写入成功
    */
    @SuppressWarnings("all")
    public static Boolean writeFile(MultipartFile file, String realPath) {
    //在路径下创建文件
    File dest = new File(realPath);
    //将上传的文件保存
    try {
    if (!dest.exists()) {
    //文件不存在,则创建
    dest.createNewFile();
    }
    file.transferTo(dest);
    return Boolean.TRUE;
    } catch (IOException e) {
    log.error(e.getMessage());
    return Boolean.FALSE;
    }
    }

    /**
    * 删除文件夹及其目录下所有文件
    *
    * @param folderPath 文件夹地址
    */
    @SuppressWarnings("all")
    public static void delFolder(String folderPath) {
    try {
    //删除完里面所有内容
    delAllFile(folderPath);
    File myFilePath = new File(folderPath);
    //删除空文件夹
    myFilePath.delete();
    } catch (Exception e) {
    log.error(e.getMessage());
    }
    }

    /**
    * 删除指定文件夹下的所有文件
    *
    * @param path 路径
    * @return true/false
    */
    @SuppressWarnings("all")
    public static void delAllFile(String path) {
    File file = new File(path);
    //文件夹不存在返回false不是文件夹返回false
    if (!file.exists() || !file.isDirectory()) {
    return;
    }
    String[] tempList = file.list();
    File temp = null;
    for (int i = 0; i < Objects.requireNonNull(tempList).length; i++) {
    if (path.endsWith(File.separator)) {
    temp = new File(path + tempList[i]);
    } else {
    temp = new File(path + File.separator + tempList[i]);
    }
    if (temp.isFile()) {
    temp.delete();
    }
    if (temp.isDirectory()) {
    //先删除文件夹里面的文件
    delAllFile(path + "/" + tempList[i]);
    //再删除空文件夹
    delFolder(path + "/" + tempList[i]);
    }
    }
    }

    /**
    * 获取文件扩展名
    *
    * @param file 文件
    * @return 后缀
    */
    public static String getFileSuffix(MultipartFile file) {
    String fileName = file.getOriginalFilename();
    if ((fileName != null) && (fileName.length() > 0)) {
    int dot = fileName.lastIndexOf('.');
    if ((dot > -1) && (dot < (fileName.length() - 1))) {
    return fileName.substring(dot);
    }
    }
    assert fileName != null;
    return fileName.toLowerCase();
    }


    /**
    * 删除文件夹fromDir下的 howDays天前的文件
    *
    * @param fromDir 文件夹
    * @param howDays 天数
    * @return 数目
    */
    @SuppressWarnings("all")
    public static Integer clearFileCache(String fromDir, int howDays) {
    File srcDir = new File(fromDir);
    if (!srcDir.exists()) {
    return 0;
    }
    File[] files = srcDir.listFiles();
    if (files == null || files.length <= 0) {
    return 0;
    }
    // 删除文件总数
    int delTotal = 0;
    Date today = new Date();
    for (File file : files) {
    if (file.isFile()) {
    try {
    long time = file.lastModified();
    Calendar cal = Calendar.getInstance();
    cal.setTimeInMillis(time);
    Date lastModified = cal.getTime();
    //(int)(today.getTime() - lastModified.getTime())/86400000;
    long days = getDistDates(today, lastModified);
    // 删除多少天前之前文件
    if (days >= howDays) {
    file.delete();
    delTotal++;
    }
    } catch (Exception e) {
    log.error(e.getMessage());
    }
    }
    }
    return delTotal;
    }

    /**
    * 获取两个时间之间的 天数
    *
    * @param startDate 开始日期
    * @param endDate 结束日期
    * @return 天数
    */
    public static long getDistDates(Date startDate, Date endDate) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(startDate);
    long timeStart = calendar.getTimeInMillis();
    calendar.setTime(endDate);
    long timeEnd = calendar.getTimeInMillis();
    return Math.abs((timeEnd - timeStart)) / (1000 * 60 * 60 * 24);
    }
    }

    开始分片上传
    /**
    * 分片上传
    *
    * @param file 文件对象
    * @param req 唯一标识+类型分组
    * @param partNum 分片号
    */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void uploadPart(MultipartFile file, ValidReq req, Integer partNum) {
    String fileType = req.getFileType();
    String fileMd5 = req.getFileMd5();
    String clientId = req.getClientId();
    //校验文件内容不为空
    FileUtils.checkContent(file);
    //获取分片号
    partNum = (partNum == null) ? 0 : partNum;
    //服务器上传路径
    String comPath = properties.comPath();
    //分块存储目录
    String partPath = String.join(FileConstant.SEPARATOR, comPath, fileMd5).replaceAll("//+", "/");
    //当前分块全路径: 服务器路径/文件md5签名/分片文件
    String partFilePath = String.join(FileConstant.SEPARATOR, partPath, String.valueOf(partNum));

    //校验分片是否上传
    boolean isUpload = FileUtils.isExist(partFilePath);
    if (!isUpload) {
    //分片目录不存在,创建 分块文件目录
    FileUtils.createDir(partPath);
    //分片文件写入磁盘
    Boolean isSuccess = FileUtils.writeFile(file, partFilePath);
    if (partNum == 0) {
    if (isSuccess) {
    //上传第一片分块时,写入数据库
    String suffix = FileUtils.getFileSuffix(file);
    String fileId = IdWorker.getIdWorker().nextIdStr();
    UploadFile uploadFile = new UploadFile();
    uploadFile.setFileId(fileId);
    uploadFile.setFileMd5(fileMd5);
    uploadFile.setFileName(file.getOriginalFilename());
    uploadFile.setFileType(fileType);
    Byte status = FileStatusEnum.UPLOADING.getCode();
    uploadFile.setFileStatus(status);
    uploadFile.setClientId(clientId);
    uploadFile.setFileSuffix(suffix);
    uploadFileService.insert(uploadFile);
    log.info("上传第一个分片文件,生成文件上传记录,文件id: " + fileId);
    } else {
    log.info("上传第一个分片文件失败");
    }
    } else {
    log.info("上传第" + partNum + "个分片成功");
    }
    }
    }
    /**
    * 服务器文件地址
    */
    public String comPath(){
    return String.join(FileConstant.SEPARATOR, this.uploadPath, this.filePath).replaceAll("//+", "/");
    }

    合并分片
    /**
    * 分片合并完成上传
    *
    * @param req 唯一标识+类型分组
    * @param partTotal 分片数
    * @return 文件记录
    */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public UploadFile uploadFinish(ValidReq req, Integer partTotal) {
    String fileType = req.getFileType();
    String fileMd5 = req.getFileMd5();
    //获取文件上传记录数据
    UploadFile uploadFile = uploadFileService.getByFileMd5(req);

    if (uploadFile == null || StringUtils.isBlank(uploadFile.getFileId())) {
    throw new ServiceException(-1, "分片未正确上传");
    }
    if (partTotal == null || partTotal == 0) {
    throw new ServiceException(-1, "分片总数不能为空");
    }
    //上传路径
    String comPath = properties.comPath();
    //相对路径
    String relativePath = String.join(FileConstant.SEPARATOR, fileType, FileUtils.dateTime2String(LocalDateTime.now())).replaceAll("//+", "/");
    //xxx/files/2020/08/11/13
    String dirPath = String.join(FileConstant.SEPARATOR, comPath, relativePath).replaceAll("//+", "/");
    //最终文件路径,不存在的话生成文件目录
    FileUtils.createDir(dirPath);
    //文件真实路径
    String realPath = String.join(FileConstant.SEPARATOR, dirPath, uploadFile.getFileId() + uploadFile.getFileSuffix()).replaceAll("//+", "/");

    //分块存储目录
    String partPath = String.join(FileConstant.SEPARATOR, comPath, fileMd5).replaceAll("//+", "/");

    ///合成后的文件流
    try (FileOutputStream fileOutputStream = new FileOutputStream(realPath)) {
    byte[] buf = new byte[1024];
    for (long i = 0; i < partTotal; i++) {
    //当前分块全路径: 服务器路径/文件md5签名/分片文件
    String partFilePath = String.join(FileConstant.SEPARATOR, partPath, String.valueOf(i));
    //获取分块文件
    File file = new File(partFilePath);
    //获取文件流
    InputStream inputStream = new FileInputStream(file);
    int len;
    while ((len = inputStream.read(buf)) != -1) {
    fileOutputStream.write(buf, 0, len);
    }
    inputStream.close();
    }
    //删除md5目录,及临时文件
    FileUtils.delFolder(partPath);
    //文件大小设置
    File finalFile = new File(realPath);
    String size = String.valueOf(finalFile.length());

    uploadFile.setFileSize(size);
    uploadFile.setFilePath(relativePath);
    uploadFile.setFileStatus(FileStatusEnum.PASS.getCode());
    uploadFileService.update(uploadFile);
    } catch (Exception e) {
    log.error(e.getMessage());
    throw new ServiceException(-1, "文件合并异常");
    }
    return uploadFile;
    }



  • 相关阅读:
    【硬件】组装一台多核电脑
    【硬件】组装电脑前的准备工作
    【长知识】设计多核电脑装机方案
    【长知识】认识电脑的硬件组成
    程序员必备基础:如何安全传输存储用户密码?
    二本应届生的大学生活、2020年总结(已上岸百度)
    白日梦的Elasticsearch系列笔记(一)基础篇-- 快手上手ES
    全网最牛X的!!! MySQL两阶段提交串讲
    删库后!除了跑路还能干什么?
    数据库面试简答、30道高频面试题
  • 原文地址:https://www.cnblogs.com/yxj808/p/14808835.html
Copyright © 2011-2022 走看看