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