Просмотр исходного кода

Revert "优化上传视频接口,尝试,有问题直接回退,第二次"

This reverts commit 4e2f441e41dc59ece9fd942f2adb45a633bbfa05.
liudongzhi 1 месяц назад
Родитель
Сommit
8d8f0119ab
1 измененных файлов с 47 добавлено и 54 удалено
  1. 47 54
      alien-store/src/main/java/shop/alien/store/util/FileUploadUtil.java

+ 47 - 54
alien-store/src/main/java/shop/alien/store/util/FileUploadUtil.java

@@ -17,7 +17,6 @@ import shop.alien.util.file.FileUtil;
 
 import java.io.File;
 import java.io.FileOutputStream;
-import java.io.IOException;
 import java.io.InputStream;
 import java.nio.channels.Channels;
 import java.nio.channels.FileChannel;
@@ -215,28 +214,16 @@ public class FileUploadUtil {
                 log.info("FileUpload.processSingleFile 获取到视频文件准备上传 {} {}", prefix, multipartFile.getOriginalFilename());
                 String videoFileName = fileNameAndType.get("name").replaceAll(",", "") + RandomCreateUtil.getRandomNum(6);
                 
-                // 重要:必须先保存文件到本地,因为 MultipartFile 的临时文件可能被清理
-                // 在并行处理之前,先确保文件已经保存到本地
-                String cacheVideoPath = copyFileOptimized(uploadDir, multipartFile);
-                File videoFile = new File(cacheVideoPath);
-                
-                if (!videoFile.exists() || videoFile.length() == 0) {
-                    throw new RuntimeException("视频文件复制失败,文件不存在或为空: " + cacheVideoPath);
-                }
-                
-                // 使用本地文件进行上传(避免 MultipartFile 临时文件被清理的问题)
-                File localVideoFile = videoFile;
+                // 优化:并行执行视频上传和文件复制(这两个操作互不依赖,可以并行)
                 CompletableFuture<String> uploadFuture = CompletableFuture.supplyAsync(() -> {
-                    try {
-                        // 使用本地文件上传,而不是 MultipartFile
-                        return aliOSSUtil.uploadFile(localVideoFile, prefix + videoFileName + "." + fileNameAndType.get("type"));
-                    } catch (Exception e) {
-                        log.error("视频上传失败", e);
-                        throw new RuntimeException("视频上传失败: " + e.getMessage(), e);
-                    }
+                    return aliOSSUtil.uploadFile(multipartFile, prefix + videoFileName + "." + fileNameAndType.get("type"));
                 });
                 
-                // 等待上传完成
+                // 复制文件用于截图(使用优化的流式复制)
+                String cacheVideoPath = copyFileOptimized(uploadDir, multipartFile);
+                File videoFile = new File(cacheVideoPath);
+                
+                // 等待上传完成(如果复制已完成,这里不会阻塞太久)
                 String videoUrl = uploadFuture.get();
                 filePathList.add(videoUrl);
                 
@@ -375,8 +362,6 @@ public class FileUploadUtil {
     /**
      * 优化的文件复制方法 - 使用流式复制,避免一次性读取全部字节
      * 对于大文件(如视频),性能提升显著
-     * 
-     * 注意:MultipartFile 的临时文件可能被清理,需要立即读取
      *
      * @param localFilePath 本地路径
      * @param file          文件
@@ -385,6 +370,8 @@ public class FileUploadUtil {
     private String copyFileOptimized(String localFilePath, MultipartFile file) {
         InputStream inputStream = null;
         FileOutputStream outputStream = null;
+        ReadableByteChannel inputChannel = null;
+        FileChannel outputChannel = null;
         
         try {
             File cacheFilePath = new File(localFilePath);
@@ -396,47 +383,53 @@ public class FileUploadUtil {
             Path path = Paths.get(localFilePath, fileName);
             Files.createDirectories(path.getParent());
             
+            // 使用NIO的Channel进行高效复制(零拷贝技术)
             File targetFile = path.toFile();
+            inputStream = file.getInputStream();
+            outputStream = new FileOutputStream(targetFile);
+            
+            // 将InputStream转换为ReadableByteChannel
+            inputChannel = Channels.newChannel(inputStream);
+            outputChannel = outputStream.getChannel();
             
-            // 重要:立即获取 InputStream,因为临时文件可能很快被清理
-            // 使用 transferTo 方法,这是 MultipartFile 推荐的方式
+            // 使用transferFrom进行高效复制,对于大文件性能更好
+            // 对于大文件,分块传输以避免内存问题
+            long transferred = 0;
+            long fileSize = file.getSize();
+            long chunkSize = 8 * 1024 * 1024; // 8MB chunks
+            
+            while (transferred < fileSize) {
+                long remaining = fileSize - transferred;
+                long toTransfer = Math.min(chunkSize, remaining);
+                transferred += outputChannel.transferFrom(inputChannel, transferred, toTransfer);
+            }
+            
+            return localFilePath + fileName;
+        } catch (Exception e) {
+            log.error("FileUpload.copyFileOptimized ERROR Msg={}", e.getMessage(), e);
+            // 降级到传统方式
             try {
-                // 优先使用 transferTo 方法(Spring 5.1+ 支持,性能最好)
-                file.transferTo(targetFile);
-                return localFilePath + fileName;
-            } catch (IllegalStateException | IOException e) {
-                // 如果 transferTo 失败(可能是临时文件已被清理),使用流式复制
-                log.warn("transferTo 失败,使用流式复制: {}", e.getMessage());
-                
-                // 重新获取 InputStream(如果可能)
-                inputStream = file.getInputStream();
-                outputStream = new FileOutputStream(targetFile);
-                
-                // 使用缓冲流进行复制(8KB 缓冲区)
-                byte[] buffer = new byte[8192];
-                int bytesRead;
-                long totalBytes = 0;
-                long fileSize = file.getSize();
-                
-                while ((bytesRead = inputStream.read(buffer)) != -1) {
-                    outputStream.write(buffer, 0, bytesRead);
-                    totalBytes += bytesRead;
-                    
-                    // 如果文件大小已知,可以显示进度
-                    if (fileSize > 0 && totalBytes % (10 * 1024 * 1024) == 0) {
-                        log.debug("文件复制进度: {}/{} bytes", totalBytes, fileSize);
+                Path path = Paths.get(localFilePath, file.getOriginalFilename());
+                Files.createDirectories(path.getParent());
+                // 使用缓冲流进行复制
+                try (InputStream is = file.getInputStream();
+                     FileOutputStream fos = new FileOutputStream(path.toFile())) {
+                    byte[] buffer = new byte[8192];
+                    int bytesRead;
+                    while ((bytesRead = is.read(buffer)) != -1) {
+                        fos.write(buffer, 0, bytesRead);
                     }
                 }
-                
-                outputStream.flush();
-                return localFilePath + fileName;
+                return localFilePath + file.getOriginalFilename();
+            } catch (Exception ex) {
+                log.error("FileUpload.copyFileOptimized 降级方案也失败 Msg={}", ex.getMessage(), ex);
+                throw new RuntimeException("文件复制失败: " + ex.getMessage(), ex);
             }
-        } catch (Exception e) {
-            log.error("FileUpload.copyFileOptimized ERROR Msg={}", e.getMessage(), e);
-            throw new RuntimeException("文件复制失败: " + e.getMessage(), e);
         } finally {
             // 关闭资源
             try {
+                if (inputChannel != null) inputChannel.close();
+                if (outputChannel != null) outputChannel.close();
                 if (inputStream != null) inputStream.close();
                 if (outputStream != null) outputStream.close();
             } catch (Exception e) {