|
|
@@ -5,6 +5,7 @@ import lombok.RequiredArgsConstructor;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
|
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
|
|
+import org.springframework.scheduling.annotation.Async;
|
|
|
import org.springframework.stereotype.Component;
|
|
|
import org.springframework.web.multipart.MultipartFile;
|
|
|
import org.springframework.web.multipart.MultipartRequest;
|
|
|
@@ -16,10 +17,13 @@ import shop.alien.util.common.VideoUtils;
|
|
|
import shop.alien.util.file.FileUtil;
|
|
|
|
|
|
import java.io.File;
|
|
|
+import java.io.InputStream;
|
|
|
import java.nio.file.Files;
|
|
|
import java.nio.file.Path;
|
|
|
import java.nio.file.Paths;
|
|
|
import java.util.*;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+import java.util.stream.Stream;
|
|
|
|
|
|
/**
|
|
|
* 二期-文件上传
|
|
|
@@ -124,77 +128,143 @@ public class FileUploadUtil {
|
|
|
* @return List<String>
|
|
|
*/
|
|
|
public List<String> uploadMoreFile(MultipartRequest multipartRequest) {
|
|
|
+ long startTime = System.currentTimeMillis();
|
|
|
try {
|
|
|
log.info("FileUpload.uploadMoreFile multipartRequest={}", multipartRequest.getFileNames());
|
|
|
- Set<String> fileNameSet = multipartRequest.getMultiFileMap().keySet();
|
|
|
- List<String> filePathList = new ArrayList<>();
|
|
|
- for (String s : fileNameSet) {
|
|
|
- MultipartFile multipartFile = multipartRequest.getFileMap().get(s);
|
|
|
- log.info("FileUpload.uploadMoreFile fileName={}", multipartFile.getOriginalFilename());
|
|
|
- String uploadDir = this.uploadDir.replace("file:///", "").replace("\\", "/");
|
|
|
- String prefix;
|
|
|
- Map<String, String> fileNameAndType = FileUtil.getFileNameAndType(multipartFile);
|
|
|
- //区分文件类型
|
|
|
- if (imageFileType.contains(fileNameAndType.get("type").toLowerCase())) {
|
|
|
- uploadDir += "/image";
|
|
|
- prefix = "image/";
|
|
|
- log.info("FileUpload.uploadMoreFile 获取到图片文件准备复制 {} {} {}", uploadDir, prefix, multipartFile.getOriginalFilename());
|
|
|
- // 去除文件名中的逗号,避免URL拼接时被错误分割
|
|
|
- String imageFileName = fileNameAndType.get("name").replaceAll(",", "");
|
|
|
- filePathList.add(aliOSSUtil.uploadFile(multipartFile, prefix + imageFileName + RandomCreateUtil.getRandomNum(6) + "." + fileNameAndType.get("type")));
|
|
|
- ;
|
|
|
- } else if (videoFileType.contains(fileNameAndType.get("type").toLowerCase())) {
|
|
|
- uploadDir += "/video/";
|
|
|
- prefix = "video/";
|
|
|
- //上传视频文件
|
|
|
- log.info("FileUpload.uploadMoreFile 获取到视频文件准备复制 {} {} {}", uploadDir, prefix, multipartFile.getOriginalFilename());
|
|
|
- // 去除文件名中的逗号,避免URL拼接时被错误分割
|
|
|
- String videoFileName = fileNameAndType.get("name").replaceAll(",", "") + RandomCreateUtil.getRandomNum(6);
|
|
|
- String cacheVideoPath = copyFile(uploadDir, multipartFile);
|
|
|
- filePathList.add(aliOSSUtil.uploadFile(multipartFile, prefix + videoFileName + "." + fileNameAndType.get("type")));
|
|
|
- //缓存视频截图使用
|
|
|
-// File videoFile = new File(cacheVideoPath);
|
|
|
-// //获取视频某帧截图
|
|
|
-// log.info("FileUpload.uploadMoreFile 视频文件复制完毕, 获取第一秒图片 {}", videoFile.getName());
|
|
|
-// String videoPath = videoUtils.getImg(uploadDir + videoFile.getName());
|
|
|
-// log.info("FileUpload.uploadMoreFile 视频文件复制完毕, 图片位置 {}", videoPath);
|
|
|
-// if (!videoPath.isEmpty()) {
|
|
|
-// File videoImgFile = new File(videoPath);
|
|
|
-// Map<String, String> videoImg = FileUtil.getFileNameAndType(videoImgFile);
|
|
|
-// filePathList.add(aliOSSUtil.uploadFile(videoImgFile, prefix + videoFileName + "." + videoImg.get("type")));
|
|
|
-// videoImgFile.delete();
|
|
|
-// videoFile.delete();
|
|
|
-// } else {
|
|
|
-// throw new RuntimeException("视频截图失败");
|
|
|
-// }
|
|
|
- } else if (voiceFileType.contains(fileNameAndType.get("type").toLowerCase())) {
|
|
|
- uploadDir += "/voice";
|
|
|
- prefix = "voice/";
|
|
|
- log.info("FileUpload.uploadMoreFile 获取到语音文件准备复制 {} {} {}", uploadDir, prefix, multipartFile.getOriginalFilename());
|
|
|
- // 去除文件名中的逗号,避免URL拼接时被错误分割
|
|
|
- String voiceFileName = fileNameAndType.get("name").replaceAll(",", "");
|
|
|
- filePathList.add(aliOSSUtil.uploadFile(multipartFile, prefix + voiceFileName + RandomCreateUtil.getRandomNum(6) + "." + fileNameAndType.get("type")));
|
|
|
- } else if (privacyFileType.contains(fileNameAndType.get("type").toLowerCase())) {
|
|
|
- uploadDir += "/privacy/";
|
|
|
- prefix = "privacy/";
|
|
|
- log.info("FileUpload.uploadMoreFile 获取到隐私文件准备复制 {} {} {}", uploadDir, prefix, multipartFile.getOriginalFilename());
|
|
|
- // 去除文件名中的逗号,避免URL拼接时被错误分割
|
|
|
- String privacyFileName = fileNameAndType.get("name").replaceAll(",", "");
|
|
|
- filePathList.add(aliOSSUtil.uploadFile(multipartFile, prefix + privacyFileName + RandomCreateUtil.getRandomNum(6) + "." + fileNameAndType.get("type")));
|
|
|
- } else if (pdfFileType.contains(fileNameAndType.get("type").toLowerCase())) {
|
|
|
- uploadDir += "/pdf";
|
|
|
- prefix = "pdf/";
|
|
|
- log.info("FileUpload.uploadMoreFile 获取到PDF文件准备复制 {} {} {}", uploadDir, prefix, multipartFile.getOriginalFilename());
|
|
|
- // 去除文件名中的逗号,避免URL拼接时被错误分割
|
|
|
- String pdfFileName = fileNameAndType.get("name").replaceAll(",", "");
|
|
|
- filePathList.add(aliOSSUtil.uploadFile(multipartFile, prefix + pdfFileName + RandomCreateUtil.getRandomNum(6) + "." + fileNameAndType.get("type")));
|
|
|
- } else if (ohterFileType.contains(fileNameAndType.get("type").toLowerCase())) {
|
|
|
- uploadDir += "/other/";
|
|
|
- prefix = "other/";
|
|
|
- log.info("FileUpload.uploadMoreFile 获取到其他文件准备复制 {} {} {}", uploadDir, prefix, multipartFile.getOriginalFilename());
|
|
|
- // 去除文件名中的逗号,避免URL拼接时被错误分割
|
|
|
- String otherFileName = fileNameAndType.get("name").replaceAll(",", "");
|
|
|
- filePathList.add(aliOSSUtil.uploadFile(multipartFile, prefix + otherFileName + RandomCreateUtil.getRandomNum(6) + "." + fileNameAndType.get("type")));
|
|
|
+ Collection<MultipartFile> files = multipartRequest.getFileMap().values();
|
|
|
+
|
|
|
+ if (files.isEmpty()) {
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
+
|
|
|
+ String baseUploadDir = this.uploadDir.replace("file:///", "").replace("\\", "/");
|
|
|
+
|
|
|
+ // 如果只有一个文件,直接处理(避免线程池开销)
|
|
|
+ if (files.size() == 1) {
|
|
|
+ MultipartFile file = files.iterator().next();
|
|
|
+ return processSingleFileForUploadMore(file, baseUploadDir);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 多个文件使用并行流处理
|
|
|
+ List<String> filePathList = files.parallelStream()
|
|
|
+ .flatMap(file -> {
|
|
|
+ try {
|
|
|
+ return processSingleFileForUploadMore(file, baseUploadDir).stream();
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("处理文件失败: {}", file.getOriginalFilename(), e);
|
|
|
+ return Stream.empty();
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ long totalTime = System.currentTimeMillis() - startTime;
|
|
|
+ log.info("批量文件上传完成,文件数: {}, 总耗时: {} ms", files.size(), totalTime);
|
|
|
+
|
|
|
+ return filePathList;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("FileUpload.uploadMoreFile ERROR Msg={}", e.getMessage(), e);
|
|
|
+ throw new RuntimeException("批量文件上传失败: " + e.getMessage(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理单个文件(用于 uploadMoreFile)
|
|
|
+ *
|
|
|
+ * @param multipartFile 文件
|
|
|
+ * @param baseUploadDir 基础上传目录
|
|
|
+ * @return 文件URL列表(视频会包含视频URL和截图URL)
|
|
|
+ */
|
|
|
+ private List<String> processSingleFileForUploadMore(MultipartFile multipartFile, String baseUploadDir) {
|
|
|
+ List<String> filePathList = new ArrayList<>();
|
|
|
+ File tempVideoFile = null;
|
|
|
+
|
|
|
+ try {
|
|
|
+ Map<String, String> fileNameAndType = FileUtil.getFileNameAndType(multipartFile);
|
|
|
+ String fileType = fileNameAndType.get("type").toLowerCase();
|
|
|
+ String prefix;
|
|
|
+ String uploadDir = baseUploadDir;
|
|
|
+
|
|
|
+ // 区分文件类型并处理
|
|
|
+ if (imageFileType.contains(fileType)) {
|
|
|
+ prefix = "image/";
|
|
|
+ String imageFileName = fileNameAndType.get("name").replaceAll(",", "");
|
|
|
+ String ossFilePath = prefix + imageFileName + RandomCreateUtil.getRandomNum(6) + "." + fileNameAndType.get("type");
|
|
|
+ // 直接流式上传
|
|
|
+ try (InputStream inputStream = multipartFile.getInputStream()) {
|
|
|
+ String url = aliOSSUtil.uploadFile(inputStream, ossFilePath);
|
|
|
+ if (url != null) {
|
|
|
+ filePathList.add(url);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ } else if (videoFileType.contains(fileType)) {
|
|
|
+ prefix = "video/";
|
|
|
+ uploadDir += "/video/";
|
|
|
+ String videoFileName = fileNameAndType.get("name").replaceAll(",", "") + RandomCreateUtil.getRandomNum(6);
|
|
|
+ String ossFilePath = prefix + videoFileName + "." + fileNameAndType.get("type");
|
|
|
+
|
|
|
+ // 视频需要截图,先保存文件
|
|
|
+ String tempPath = copyFile(uploadDir, multipartFile);
|
|
|
+ tempVideoFile = new File(tempPath);
|
|
|
+ if (!tempVideoFile.exists() || tempVideoFile.length() == 0) {
|
|
|
+ throw new RuntimeException("视频文件保存失败: " + tempPath);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 从文件上传视频
|
|
|
+ String videoUrl = aliOSSUtil.uploadFile(tempVideoFile, ossFilePath);
|
|
|
+ if (videoUrl != null) {
|
|
|
+ filePathList.add(videoUrl);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 异步处理视频截图
|
|
|
+ processVideoScreenshotAsync(tempVideoFile, videoFileName, prefix, uploadDir);
|
|
|
+
|
|
|
+ } else if (voiceFileType.contains(fileType)) {
|
|
|
+ prefix = "voice/";
|
|
|
+ String voiceFileName = fileNameAndType.get("name").replaceAll(",", "");
|
|
|
+ String ossFilePath = prefix + voiceFileName + RandomCreateUtil.getRandomNum(6) + "." + fileNameAndType.get("type");
|
|
|
+ // 直接流式上传
|
|
|
+ try (InputStream inputStream = multipartFile.getInputStream()) {
|
|
|
+ String url = aliOSSUtil.uploadFile(inputStream, ossFilePath);
|
|
|
+ if (url != null) {
|
|
|
+ filePathList.add(url);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ } else if (privacyFileType.contains(fileType)) {
|
|
|
+ prefix = "privacy/";
|
|
|
+ String privacyFileName = fileNameAndType.get("name").replaceAll(",", "");
|
|
|
+ String ossFilePath = prefix + privacyFileName + RandomCreateUtil.getRandomNum(6) + "." + fileNameAndType.get("type");
|
|
|
+ // 直接流式上传
|
|
|
+ try (InputStream inputStream = multipartFile.getInputStream()) {
|
|
|
+ String url = aliOSSUtil.uploadFile(inputStream, ossFilePath);
|
|
|
+ if (url != null) {
|
|
|
+ filePathList.add(url);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ } else if (pdfFileType.contains(fileType)) {
|
|
|
+ prefix = "pdf/";
|
|
|
+ String pdfFileName = fileNameAndType.get("name").replaceAll(",", "");
|
|
|
+ String ossFilePath = prefix + pdfFileName + RandomCreateUtil.getRandomNum(6) + "." + fileNameAndType.get("type");
|
|
|
+ // 直接流式上传
|
|
|
+ try (InputStream inputStream = multipartFile.getInputStream()) {
|
|
|
+ String url = aliOSSUtil.uploadFile(inputStream, ossFilePath);
|
|
|
+ if (url != null) {
|
|
|
+ filePathList.add(url);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ } else if (ohterFileType.contains(fileType)) {
|
|
|
+ prefix = "other/";
|
|
|
+ String otherFileName = fileNameAndType.get("name").replaceAll(",", "");
|
|
|
+ String ossFilePath = prefix + otherFileName + RandomCreateUtil.getRandomNum(6) + "." + fileNameAndType.get("type");
|
|
|
+ // 直接流式上传
|
|
|
+ try (InputStream inputStream = multipartFile.getInputStream()) {
|
|
|
+ String url = aliOSSUtil.uploadFile(inputStream, ossFilePath);
|
|
|
+ if (url != null) {
|
|
|
+ filePathList.add(url);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
return filePathList;
|
|
|
@@ -205,6 +275,53 @@ public class FileUploadUtil {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
+ * 异步处理视频截图
|
|
|
+ *
|
|
|
+ * @param tempVideoFile 临时视频文件
|
|
|
+ * @param videoFileName 视频文件名(不含扩展名)
|
|
|
+ * @param prefix OSS路径前缀
|
|
|
+ * @param uploadDir 上传目录
|
|
|
+ */
|
|
|
+ @Async
|
|
|
+ protected void processVideoScreenshotAsync(File tempVideoFile, String videoFileName, String prefix, String uploadDir) {
|
|
|
+ try {
|
|
|
+ log.info("开始异步处理视频截图: {}", tempVideoFile.getName());
|
|
|
+
|
|
|
+ // 获取视频截图
|
|
|
+ String screenshotPath = videoUtils.getImg(uploadDir + tempVideoFile.getName());
|
|
|
+
|
|
|
+ if (screenshotPath != null && !screenshotPath.isEmpty()) {
|
|
|
+ File screenshotFile = new File(screenshotPath);
|
|
|
+ if (screenshotFile.exists()) {
|
|
|
+ try {
|
|
|
+ // 上传截图到OSS
|
|
|
+ Map<String, String> screenshotInfo = FileUtil.getFileNameAndType(screenshotFile);
|
|
|
+ String screenshotUrl = aliOSSUtil.uploadFile(screenshotFile, prefix + videoFileName + "." + screenshotInfo.get("type"));
|
|
|
+
|
|
|
+ log.info("视频截图上传成功: {} -> {}", tempVideoFile.getName(), screenshotUrl);
|
|
|
+ } finally {
|
|
|
+ // 清理临时截图文件
|
|
|
+ screenshotFile.delete();
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ log.warn("视频截图文件不存在: {}", screenshotPath);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ log.warn("视频截图失败: {}", tempVideoFile.getName());
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("异步处理视频截图失败: {}", tempVideoFile.getName(), e);
|
|
|
+ } finally {
|
|
|
+ // 清理临时视频文件
|
|
|
+ if (tempVideoFile != null && tempVideoFile.exists()) {
|
|
|
+ tempVideoFile.delete();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
* 上传图片
|
|
|
*
|
|
|
* @param multipartRequest 文件
|