当前位置:网站首页>文件 - 02 上传文件:上传临时文件到服务器
文件 - 02 上传文件:上传临时文件到服务器
2022-07-31 05:48:00 【追风筝~】
系列文章:
1.FileController
@RestController
@ResponseBody
@ResponseResult
@Slf4j
@RequestMapping("/ngsoc/PORTAL/api/v1")
@Api(tags = "File")
public class FileController {
/** * * @param file 临时文件 * @param maxFileSize 用于限制上传文件的大小,这个限制是业务功能要求的权限,不是系统最大限制。不指定的时候不做限制。 * @param minFileSize 用于限制上传文件体积的最小值,可是0,这个限制是业务功能要求的权限,不指定的时候,该值为1 * @param unit 文件大小单位 * @param share 是否共享 * @param request 请求 */
@SneakyThrows
@OperateLog(target = "operation.target.file", action = "operation.action.file.temp.upload")
@ApiOperation(value = "上传临时文件", notes = "上传临时文件,默认的过期时间12h", httpMethod = "POST")
@RequestMapping(value = "/files/temp", method = RequestMethod.POST)
public FileInfoResponse uploadTempFile(
@ApiParam(value = "上传文件时的key", required = true) @RequestParam(value = "file", required = true) MultipartFile file,
@RequestParam(value = "maxFileSize", required = false) Integer maxFileSize,
@RequestParam(value = "minFileSize", required = false, defaultValue = "1") Integer minFileSize,
@RequestParam(value = "unit", required = false, defaultValue = "K") String unit,
@ApiParam(value = "是否所有人都可以获取&操作该文件", required = false) @RequestParam(value = "share", required = false, defaultValue = "1") Integer share,
HttpServletRequest request
) {
FileInfoResponse response = fileService.uploadTempFile(
file,
maxFileSize,
minFileSize,
unit,
share,
request
);
// 触发清理任务
fileClearerService.triggerClearTask();
return response;
}
}
2. IFileService接口
@Slf4j
public class FileServiceImpl implements IFileService {
@Override
public FileInfoResponse uploadTempFile(
MultipartFile file,
Integer maxFileSize,
Integer minFileSize,
String unit,
Integer share,
HttpServletRequest request
) throws UploadDownloadFileException {
// 获取临时文件的过期时间
String expireTimeForTempFile = fileConfig.getExpireTimeForTempFile();
if (Strings.isEmpty(expireTimeForTempFile)) {
expireTimeForTempFile = FileConfig.DEFAULT_EXPIRE_TIME_FOR_TEMP_FILE;
}
return this.uploadFile(
file,
maxFileSize,
minFileSize,
unit,
expireTimeForTempFile,
share,
request
);
}
/** * 上传附件 * @param file 文件流 * @param maxFileSize 可上传文件的最大值 * @param minFileSize 可上传文件的最小值 * @param unit 文件单位 * @param expireTime 文件过期时间 * @param share 分享 * @param request 请求 * @return * @throws UploadDownloadFileException */
@Override
public FileInfoResponse uploadFile(
MultipartFile file,
Integer maxFileSize,
Integer minFileSize,
String unit,
String expireTime,
Integer share,
HttpServletRequest request
) throws UploadDownloadFileException {
// 检查文件和目录
checkFolderAndFile(file, maxFileSize, minFileSize, unit);
long current = System.currentTimeMillis();
// 检查和解析文件的超时时间
ExpireConfig expireConfig = checkAndParseExpireTime(expireTime, current);
// 准备文件的相关信息
String fileId = UuidUtils.uuidWithoutMinus().concat(String.valueOf(current));
// 存储在mysql里的文件路径后半部分 generateFileStorePath(); 存储在mysql中
Path pathSuffix = this.generateFileStorePath(fileId);
String fileUploader = ClientUtils.getClientAccount(request);
Timestamp nowTimeStamp = new Timestamp(current);
// 保存文件到服务器的目录
storeFileToServerFolder(file, pathSuffix.toString());
// 文件信息入库
FileInfo fileInfo = new FileInfo(
fileId,
file.getOriginalFilename(),
file.getContentType(),
file.getSize(),
pathSuffix.toString(),
fileUploader,
expireConfig.getNeverExpire(),
expireConfig.getExpireTime(),
nowTimeStamp,
nowTimeStamp,
share
);
this.saveFileInfo(fileInfo);
// 记录fileToken 和uuid的映射
String downloadLinkToken = UuidUtils.uuidWithoutMinus();
this.saveDownloadLinkTokenId(downloadLinkToken, fileId);
// 返回下载链接
String downloadLink = fileConfig.getDownloadLinkBase().concat(downloadLinkToken);
return new FileInfoResponse(fileId, file.getOriginalFilename(), downloadLink);
}
/** * 检查文件和目录 * @param file * @param maxFileSize * @param minFileSize * @param unit * @throws UploadDownloadFileException */
private void checkFolderAndFile(MultipartFile file, Integer maxFileSize, Integer minFileSize, String unit) throws UploadDownloadFileException {
// 检查文件夹大小是否超过配置的限制
Double folderSizeU = transFolderSize(fileDao.getFolderSize(), fileConfig.getFolderSizeUnit());
if (folderSizeU > fileConfig.getFolderSize()) {
throw new UploadDownloadFileException(I18nUtils.i18n("exception.file.folder.size.oversize"));
}
// 检查文件名长度
String filename = Objects.requireNonNull(file.getOriginalFilename());
if (filename.length() > FileConstants.MAX_FILENAME_LENGTH) {
log.error("filename length is too long");
throw new UploadDownloadFileException(I18nUtils.i18n("exception.file.filename.length", FileConstants.MAX_FILENAME_LENGTH));
}
// 检查文件后缀名
String suffix = this.getFileSuffix(filename);
List<String> whiteSuffix = fileConfig.getWhiteSuffix();
if (!whiteSuffix.contains(suffix)) {
log.error("File type not permitted.");
throw new UploadDownloadFileException(I18nUtils.i18n("exception.file.type"));
}
// 判断文件大小是否超出、或是否小于配置
Long fileSize = file.getSize();
if (maxFileSize != null) {
if (this.isFileOversize(fileSize, maxFileSize, unit)) {
// 大于就抛出异常
log.error("Upload file too big.");
throw new UploadDownloadFileException(I18nUtils.i18n("exception.file.size.oversize"));
}
}
if (this.isFileTinySize(fileSize, minFileSize, unit)) {
// 小于也抛出异常
log.error("upload file too tiny.");
throw new UploadDownloadFileException(I18nUtils.i18n("exception.file.size.tiny"));
}
}
/** * 封装文件过期时间配置类 * @param expireTime 前端传入的文件过期时间 * @param currentTimestamp 当前系统时间 * @return ExpireConfig * @throws UploadDownloadFileException */
private ExpireConfig checkAndParseExpireTime(String expireTime, Long currentTimestamp) throws UploadDownloadFileException {
try {
// never/NEVER
if (ExpireConfig.NEVER.equalsIgnoreCase(expireTime)) {
// neverExpire = 1, expireTime = null
// 永不过期
return new ExpireConfig(ExpireConfig.NEVER_EXPIRE_VALUE, null);
}
long expireMilliSeconds = TimeUtils.parseTimeStringToMillis(expireTime);
Timestamp expireTimestamp = new Timestamp(expireMilliSeconds + currentTimestamp);
// neverExpire = 0, expireTime = xxx
return new ExpireConfig(ExpireConfig.WILL_EXPIRE_VALUE, expireTimestamp);
} catch (Exception e) {
log.error("checkAndParseExpireTime failed: ", e);
throw new UploadDownloadFileException(I18nUtils.i18n("exception.file.expire.invalid"));
}
}
/** * 生成存储文件路径 * @param fileId 文件id * @return */
@Override
public Path generateFileStorePath(String fileId) {
String[] dirs = {
fileId.substring(0, 2), fileId.substring(2, 4) };
Path pathSuffix = Paths.get(dirs[0], dirs[1], UuidUtils.uuidWithoutMinus());
return pathSuffix;
}
@Override
public void saveDownloadLinkTokenId(String token, String id) throws DataAccessException {
ValueOperations<String, String> opsForValue = this.stringRedisTemplate.opsForValue();
if (BooleanUtils.isNotTrue(opsForValue.setIfAbsent(token, id, fileConfig.getTokenExpireTime(), TimeUnit.MINUTES))) {
log.error("save downloadLinkToken-fileId failed.");
throw new RedisDataAccessException("exception.file.redis.save");
}
}
}
读取配置文件到配置类,配置文件:application-fileupload.yml
ngsoc:
file:
savePath: /opt/ngsoc/data/local/upload/
tokenExpireTime: 10
downloadLinkBase: /ngsoc/PORTAL/api/v1/files/fetch/
content-type:
- image
white-suffix: [ pdf, doc, xml, html, doc, docx, txt, wps, xls, xlsx, png, jpg, jpeg, gif, rtf, bmp, icon, mpg, zip, rar, 7z, ppt, pptx ]
white-urls:
- /api/v1/files/fetch/*
folderSize: 8
folderSizeUnit: G
# 临时文件的过期时间,默认半天 12h
expireTimeForTempFile: 12h
# 清理过期文件任务的冷静期,暂定为 30min
calmingPeriodOfClearTask: 30min
@Data
@Configuration
@ConfigurationProperties(prefix = "ngsoc.file")
public class FileConfig {
/** * 临时文件的默认过期时间 */
public static final String DEFAULT_EXPIRE_TIME_FOR_TEMP_FILE = "12h";
/** * 冷静期的默认时间 */
public static final String DEFAULT_CALMING_PERIOD_OF_CLEAR_TASK = "30min";
/** * 附件的保存路径 * */
private String savePath;
/** * token的失效时间 * */
private Integer tokenExpireTime;
/** * 附件下载的路径 * */
private String downloadLinkBase;
/** * 文件类型 * */
private List<String> contentType;
/** * 附件白名单后缀 * */
private List<String> whiteSuffix;
/** * 白名单 */
private List<String> whiteUrls;
/** * 文件最小尺寸 * */
private Integer minFileSize;
/** * 文件大小 * */
private Integer folderSize;
/** * 文件大小的单位 * */
private String folderSizeUnit;
/** * 临时文件的失效时间 * */
private String expireTimeForTempFile;
/** * 任务清理的周期 * */
private String calmingPeriodOfClearTask;
}
边栏推荐
- Chapter 17: go back to find the entrance to the specified traverse, "ma bu" or horse stance just look greedy, no back to search traversal, "ma bu" or horse stance just look recursive search NXM board
- postgresql源码学习(33)—— 事务日志⑨ - 从insert记录看日志写入整体流程
- 一文读懂 MongoDB 和 MySQL 的差异
- 批量免费文字翻译
- Log4net 思维导图
- 第十七章:回溯探求指定入口的马步遍历,贪心无回溯探求马步遍历,递归探求nxm棋盘带障碍马步遍历
- 数据库概论 - MySQL的简单介绍
- Kubernetes scheduling
- 安装gstreamer开发依赖库到项目sysroot目录
- 银河麒麟高级服务器v10 sp1 手动加载Raid卡驱动
猜你喜欢
熟悉而陌生的新朋友——IAsyncDisposable
Analysis of the principle and implementation of waterfall flow layout
DHCP原理与配置
浅析v-model语法糖的实现原理与细节知识及如何让你开发的组件支持v-model
浅析重复线性渐变repeating-linear-gradient如何使用
uni-app生命周期
mysql索引失效的常见9种原因详解
Difficulty comparison between high concurrency and multithreading (easy to confuse)
360推送-360推送工具-360批量推送工具
关于求反三角函数的三角函数值
随机推荐
浅析v-model语法糖的实现原理与细节知识及如何让你开发的组件支持v-model
es6数组/数组对象求并集、交集、差集、去重、排序
Core Tower Electronics won the championship in the Wuhu Division of the 11th China Innovation and Entrepreneurship Competition
【并发编程】ReentrantLock的lock()方法源码分析
Analysis of the implementation principle and detailed knowledge of v-model syntactic sugar and how to make the components you develop support v-model
芯塔电子斩获第十一届中国双创大赛芜湖赛区桂冠
进程和计划任务管理
测试 思维导图
PXE高效批量网络装机
codec2 BlockPool:不可读库
Conditional statements of shell (test, if, case)
MySQL笔记下
银河麒麟服务器v10 sp1安装.net6
Database Principles Homework 3 — JMU
LeetCode brush # 376 # Medium - swing sequence
DirectExchange交换机简单入门demo
单点登录 思维导图
深度解析 z-index
gstreamer的caps event和new_segment event
浅析重复线性渐变repeating-linear-gradient如何使用