參考: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;
}