package com.tcm.training.admin.upload;
|
|
import com.tcm.training.admin.common.ApiResponse;
|
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.multipart.MultipartFile;
|
|
import javax.servlet.http.HttpServletRequest;
|
import java.io.File;
|
import java.io.IOException;
|
import java.time.LocalDate;
|
import java.time.format.DateTimeFormatter;
|
import java.util.Arrays;
|
import java.util.HashSet;
|
import java.util.Set;
|
import java.util.UUID;
|
|
@RestController
|
@RequestMapping({"/api/v1", "/api"})
|
public class UploadController {
|
|
private static final long MAX_SIZE = 5 * 1024 * 1024L; // 5MB
|
private static final Set<String> ALLOWED_TYPES = new HashSet<>(
|
Arrays.asList("image/jpeg", "image/png", "image/gif", "image/webp")
|
);
|
|
@Value("${app.upload.dir:uploads}")
|
private String uploadDir;
|
|
@Value("${app.upload.path-prefix:/uploads}")
|
private String pathPrefix;
|
|
/**
|
* 图片上传
|
* POST /api/v1/upload/image
|
* @return { url: "访问地址" }
|
*/
|
@PostMapping("/upload/image")
|
public ApiResponse<UploadResult> uploadImage(
|
@RequestParam("file") MultipartFile file,
|
HttpServletRequest request) throws IOException {
|
|
// 校验文件非空
|
if (file == null || file.isEmpty()) {
|
throw new IllegalArgumentException("请选择要上传的文件");
|
}
|
|
// 校验文件大小
|
if (file.getSize() > MAX_SIZE) {
|
throw new IllegalArgumentException("文件大小不能超过 5MB");
|
}
|
|
// 校验文件类型
|
String contentType = file.getContentType();
|
if (contentType == null || !ALLOWED_TYPES.contains(contentType)) {
|
throw new IllegalArgumentException("仅支持 JPG、PNG、GIF、WebP 格式");
|
}
|
|
// 生成子目录(按日期)和唯一文件名
|
String dateDir = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
|
String originalName = file.getOriginalFilename();
|
String ext = "";
|
if (originalName != null && originalName.contains(".")) {
|
ext = originalName.substring(originalName.lastIndexOf("."));
|
}
|
String fileName = UUID.randomUUID().toString().replace("-", "") + ext;
|
|
// 创建目录并保存文件(强制绝对路径,规避 transferTo 临时文件问题)
|
File dir = new File(uploadDir, dateDir).getAbsoluteFile();
|
if (!dir.exists() && !dir.mkdirs()) {
|
throw new IOException("创建上传目录失败: " + dir.getAbsolutePath());
|
}
|
File dest = new File(dir, fileName);
|
// 用 InputStream 手动拷贝,避免 Spring transferTo 依赖临时文件的问题
|
try (java.io.InputStream in = file.getInputStream();
|
java.io.OutputStream out = new java.io.FileOutputStream(dest)) {
|
byte[] buf = new byte[8192];
|
int n;
|
while ((n = in.read(buf)) != -1) {
|
out.write(buf, 0, n);
|
}
|
}
|
|
// 构造访问 URL
|
String baseUrl = request.getScheme() + "://" + request.getServerName()
|
+ ":" + request.getServerPort();
|
String url = baseUrl + pathPrefix + "/" + dateDir + "/" + fileName;
|
|
return ApiResponse.ok(new UploadResult(url));
|
}
|
|
public static class UploadResult {
|
private final String url;
|
|
public UploadResult(String url) {
|
this.url = url;
|
}
|
|
public String getUrl() {
|
return url;
|
}
|
}
|
}
|