提交 da209647 作者: 方治民

feat: 优化文件上传的临时文件处理方式

上级 47dbe6cd
...@@ -5,11 +5,11 @@ import cn.hutool.core.io.FileUtil; ...@@ -5,11 +5,11 @@ import cn.hutool.core.io.FileUtil;
import com.yiring.common.config.EnvConfig; import com.yiring.common.config.EnvConfig;
import com.yiring.common.core.Minio; import com.yiring.common.core.Minio;
import com.yiring.common.service.UploadProcessService; import com.yiring.common.service.UploadProcessService;
import com.yiring.common.util.Commons;
import io.minio.ObjectWriteResponse; import io.minio.ObjectWriteResponse;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
...@@ -33,6 +33,7 @@ import org.apache.pdfbox.rendering.PDFRenderer; ...@@ -33,6 +33,7 @@ import org.apache.pdfbox.rendering.PDFRenderer;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
/** /**
* @author Jim * @author Jim
...@@ -53,24 +54,26 @@ public class UploadProcessServiceImpl implements UploadProcessService { ...@@ -53,24 +54,26 @@ public class UploadProcessServiceImpl implements UploadProcessService {
@SneakyThrows @SneakyThrows
@Override @Override
public String handle(String object, Path path) { public String handle(String object, MultipartFile file) {
@Cleanup
FileInputStream is = new FileInputStream(path.toFile());
String suffix = FileUtil.getSuffix(object); String suffix = FileUtil.getSuffix(object);
// Image: 在文件名上追加图片物理像素 // Image: 在文件名上追加图片物理像素
if (isSupportiveImage(suffix)) { if (isSupportiveImage(suffix)) {
@Cleanup
InputStream is = file.getInputStream();
object = handleImage(object, is); object = handleImage(object, is);
} }
// PDF: 在文件名上追加页数,同时在同目录生成 PDF 每一页的图片 // PDF: 在文件名上追加页数,同时在同目录生成 PDF 每一页的图片
if (isPdf(suffix)) { if (isPdf(suffix)) {
@Cleanup
InputStream is = file.getInputStream();
object = handlePdf(object, is); object = handlePdf(object, is);
} }
// Video/Audio: 在文件名上追加时长,视频生成封面图 // Video/Audio: 在文件名上追加时长,视频生成封面图
if (isSupportiveMedia(suffix)) { if (isSupportiveMedia(suffix)) {
object = handleMedia(object, suffix, path); object = handleMedia(object, suffix, file);
} }
return object; return object;
...@@ -84,6 +87,7 @@ public class UploadProcessServiceImpl implements UploadProcessService { ...@@ -84,6 +87,7 @@ public class UploadProcessServiceImpl implements UploadProcessService {
@SneakyThrows @SneakyThrows
public String handlePdf(String object, InputStream is) { public String handlePdf(String object, InputStream is) {
@Cleanup
PDDocument doc = PDDocument.load(is); PDDocument doc = PDDocument.load(is);
int pages = doc.getNumberOfPages(); int pages = doc.getNumberOfPages();
...@@ -107,7 +111,7 @@ public class UploadProcessServiceImpl implements UploadProcessService { ...@@ -107,7 +111,7 @@ public class UploadProcessServiceImpl implements UploadProcessService {
} }
@SneakyThrows @SneakyThrows
public String handleMedia(String object, String suffix, Path path) { public String handleMedia(String object, String suffix, MultipartFile file) {
// 判断是否配置 ffmpeg 环境 // 判断是否配置 ffmpeg 环境
FFmpeg ffmpeg = new FFmpeg(); FFmpeg ffmpeg = new FFmpeg();
FFprobe ffprobe = new FFprobe(); FFprobe ffprobe = new FFprobe();
...@@ -115,8 +119,13 @@ public class UploadProcessServiceImpl implements UploadProcessService { ...@@ -115,8 +119,13 @@ public class UploadProcessServiceImpl implements UploadProcessService {
return object; return object;
} }
// 将上传的文件转存一份到本地临时目录
Path tempFile = Paths.get(FileUtil.getTmpDirPath(), "T_" + Commons.uuid(), file.getOriginalFilename());
FileUtil.mkParentDirs(tempFile);
file.transferTo(tempFile);
// 解析媒体文件 // 解析媒体文件
FFmpegProbeResult probeResult = ffprobe.probe(path.toString()); FFmpegProbeResult probeResult = ffprobe.probe(tempFile.toString());
FFmpegFormat format = probeResult.getFormat(); FFmpegFormat format = probeResult.getFormat();
// 构建具有时长(秒)标记的存储地址 // 构建具有时长(秒)标记的存储地址
...@@ -126,33 +135,35 @@ public class UploadProcessServiceImpl implements UploadProcessService { ...@@ -126,33 +135,35 @@ public class UploadProcessServiceImpl implements UploadProcessService {
// 视频截取首帧可见画面作为封面 // 视频截取首帧可见画面作为封面
if (isSupportiveVideo(suffix)) { if (isSupportiveVideo(suffix)) {
// 使用 ffmpeg 截取视频首帧图片 // 使用 ffmpeg 截取视频首帧图片
Path tempFile = Paths.get(path.getParent().toString(), FileUtil.getName(filepath) + ".jpg"); Path path = Paths.get(tempFile.getParent().toString(), FileUtil.getName(filepath) + ".jpg");
FFmpegBuilder builder = new FFmpegBuilder() FFmpegBuilder builder = new FFmpegBuilder()
.setInput(path.toString()) .setInput(tempFile.toString())
.overrideOutputFiles(true) .overrideOutputFiles(true)
.addOutput(tempFile.toString()) .addOutput(path.toString())
.setFrames(1) .setFrames(1)
.done(); .done();
FFmpegExecutor executor = new FFmpegExecutor(ffmpeg, ffprobe); FFmpegExecutor executor = new FFmpegExecutor(ffmpeg, ffprobe);
executor.createJob(builder).run(); executor.createJob(builder).run();
// 判断封面图是否生成 // 判断封面图是否生成
if (Files.exists(tempFile)) { if (Files.exists(path)) {
// 上传封面图 // 上传封面图
minio.putObject(tempFile.toFile(), getObjectFolder(filepath)); minio.putObject(path.toFile(), getObjectFolder(filepath));
} }
// 大视频文件切片上传(> 5s) // 大视频文件切片上传(> 5s)
if (sec > 5) { if (sec > 5) {
filepath = handleToHls(filepath, path, ffmpeg, ffprobe); filepath = handleVideoToHls(filepath, tempFile, ffmpeg, ffprobe);
} }
} }
// 删除为本次上传进行本地处理所创建的整个临时文件夹目录
FileUtil.del(tempFile.getParent());
return filepath; return filepath;
} }
@SneakyThrows @SneakyThrows
public String handleToHls(String object, Path path, FFmpeg ffmpeg, FFprobe ffprobe) { public String handleVideoToHls(String object, Path path, FFmpeg ffmpeg, FFprobe ffprobe) {
String originName = FileUtil.getName(object); String originName = FileUtil.getName(object);
String objectFolder = object.replace("/" + originName, ""); String objectFolder = object.replace("/" + originName, "");
String targetName = originName.replaceAll("^(.*)\\." + FileUtil.getSuffix(originName) + "$", "$1.m3u8"); String targetName = originName.replaceAll("^(.*)\\." + FileUtil.getSuffix(originName) + "$", "$1.m3u8");
......
/* (C) 2022 YiRing, Inc. */ /* (C) 2022 YiRing, Inc. */
package com.yiring.common.service; package com.yiring.common.service;
import java.nio.file.Path; import org.springframework.web.multipart.MultipartFile;
/** /**
* 文件上传媒体文件预处理服务 * 文件上传媒体文件预处理服务
...@@ -19,10 +19,10 @@ public interface UploadProcessService { ...@@ -19,10 +19,10 @@ public interface UploadProcessService {
* 视频封面:haha.mp4 -> haha.mp4.jpg 截取视频封面 * 视频封面:haha.mp4 -> haha.mp4.jpg 截取视频封面
* *
* @param object 上传文件存储地址 * @param object 上传文件存储地址
* @param path 文件地址 * @param file 上传的文件
* @return 预处理后的文件地址(可能对文件名追加了时长、页数、分辨率等标识) * @return 预处理后的文件地址(可能对文件名追加了时长、页数、分辨率等标识)
*/ */
default String handle(String object, Path path) { default String handle(String object, MultipartFile file) {
return object; return object;
} }
} }
/* (C) 2023 YiRing, Inc. */ /* (C) 2023 YiRing, Inc. */
package com.yiring.common.service.impl; package com.yiring.common.service.impl;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.yiring.common.core.Minio; import com.yiring.common.core.Minio;
import com.yiring.common.service.FileManageService; import com.yiring.common.service.FileManageService;
import com.yiring.common.service.UploadProcessService; import com.yiring.common.service.UploadProcessService;
import com.yiring.common.util.Commons;
import java.nio.file.Path;
import java.nio.file.Paths;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
...@@ -43,14 +39,8 @@ public class FileManageServiceImpl implements FileManageService { ...@@ -43,14 +39,8 @@ public class FileManageServiceImpl implements FileManageService {
String uuid = IdUtil.fastSimpleUUID(); String uuid = IdUtil.fastSimpleUUID();
String object = minio.buildUploadPath(filename, "", uuid); String object = minio.buildUploadPath(filename, "", uuid);
// 将上传的文件转存一份到本地临时目录
Path tempFile = Paths.get(FileUtil.getTmpDirPath(), Commons.uuid(), filename);
FileUtil.mkParentDirs(tempFile);
file.transferTo(tempFile);
// 预处理(默认不做任何处理,具体逻辑需自行在外部实现) // 预处理(默认不做任何处理,具体逻辑需自行在外部实现)
object = service.handle(object, tempFile); object = service.handle(object, file);
// 删除为本次上传进行本地处理所创建的整个临时文件夹目录
FileUtil.del(tempFile.getParent());
// 上传原文件(如果是转换成了 m3u8 hls 文件则不保存原文件) // 上传原文件(如果是转换成了 m3u8 hls 文件则不保存原文件)
String suffix = ".m3u8"; String suffix = ".m3u8";
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论