FastDFS結合vue-simple-uploader實現斷點續傳

參考:https://gitee.com/zwlan/renewFastdfs

1. maven依賴

<!--<fast.clent.version>1.26.2</fast.clent.version>-->
<!--<hutool.all.version>4.0.12</hutool.all.version>-->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>${hutool.all.version}</version>
</dependency>

<dependency>
    <groupId>com.github.tobato</groupId>
    <artifactId>fastdfs-client</artifactId>
    <version>${fast.clent.version}</version>
</dependency>

2. 部分配置

#redis

spring:  
  redis:
    open: true  # 是否開啓redis緩存  true開啓   false關閉
    database: 0
    host: xx.xx.xx.xx # ip
    port: xxxx # port
    password:   # 密碼(默認爲空)
    timeout: 6000ms  # 連接超時時長(毫秒)
    jedis:
      pool:
        max-active: 1000  # 連接池最大連接數(使用負值表示沒有限制)
        max-wait: -1ms      # 連接池最大阻塞等待時間(使用負值表示沒有限制)
        max-idle: 10      # 連接池中的最大空閒連接
        min-idle: 5       # 連接池中的最小空閒連接

#FastDFS Client
fdfs:
  so-timeout: 1501
  connect-timeout: 601
  thumb-image: #縮略圖生成參數
    width: 150
    height: 150
  tracker-list: #TrackerList參數,支持多個
    - xx.xx.xx.xx:22122  #ip:port
  pool:
    max-total: 153
    jmx-name-base: 1
    jmx-name-prefix: 1

# nginx 反向代理之後的查看資源路徑
fastDFS:
  file:
    server:
      url: http://xx.xx.xx.xx:8091 # ip:port
    show:
      url: http://xx.xx.xx.xx:8090 

#FastDFS臨時上傳路徑
file:
  upload:
    temp:
      path: /data0/fastdfs/temp

3. 代碼

package cn.longrace.wisdom.modules.uploader.controller;

import cn.hutool.core.io.FileUtil;
import cn.longrace.wisdom.common.utils.RedisUtils;
import cn.longrace.wisdom.common.vo.R;
import cn.longrace.wisdom.modules.sys.controller.AbstractController;
import cn.longrace.wisdom.modules.uploader.common.UpLoadConstant;
import cn.longrace.wisdom.modules.uploader.entity.Chunk;
import cn.longrace.wisdom.modules.uploader.entity.FileInfo;
import cn.longrace.wisdom.modules.uploader.service.ChunkService;
import cn.longrace.wisdom.modules.uploader.service.FileInfoService;
import cn.longrace.wisdom.modules.uploader.util.FileUtils;
import com.github.tobato.fastdfs.domain.StorePath;
import com.github.tobato.fastdfs.service.AppendFileStorageClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Random;

import static cn.longrace.wisdom.modules.uploader.util.FileUtils.generatePath;

/**
 * @author 
 * @date
 */
@RestController
@RequestMapping("/new/uploader")
@Slf4j
public class NewUploadController extends AbstractController {

    @Value("${fastDFS.file.server.url}")
    private String fileUploadUrl;

    @Value("${fastDFS.file.show.url}")
    private String showFileUploadUrl;

    @Value("${file.upload.temp.path}")
    private String uploadFolder;

    @Autowired
    private AppendFileStorageClient appendFileStorageClient;

    private Long curriculumId;

    @Resource
    private ChunkService chunkService;

    @Autowired
    RedisUtils redisUtils;

    @Autowired
    FileInfoService fileInfoService;

