diff --git a/file-service/src/main/java/com/xuqm/file/service/FileStorageService.java b/file-service/src/main/java/com/xuqm/file/service/FileStorageService.java index 489c3b6..12aea04 100644 --- a/file-service/src/main/java/com/xuqm/file/service/FileStorageService.java +++ b/file-service/src/main/java/com/xuqm/file/service/FileStorageService.java @@ -14,11 +14,14 @@ import org.springframework.web.multipart.MultipartFile; import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; -import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.security.DigestInputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.time.Instant; @@ -62,39 +65,49 @@ public class FileStorageService { throw new BusinessException(400, "File must not be empty"); } - byte[] bytes; + String originalName = file.getOriginalFilename(); + String mimeType = resolveMimeType(file); + String ext = resolveExtension(originalName, mimeType); + + Path storageDir = Paths.get(uploadDir); + ensureDirectory(storageDir); + + // Stream to a temp file while computing SHA-256 to avoid loading the whole file into heap + Path tempPath = storageDir.resolve("tmp-" + UUID.randomUUID() + ".tmp"); + String hash; + long fileSize; try { - bytes = file.getBytes(); - } catch (IOException e) { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + try (InputStream in = new DigestInputStream(file.getInputStream(), digest); + OutputStream out = Files.newOutputStream(tempPath)) { + fileSize = in.transferTo(out); + } + hash = HexFormat.of().formatHex(digest.digest()); + } catch (IOException | NoSuchAlgorithmException e) { + try { Files.deleteIfExists(tempPath); } catch (IOException ignored) {} throw new BusinessException(500, "Failed to read uploaded file"); } - String hash = sha256Hex(bytes); - String mimeType = resolveMimeType(file); - String originalName = file.getOriginalFilename(); - Optional existing = fileRepository.findByHash(hash); if (existing.isPresent()) { FileEntity entity = existing.get(); - // 检查磁盘文件是否仍然存在,若不存在则重新写入 if (Files.exists(Paths.get(entity.getStoragePath()))) { + // File already on disk — drop temp and return existing record + try { Files.deleteIfExists(tempPath); } catch (IOException ignored) {} entity.setLastAccessedAt(Instant.now()); fileRepository.save(entity); return toUploadResult(entity); } - // 文件记录存在但磁盘文件已丢失,删除旧记录后重新上传 + // 文件记录存在但磁盘文件已丢失,删除旧记录,保留 temp 继续写入 fileRepository.delete(entity); fileRepository.flush(); } - String ext = resolveExtension(originalName, mimeType); - Path storageDir = Paths.get(uploadDir); - ensureDirectory(storageDir); - Path storagePath = storageDir.resolve(hash + ext); try { - Files.write(storagePath, bytes); + Files.move(tempPath, storagePath, StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { + try { Files.deleteIfExists(tempPath); } catch (IOException ignored) {} throw new BusinessException(500, "Failed to save uploaded file"); } @@ -111,7 +124,7 @@ public class FileStorageService { } } } else if (mimeType != null && mimeType.startsWith("image/")) { - thumbnailStoragePath = generateImageThumbnail(bytes, hash); + thumbnailStoragePath = generateImageThumbnail(storagePath, hash); if (thumbnailStoragePath != null) { try { thumbnailSize = Files.size(Paths.get(thumbnailStoragePath)); @@ -127,7 +140,7 @@ public class FileStorageService { entity.setHash(hash); entity.setOriginalName(originalName); entity.setMimeType(mimeType); - entity.setSize(bytes.length); + entity.setSize(fileSize); entity.setExt(ext); entity.setStoragePath(storagePath.toAbsolutePath().toString()); entity.setThumbnailPath(thumbnailStoragePath); @@ -193,9 +206,9 @@ public class FileStorageService { } } - private String generateImageThumbnail(byte[] imageBytes, String hash) { + private String generateImageThumbnail(Path imagePath, String hash) { try { - BufferedImage original = ImageIO.read(new ByteArrayInputStream(imageBytes)); + BufferedImage original = ImageIO.read(imagePath.toFile()); if (original == null) { return null; } @@ -230,16 +243,6 @@ public class FileStorageService { } } - private String sha256Hex(byte[] data) { - try { - MessageDigest digest = MessageDigest.getInstance("SHA-256"); - byte[] hashBytes = digest.digest(data); - return HexFormat.of().formatHex(hashBytes); - } catch (NoSuchAlgorithmException e) { - throw new IllegalStateException("SHA-256 not available", e); - } - } - private String resolveMimeType(MultipartFile file) { String ct = file.getContentType(); if (ct != null && !ct.isBlank() && !ct.equals("application/octet-stream")) {