|
|
@@ -11,6 +11,7 @@ import com.ylx.common.utils.file.FileUtils;
|
|
|
import com.ylx.framework.config.ServerConfig;
|
|
|
import com.ylx.massage.domain.TbFile;
|
|
|
import com.ylx.massage.mapper.TbFileMapper;
|
|
|
+import com.ylx.massage.utils.MimeTypeUtil;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
|
@@ -116,7 +117,7 @@ public class TbFileServiceImpl extends ServiceImpl<TbFileMapper, TbFile> impleme
|
|
|
TbFile dbFile = this.getByMd5(md5);
|
|
|
if (null != dbFile) {
|
|
|
// 文件已存在,直接返回已有文件的访问信息(秒传)
|
|
|
- ajax.put("url", dbFile.getFileUrl());
|
|
|
+ ajax.put("url", dbFile.getFileUrl());
|
|
|
ajax.put("newFileName", FileUtils.getName(dbFile.getFileUrl()));
|
|
|
ajax.put("originalFilename", dbFile.getFileName());
|
|
|
// 封面图URL路径(针对视频文件)
|
|
|
@@ -130,7 +131,7 @@ public class TbFileServiceImpl extends ServiceImpl<TbFileMapper, TbFile> impleme
|
|
|
// 上传文件到服务器并返回新的文件名称
|
|
|
String fileName = FileUploadUtils.upload(filePath, file);
|
|
|
//获取新的文件子路径
|
|
|
- String subFileName= fileName.substring(fileName.indexOf("/upload") + 7);
|
|
|
+ String subFileName = fileName.substring(fileName.indexOf("/upload") + 7);
|
|
|
// 构建完整的文件访问路径
|
|
|
subFileName = filePath + subFileName;
|
|
|
log.info("上传文件的路径:{},返回的新文件路径:{}", filePath, subFileName);
|
|
|
@@ -146,7 +147,7 @@ public class TbFileServiceImpl extends ServiceImpl<TbFileMapper, TbFile> impleme
|
|
|
ajax.put("newFileName", FileUtils.getName(fileName));
|
|
|
ajax.put("originalFilename", file.getOriginalFilename());
|
|
|
// 封面图URL路径(针对视频文件)
|
|
|
- ajax.put("coverUrl", substring);
|
|
|
+ ajax.put("coverUrl", substring);
|
|
|
|
|
|
// 保存文件记录到数据库
|
|
|
TbFile tbFile = new TbFile();
|
|
|
@@ -174,11 +175,11 @@ public class TbFileServiceImpl extends ServiceImpl<TbFileMapper, TbFile> impleme
|
|
|
*
|
|
|
* @param file 上传的文件对象
|
|
|
* @return AjaxResult 包含文件访问信息的对象:
|
|
|
- * - url: 文件的完整访问 URL(包含域名)
|
|
|
- * - fileName: 服务器存储的文件路径
|
|
|
- * - newFileName: 文件名(不含路径)
|
|
|
- * - originalFilename: 原始上传文件名
|
|
|
- * 如果上传失败,返回错误信息
|
|
|
+ * - url: 文件的完整访问 URL(包含域名)
|
|
|
+ * - fileName: 服务器存储的文件路径
|
|
|
+ * - newFileName: 文件名(不含路径)
|
|
|
+ * - originalFilename: 原始上传文件名
|
|
|
+ * 如果上传失败,返回错误信息
|
|
|
*/
|
|
|
@Override
|
|
|
public AjaxResult uploadFile(MultipartFile file) {
|
|
|
@@ -190,7 +191,7 @@ public class TbFileServiceImpl extends ServiceImpl<TbFileMapper, TbFile> impleme
|
|
|
TbFile dbFile = this.getByMd5(md5);
|
|
|
if (null != dbFile) {
|
|
|
// 文件已存在,直接返回已有文件的访问信息(秒传)
|
|
|
- ajax.put("url", dbFile.getFileUrl());
|
|
|
+ ajax.put("url", dbFile.getFileUrl());
|
|
|
ajax.put("fileName", dbFile.getFileUrl());
|
|
|
ajax.put("newFileName", FileUtils.getName(dbFile.getFileUrl()));
|
|
|
ajax.put("originalFilename", dbFile.getFileName());
|
|
|
@@ -204,7 +205,7 @@ public class TbFileServiceImpl extends ServiceImpl<TbFileMapper, TbFile> impleme
|
|
|
String fileName = FileUploadUtils.upload(filePath, file);
|
|
|
log.info("上传文件到服务器并返回新的文件名称:{}", fileName);
|
|
|
//获取新的文件子路径
|
|
|
- String subFileName= fileName.substring(fileName.indexOf("/upload") + 7);
|
|
|
+ String subFileName = fileName.substring(fileName.indexOf("/upload") + 7);
|
|
|
// 构建完整的文件访问路径
|
|
|
subFileName = filePath + subFileName;
|
|
|
log.info("上传文件路径:{},返回的新文件路径:{}", filePath, subFileName);
|
|
|
@@ -237,10 +238,10 @@ public class TbFileServiceImpl extends ServiceImpl<TbFileMapper, TbFile> impleme
|
|
|
* 4. 验证生成的图片文件有效性
|
|
|
* </p>
|
|
|
*
|
|
|
- * @param videoPath 视频文件路径
|
|
|
+ * @param videoPath 视频文件路径
|
|
|
* @param originalFilename 原始视频文件名(用于生成缩略图文件名)
|
|
|
* @return String 生成的封面图片文件绝对路径
|
|
|
- * @throws IOException 如果目录创建失败
|
|
|
+ * @throws IOException 如果目录创建失败
|
|
|
* @throws VideoThumbnailService.VideoProcessException 如果封面生成失败或验证失败
|
|
|
*/
|
|
|
private String generateThumbnailFilePath(String videoPath, String originalFilename) throws IOException, VideoThumbnailService.VideoProcessException {
|
|
|
@@ -287,14 +288,12 @@ public class TbFileServiceImpl extends ServiceImpl<TbFileMapper, TbFile> impleme
|
|
|
if (hostPath == null || hostPath.isEmpty()) {
|
|
|
return hostPath;
|
|
|
}
|
|
|
+
|
|
|
// 规范化路径分隔符(Windows风格转Linux风格)
|
|
|
String normalizedPath = hostPath.replace("\\", "/");
|
|
|
// 移除可能存在的重复斜杠
|
|
|
normalizedPath = normalizedPath.replaceAll("/+", "/");
|
|
|
|
|
|
- //添加指定的前缀
|
|
|
- normalizedPath = "/usr/local/java" + normalizedPath;
|
|
|
-
|
|
|
// 获取配置的宿主机映射路径(规范化)
|
|
|
String normalizedVolumePath = hostVolumePath.replace("\\", "/").replaceAll("/+", "/");
|
|
|
if (!normalizedVolumePath.endsWith("/")) {
|
|
|
@@ -306,6 +305,7 @@ public class TbFileServiceImpl extends ServiceImpl<TbFileMapper, TbFile> impleme
|
|
|
String relativePath = normalizedPath.substring(normalizedVolumePath.length());
|
|
|
return containerWorkDir + "/" + relativePath;
|
|
|
}
|
|
|
+
|
|
|
// 如果路径不匹配,直接返回原路径
|
|
|
log.warn("路径 {} 不在Docker卷映射路径 {} 下,使用原路径", hostPath, hostVolumePath);
|
|
|
return normalizedPath;
|
|
|
@@ -334,69 +334,30 @@ public class TbFileServiceImpl extends ServiceImpl<TbFileMapper, TbFile> impleme
|
|
|
* 2. Docker模式:通过docker run命令调用容器内的FFmpeg
|
|
|
* </p>
|
|
|
*
|
|
|
- * @param videoPath 视频文件路径(宿主机路径)
|
|
|
+ * @param videoPath 视频文件路径(宿主机路径)
|
|
|
* @param thumbnailPath 封面图片保存路径(宿主机路径)
|
|
|
*/
|
|
|
private void executeFfmpegCommand(String videoPath, String thumbnailPath) throws IOException, InterruptedException, VideoThumbnailService.VideoProcessException {
|
|
|
- log.info("开始执行FFmpeg命令,Docker模式: {}, 视频文件路径: {}, 封面图片保存路径: {}", dockerEnabled, videoPath, thumbnailPath);
|
|
|
+ log.info("开始执行FFmpeg命令,视频文件路径: {}, 封面图片保存路径: {}", videoPath, thumbnailPath);
|
|
|
List<String> command = new ArrayList<>();
|
|
|
- if (dockerEnabled) {
|
|
|
- // Docker模式:构建 docker run 命令
|
|
|
- // 命令格式:docker run --rm -v /host/path:/data -w /data jrottenberg/ffmpeg -ss 1 -i /data/input.mp4 -vframes 1 -q:v 2 -y /data/output.jpg
|
|
|
- command.add("docker");
|
|
|
- command.add("run");
|
|
|
- command.add("--rm");
|
|
|
-
|
|
|
- // 添加volume映射:将宿主机路径映射到容器内
|
|
|
- command.add("-v");
|
|
|
- // 规范化路径(处理Windows风格的路径分隔符)
|
|
|
- String normalizedVolumePath = hostVolumePath.replace("\\", "/");
|
|
|
- if (!normalizedVolumePath.startsWith("/")) {
|
|
|
- // Windows路径需要特殊处理,转换为 /host 形式或使用完整路径
|
|
|
- normalizedVolumePath = "/" + normalizedVolumePath.replace(":", "").replace("\\", "/");
|
|
|
- }
|
|
|
- String volumeMapping = normalizedVolumePath + ":" + containerWorkDir;
|
|
|
- command.add(volumeMapping);
|
|
|
-
|
|
|
- // 设置工作目录
|
|
|
- command.add("-w");
|
|
|
- command.add(containerWorkDir);
|
|
|
-
|
|
|
- // Docker镜像名称
|
|
|
- command.add(dockerImage);
|
|
|
-
|
|
|
- // 将宿主机路径转换为容器内路径
|
|
|
- String containerVideoPath = convertToContainerPath(videoPath);
|
|
|
- String containerThumbnailPath = convertToContainerPath(thumbnailPath);
|
|
|
-
|
|
|
- // FFmpeg参数
|
|
|
- command.add("-ss");
|
|
|
- command.add(String.valueOf(CAPTURE_TIME_SECOND));
|
|
|
- command.add("-i");
|
|
|
- command.add(containerVideoPath);
|
|
|
- command.add("-vframes");
|
|
|
- command.add("1");
|
|
|
- command.add("-q:v");
|
|
|
- command.add("2");
|
|
|
- command.add("-y");
|
|
|
- command.add(containerThumbnailPath);
|
|
|
- } else {
|
|
|
- // 本地模式:直接调用FFmpeg可执行文件
|
|
|
- command.add(FFMPEG_PATH);
|
|
|
- command.add("-ss");
|
|
|
- command.add(String.valueOf(CAPTURE_TIME_SECOND));
|
|
|
- command.add("-i");
|
|
|
- command.add(videoPath);
|
|
|
- command.add("-vframes");
|
|
|
- command.add("1");
|
|
|
- command.add("-q:v");
|
|
|
- command.add("2");
|
|
|
- command.add("-y");
|
|
|
- command.add(thumbnailPath);
|
|
|
- }
|
|
|
+ //直接调用FFmpeg可执行文件
|
|
|
+ command.add(FFMPEG_PATH);
|
|
|
+ command.add("-ss");
|
|
|
+ command.add(String.valueOf(CAPTURE_TIME_SECOND));
|
|
|
+ command.add("-i");
|
|
|
+ command.add(videoPath);
|
|
|
+ command.add("-vframes");
|
|
|
+ command.add("1");
|
|
|
+ command.add("-q:v");
|
|
|
+ command.add("2");
|
|
|
+ command.add("-y");
|
|
|
+ command.add(thumbnailPath);
|
|
|
log.info("执行命令: {}", String.join(" ", command));
|
|
|
+
|
|
|
// 执行命令
|
|
|
ProcessBuilder processBuilder = new ProcessBuilder(command);
|
|
|
+ // 设置工作目录
|
|
|
+ processBuilder.directory(new File("/nightFragrance/uploadPath"));
|
|
|
processBuilder.redirectErrorStream(true);
|
|
|
Process process = processBuilder.start();
|
|
|
// 读取命令输出(用于日志和调试)
|
|
|
@@ -434,11 +395,12 @@ public class TbFileServiceImpl extends ServiceImpl<TbFileMapper, TbFile> impleme
|
|
|
}
|
|
|
// 可选:验证文件是否为有效的图片
|
|
|
try {
|
|
|
- String mimeType = Files.probeContentType(thumbnailFile.toPath());
|
|
|
+ String mimeType = MimeTypeUtil.getMimeType(thumbnailFile.toPath());
|
|
|
+ log.info("mimeType的值:{}", mimeType);
|
|
|
if (mimeType == null || !mimeType.startsWith("image/")) {
|
|
|
throw new VideoThumbnailService.VideoProcessException("生成的文件不是有效的图片");
|
|
|
}
|
|
|
- } catch (IOException e) {
|
|
|
+ } catch (Exception e) {
|
|
|
e.printStackTrace();
|
|
|
log.info("无法验证文件 MIME 类型", e);
|
|
|
}
|