package com.tcm.training.admin.upload;
|
|
import com.tcm.training.admin.common.ApiResponse;
|
import org.springframework.web.bind.annotation.GetMapping;
|
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 org.springframework.util.StringUtils;
|
|
import java.io.IOException;
|
import java.util.Arrays;
|
import java.util.HashSet;
|
import java.util.Set;
|
|
@RestController
|
@RequestMapping({"/api/v1", "/api"})
|
public class UploadController {
|
|
private static final long MAX_SIZE = 5 * 1024 * 1024L; // 5MB
|
private static final long VIDEO_MAX_SIZE = 200 * 1024 * 1024L; // 200MB
|
private static final long APP_PACKAGE_MAX_SIZE = 500 * 1024 * 1024L; // 500MB
|
private static final Set<String> ALLOWED_TYPES = new HashSet<>(
|
Arrays.asList("image/jpeg", "image/png", "image/gif", "image/webp")
|
);
|
private static final Set<String> ALLOWED_VIDEO_TYPES = new HashSet<>(
|
Arrays.asList("video/mp4", "video/quicktime", "video/x-msvideo", "video/x-matroska", "video/webm")
|
);
|
private static final Set<String> ALLOWED_APP_PACKAGE_TYPES = new HashSet<>(
|
Arrays.asList(
|
"application/vnd.android.package-archive",
|
"application/octet-stream",
|
"application/x-itunes-ipa",
|
"application/zip",
|
"application/x-zip-compressed"
|
)
|
);
|
private static final String PLATFORM_ANDROID = "ANDROID";
|
private static final String PLATFORM_IOS = "IOS";
|
|
private final ObsUploadService obsUploadService;
|
private final UploadProgressService uploadProgressService;
|
|
public UploadController(ObsUploadService obsUploadService, UploadProgressService uploadProgressService) {
|
this.obsUploadService = obsUploadService;
|
this.uploadProgressService = uploadProgressService;
|
}
|
|
/**
|
* 图片上传
|
* POST /api/v1/upload/image
|
* @return { url: "访问地址" }
|
*/
|
@PostMapping("/upload/image")
|
public ApiResponse<UploadResult> uploadImage(
|
@RequestParam("file") MultipartFile file) throws IOException {
|
return ApiResponse.ok(storeFile(file, MAX_SIZE, ALLOWED_TYPES, "仅支持 JPG、PNG、GIF、WebP 格式", "image", null));
|
}
|
|
@PostMapping("/upload/video")
|
public ApiResponse<UploadResult> uploadVideo(
|
@RequestParam("file") MultipartFile file,
|
@RequestParam(value = "uploadTaskId", required = false) String uploadTaskId) throws IOException {
|
return ApiResponse.ok(storeFile(file, VIDEO_MAX_SIZE, ALLOWED_VIDEO_TYPES, "仅支持 MP4、MOV、AVI、MKV、WebM 格式", "video", uploadTaskId));
|
}
|
|
@PostMapping("/upload/app-package")
|
public ApiResponse<UploadResult> uploadAppPackage(
|
@RequestParam("file") MultipartFile file,
|
@RequestParam("platform") String platform) throws IOException {
|
String normalizedPlatform = normalizePlatform(platform);
|
validateAppPackage(file, normalizedPlatform);
|
UploadFileResult uploadResult = obsUploadService.upload(file, "app-package/" + normalizedPlatform.toLowerCase(), null);
|
return ApiResponse.ok(new UploadResult(uploadResult.getUrl(), uploadResult.getFileName()));
|
}
|
|
@GetMapping("/upload/video/progress")
|
public ApiResponse<UploadProgressInfo> uploadVideoProgress(
|
@RequestParam("taskId") String taskId) {
|
return ApiResponse.ok(uploadProgressService.get(taskId));
|
}
|
|
private UploadResult storeFile(MultipartFile file,
|
long maxSize,
|
Set<String> allowedTypes,
|
String typeMessage,
|
String bizPath,
|
String uploadTaskId) throws IOException {
|
validateFileSize(file, maxSize);
|
String contentType = file.getContentType();
|
if (contentType == null || !allowedTypes.contains(contentType)) {
|
throw new IllegalArgumentException(typeMessage);
|
}
|
UploadFileResult uploadResult = obsUploadService.upload(file, bizPath, uploadTaskId);
|
return new UploadResult(uploadResult.getUrl(), uploadResult.getFileName());
|
}
|
|
private void validateFileSize(MultipartFile file, long maxSize) {
|
if (file == null || file.isEmpty()) {
|
throw new IllegalArgumentException("请选择要上传的文件");
|
}
|
if (file.getSize() > maxSize) {
|
throw new IllegalArgumentException("文件大小超出限制");
|
}
|
}
|
|
private void validateAppPackage(MultipartFile file, String platform) {
|
validateFileSize(file, APP_PACKAGE_MAX_SIZE);
|
String originalName = file.getOriginalFilename();
|
String extension = "";
|
if (StringUtils.hasText(originalName) && originalName.contains(".")) {
|
extension = originalName.substring(originalName.lastIndexOf(".") + 1).toLowerCase();
|
}
|
if (PLATFORM_ANDROID.equals(platform) && !"apk".equals(extension)) {
|
throw new IllegalArgumentException("安卓安装包仅支持 APK 格式");
|
}
|
if (PLATFORM_IOS.equals(platform) && !"ipa".equals(extension)) {
|
throw new IllegalArgumentException("iOS 安装包仅支持 IPA 格式");
|
}
|
String contentType = file.getContentType();
|
if (StringUtils.hasText(contentType) && !ALLOWED_APP_PACKAGE_TYPES.contains(contentType)) {
|
throw new IllegalArgumentException("安装包格式不合法");
|
}
|
}
|
|
private String normalizePlatform(String platform) {
|
if (!StringUtils.hasText(platform)) {
|
throw new IllegalArgumentException("平台类型不能为空");
|
}
|
String normalized = platform.trim().toUpperCase();
|
if (PLATFORM_ANDROID.equals(normalized) || PLATFORM_IOS.equals(normalized)) {
|
return normalized;
|
}
|
throw new IllegalArgumentException("平台类型不合法");
|
}
|
|
public static class UploadResult {
|
private final String url;
|
private final String fileName;
|
|
public UploadResult(String url) {
|
this(url, null);
|
}
|
|
public UploadResult(String url, String fileName) {
|
this.url = url;
|
this.fileName = fileName;
|
}
|
|
public String getUrl() {
|
return url;
|
}
|
|
public String getFileName() {
|
return fileName;
|
}
|
}
|
}
|