    /**
     * 方式一:
     * 功能描述:上傳文件
     *
     * @param:
     * @return:
     * @auther: 
     * @date:
     */
    @PostMapping("/chunk")
    public String uploadChunk(Chunk chunk) {
        MultipartFile file = chunk.getFile();
        log.debug("file originName: {}, chunkNumber: {}", file.getOriginalFilename(), chunk.getChunkNumber());

        try {
            String contentType = file.getContentType();
            chunk.setType(contentType);

            byte[] bytes = file.getBytes();
            Path p1 = Paths.get(generatePath(uploadFolder, chunk));
            //文件寫入指定路徑
            Path p = Files.write(p1, bytes);
            log.debug("文件 {} 寫入成功, uuid:{}", chunk.getFilename(), chunk.getIdentifier());
            StorePath path = null;
            try {
                File f = p.toFile();
                if (redisUtils.get(chunk.getIdentifier()) == null) {
                    // 創建可續傳的文件
                    path = appendFileStorageClient.uploadAppenderFile(UpLoadConstant.DEFAULT_GROUP,  new FileInputStream(f), f.length(), FileUtil.extName(chunk.getFilename()));
                    redisUtils.set(chunk.getIdentifier(), path.getPath());
                } else {
                    // 最後一個參數記錄文件的偏移量
                    appendFileStorageClient.modifyFile(UpLoadConstant.DEFAULT_GROUP, redisUtils.get(chunk.getIdentifier()),  new FileInputStream(f), f.length(),(chunk.getChunkNumber() -1)*(10*1024*1024));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            // 保存chunk
            Random rn = new Random(1000);
            chunk.setId(rn.nextLong());
            chunk.setCreator(this.getUserId());
            chunk.setCurriculumId(curriculumId);
            chunkService.saveChunk(chunk);

            return "文件上傳成功";
        }catch (Exception e) {
            e.printStackTrace();
            return "後端異常...";
        }
    }

    @GetMapping("/chunk")
    public Object checkChunk(Chunk chunk, HttpServletResponse response) {
        curriculumId = chunk.getCurriculumId();
        if (chunkService.checkChunk(chunk)) {
            response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
        }
        return chunk;
    }

    @PostMapping("/mergeFile")
    public R test (FileInfo fileInfo) throws Exception {
        // TODO 截圖一----------------------
        String filePath = showFileUploadUrl + "/"+ UpLoadConstant.DEFAULT_GROUP + "/" + redisUtils.get(fileInfo.getIdentifier());
        String filename = fileInfo.getFileName();
        String extension = filename.substring(filename.indexOf('.') + 1, filename.length());
        String finalExtension = "mp4";
        try {
            if (!StringUtils.isEmpty(extension) && extension.equals(finalExtension)) {
                System.out.println("==============視頻截圖開始================");
                String imgFileName = filename + ".jpg";
                String imgFilePath = uploadFolder + "/" + filename + ".jpg";

                FileUtils.fetchFrame(filePath, imgFilePath);
                File tempImgFile = new File(imgFilePath);
//                FileInputStream fileImgInputStream = new FileInputStream(tempImgFile);
//                MultipartFile multipartImgFile = new MockMultipartFile(imgFileName, imgFileName,
//                        ContentType.APPLICATION_OCTET_STREAM.toString(), fileImgInputStream);
//
//                String jsonImgStr = HttpUtils.upload(fileUploadUrl, multipartImgFile);
//                System.out.println("jsonImgStr-------------------------"+jsonImgStr);
                StorePath path = appendFileStorageClient.uploadAppenderFile(UpLoadConstant.DEFAULT_GROUP,  new FileInputStream(tempImgFile), tempImgFile.length(), FileUtil.extName(imgFileName));
                fileInfo.setImgLocation(UpLoadConstant.DEFAULT_GROUP + "/" + path.getPath());
                tempImgFile.delete();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        System.out.println("==============視頻截圖結束================");
        // UpLoadConstant.DEFAULT_GROUP 即group1
        fileInfo.setLocation(UpLoadConstant.DEFAULT_GROUP + "/" + redisUtils.get(fileInfo.getIdentifier()));
        this.fileInfoService.insert(fileInfo);
        // 刪除redis中文件key
        redisUtils.delete(fileInfo.getIdentifier());
        // 刪除臨時文件目錄
        FileUtils.deleteDir(new File(uploadFolder + "/" + fileInfo.getIdentifier()));
        return R.ok().put("fileInfo",fileInfo);
    }
}

4. 相關實體類

package cn.longrace.wisdom.modules.uploader.entity;

import com.baomidou.mybatisplus.annotations.TableField;
import com.baomidou.mybatisplus.annotations.TableName;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;

import java.io.Serializable;

/**
 * 文件分塊大小
 */
@Data
@TableName("lr_teacher_resource_chunk")
public class Chunk implements Serializable {
    private Long id;
    /**
     * 當前文件塊,從1開始
     */
    private Integer chunkNumber;
    /**
     * 分塊大小
     */
    private Long chunkSize;
    /**
     * 當前分塊大小
     */
    private Long currentChunkSize;
    /**
     * 總大小
     */
    private Long totalSize;
    /**
     * 文件標識
     */
    private String identifier;
    /**
     * 文件名
     */
    private String filename;

    /**
     * 相對路徑
     */
    private String relativePath;
    /**
     * 總塊數
     */
    private Integer totalChunks;
    /**
     * 文件類型
     */
    private String type;

    /**
     * 課程ID
     */
    private Long curriculumId;
    /**
     * 創建人
     */
    private Long creator;
    /**
     * 所屬備課ID
     */
    private Long prepareId;
    /**
     * 所屬班級ID
     */
    private Long classId;

    @TableField(exist = false)
    private MultipartFile file;
}
package cn.longrace.wisdom.modules.uploader.entity;

import com.baomidou.mybatisplus.annotations.TableName;
import lombok.Data;

import java.io.Serializable;

/**
 * 文件信息實體類
 */
@Data
@TableName("lr_teacher_resource_file_info")
public class FileInfo implements Serializable {
    private Long id;

    /**
     * 文件名稱
     */
    private String fileName;
    /**
     * 文件標識
     */
    private String identifier;
    /**
     * 文件總大小
     */
    private Long totalSize;
    /**
     * 文件類型
     */
    private String type;
    /**
     * 文件路徑
     */
    private String location;
    /**
     * 視頻文件封面路徑
     */
    private String imgLocation;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章