|
|
@@ -12,6 +12,8 @@ import java.io.IOException;
|
|
|
import java.nio.file.Files;
|
|
|
import java.nio.file.Path;
|
|
|
import java.nio.file.Paths;
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.List;
|
|
|
import java.util.UUID;
|
|
|
|
|
|
@Service
|
|
|
@@ -19,45 +21,76 @@ public class FileStorageService {
|
|
|
|
|
|
private static final Logger log = LoggerFactory.getLogger(FileStorageService.class);
|
|
|
|
|
|
- @Value("${file.upload-dir:./uploads}")
|
|
|
- private String uploadDir;
|
|
|
+ @Value("${upload.base-dir:/data/cfc-uploads}")
|
|
|
+ private String baseDir;
|
|
|
|
|
|
+ @Value("${upload.allowed-types:image/jpeg,image/png,image/gif,image/webp}")
|
|
|
+ private String allowedTypesStr;
|
|
|
+
|
|
|
+ private List<String> allowedTypes;
|
|
|
private Path uploadPath;
|
|
|
|
|
|
@PostConstruct
|
|
|
public void init() {
|
|
|
- uploadPath = Paths.get(uploadDir).toAbsolutePath().normalize();
|
|
|
+ allowedTypes = Arrays.asList(allowedTypesStr.split(","));
|
|
|
+ uploadPath = Paths.get(baseDir).toAbsolutePath().normalize();
|
|
|
try {
|
|
|
Files.createDirectories(uploadPath);
|
|
|
} catch (IOException e) {
|
|
|
log.error("初始化上传目录失败: {}", uploadPath, e);
|
|
|
}
|
|
|
+ log.info("FileStorageService init: baseDir={}, allowedTypes={}", uploadPath, allowedTypes);
|
|
|
}
|
|
|
|
|
|
- public String upload(MultipartFile file, String subDir) {
|
|
|
+ public String store(MultipartFile file, String subDir) {
|
|
|
if (file == null || file.isEmpty()) {
|
|
|
- return null;
|
|
|
+ throw new IllegalArgumentException("文件为空");
|
|
|
}
|
|
|
- try {
|
|
|
- String originalName = file.getOriginalFilename();
|
|
|
- String ext = "";
|
|
|
- if (originalName != null && originalName.contains(".")) {
|
|
|
- ext = originalName.substring(originalName.lastIndexOf("."));
|
|
|
- }
|
|
|
- String filename = UUID.randomUUID().toString().replace("-", "") + ext;
|
|
|
+ String contentType = file.getContentType();
|
|
|
+ if (contentType == null || !allowedTypes.contains(contentType)) {
|
|
|
+ throw new IllegalArgumentException("不支持的文件类型,仅支持: " + allowedTypes);
|
|
|
+ }
|
|
|
+ String originalName = file.getOriginalFilename();
|
|
|
+ String ext = "";
|
|
|
+ if (originalName != null && originalName.contains(".")) {
|
|
|
+ ext = originalName.substring(originalName.lastIndexOf("."));
|
|
|
+ }
|
|
|
+ String filename = UUID.randomUUID().toString().replace("-", "") + ext;
|
|
|
|
|
|
- Path targetDir = uploadPath.resolve(subDir != null ? subDir : "general");
|
|
|
+ Path targetDir = uploadPath.resolve(subDir != null ? subDir : "general");
|
|
|
+ try {
|
|
|
Files.createDirectories(targetDir);
|
|
|
+ } catch (IOException e) {
|
|
|
+ throw new RuntimeException("创建上传目录失败: " + targetDir, e);
|
|
|
+ }
|
|
|
|
|
|
- Path targetPath = targetDir.resolve(filename);
|
|
|
+ Path targetPath = targetDir.resolve(filename);
|
|
|
+ try {
|
|
|
file.transferTo(targetPath.toFile());
|
|
|
-
|
|
|
- String relativePath = (subDir != null ? "/" + subDir : "") + "/" + filename;
|
|
|
- log.info("文件已保存: {}", relativePath);
|
|
|
- return relativePath;
|
|
|
} catch (IOException e) {
|
|
|
- log.error("文件上传失败", e);
|
|
|
- return null;
|
|
|
+ throw new RuntimeException("文件保存失败", e);
|
|
|
+ }
|
|
|
+
|
|
|
+ String url = "/uploads/" + (subDir != null ? subDir + "/" : "") + filename;
|
|
|
+ log.info("文件已保存: original={}, url={}", originalName, url);
|
|
|
+ return url;
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean delete(String url) {
|
|
|
+ if (url == null || url.isEmpty() || !url.startsWith("/uploads/")) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ String relativePath = url.replace("/uploads/", "");
|
|
|
+ Path filePath = uploadPath.resolve(relativePath);
|
|
|
+ if (Files.exists(filePath)) {
|
|
|
+ try {
|
|
|
+ Files.delete(filePath);
|
|
|
+ log.info("文件已删除: {}", url);
|
|
|
+ return true;
|
|
|
+ } catch (IOException e) {
|
|
|
+ log.error("文件删除失败: {}", url, e);
|
|
|
+ }
|
|
|
}
|
|
|
+ return false;
|
|
|
}
|
|
|
-}
|
|
|
+}
|