当前位置:网站首页>文件 - 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;
}
边栏推荐
- gstreamer的caps event和new_segment event
- R——避免使用 col=0
- LeetCode brush # 376 # Medium - swing sequence
- SQLite数据库连接字符串
- Koa框架的基本使用
- Zero-Shot Learning & Domain-aware Visual Bias Eliminating for Generalized Zero-Shot Learning
- 测试 思维导图
- 浅析瀑布流布局原理及实现方式
- (border-box)盒子模型w3c、IE的区别
- Some derivation formulas for machine learning backpropagation
猜你喜欢
随机推荐
Moment.js常用方法
线程唤醒机制
SQLite数据库连接字符串
360 push-360 push tool-360 batch push tool
小实战项目之——吃货联盟订餐系统
Explain the example + detail the difference between @Resource and @Autowired annotations (the most complete in the entire network)
DNS域名解析服务
How to choose a suitable UI component library in uni-app
shell脚本 -d 是目录文件,那么-e,-f等说明
银河麒麟V10 sp1服务器安装英伟达显卡驱动
浅析瀑布流布局原理及实现方式
接口报错no message avaliable
【云原生】3.3 Kubernetes 中间件部署实战
零样本学习&Domain-aware Visual Bias Eliminating for Generalized Zero-Shot Learning
shell之条件语句(test、if、case)
线程中断方法
shell的脚本的基本用法
Automatic translation software - batch batch automatic translation software recommendation
第十七章:回溯探求指定入口的马步遍历,贪心无回溯探求马步遍历,递归探求nxm棋盘带障碍马步遍历
Analysis of the implementation principle and detailed knowledge of v-model syntactic sugar and how to make the components you develop support v-model