|
@@ -2,15 +2,21 @@ package com.zhentao.osspicture;
|
|
|
|
|
|
import com.aliyun.oss.OSS;
|
|
|
import com.aliyun.oss.OSSClientBuilder;
|
|
|
+import com.aliyun.oss.model.ObjectMetadata;
|
|
|
import com.aliyun.oss.model.PutObjectRequest;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.stereotype.Component;
|
|
|
import org.springframework.web.multipart.MultipartFile;
|
|
|
|
|
|
import java.io.IOException;
|
|
|
+import java.io.InputStream;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
import java.util.UUID;
|
|
|
|
|
|
@Component
|
|
|
+@Slf4j
|
|
|
public class OssUtil {
|
|
|
|
|
|
// 从配置文件中读取OSS相关信息
|
|
@@ -18,44 +24,204 @@ public class OssUtil {
|
|
|
private static final String accessKeyId = "LTAI5tH9VHPZwGJu4UX3hrL5";
|
|
|
private static final String accessKeySecret = "mbsutFJYLkzosvvKNr0DD28XSg4mqA";
|
|
|
private static final String bucketName = "fjj1";
|
|
|
+ private static final boolean useCDN = true;
|
|
|
+
|
|
|
+ private static final String cdnDomain = "https://cdn.yourdomain.com";
|
|
|
+
|
|
|
+ // MIME类型到扩展名的映射
|
|
|
+ private static final Map<String, String> MIME_TO_EXTENSION = new HashMap<>();
|
|
|
+ static {
|
|
|
+ // 图片类型
|
|
|
+ MIME_TO_EXTENSION.put("image/jpeg", "jpg");
|
|
|
+ MIME_TO_EXTENSION.put("image/png", "png");
|
|
|
+ MIME_TO_EXTENSION.put("image/gif", "gif");
|
|
|
+ MIME_TO_EXTENSION.put("image/bmp", "bmp");
|
|
|
+ MIME_TO_EXTENSION.put("image/webp", "webp");
|
|
|
+ MIME_TO_EXTENSION.put("image/svg+xml", "svg");
|
|
|
+
|
|
|
+ // 视频类型
|
|
|
+ MIME_TO_EXTENSION.put("video/mp4", "mp4");
|
|
|
+ MIME_TO_EXTENSION.put("video/quicktime", "mov");
|
|
|
+ MIME_TO_EXTENSION.put("video/x-msvideo", "avi");
|
|
|
+ MIME_TO_EXTENSION.put("video/x-matroska", "mkv");
|
|
|
+ MIME_TO_EXTENSION.put("video/x-flv", "flv");
|
|
|
+ MIME_TO_EXTENSION.put("video/webm", "webm");
|
|
|
+
|
|
|
+ // 音频类型
|
|
|
+ MIME_TO_EXTENSION.put("audio/mpeg", "mp3");
|
|
|
+ MIME_TO_EXTENSION.put("audio/wav", "wav");
|
|
|
+ MIME_TO_EXTENSION.put("audio/ogg", "ogg");
|
|
|
+ MIME_TO_EXTENSION.put("audio/flac", "flac");
|
|
|
+ MIME_TO_EXTENSION.put("audio/aac", "aac");
|
|
|
+
|
|
|
+ // 文档类型
|
|
|
+ MIME_TO_EXTENSION.put("application/pdf", "pdf");
|
|
|
+ MIME_TO_EXTENSION.put("application/msword", "doc");
|
|
|
+ MIME_TO_EXTENSION.put("application/vnd.openxmlformats-officedocument.wordprocessingml.document", "docx");
|
|
|
+ MIME_TO_EXTENSION.put("application/vnd.ms-excel", "xls");
|
|
|
+ MIME_TO_EXTENSION.put("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "xlsx");
|
|
|
+ MIME_TO_EXTENSION.put("application/vnd.ms-powerpoint", "ppt");
|
|
|
+ MIME_TO_EXTENSION.put("application/vnd.openxmlformats-officedocument.presentationml.presentation", "pptx");
|
|
|
+ MIME_TO_EXTENSION.put("text/plain", "txt");
|
|
|
+ }
|
|
|
|
|
|
public String uploadFile(MultipartFile file) throws IOException {
|
|
|
- // 创建OSSClient实例
|
|
|
- OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
|
|
|
+ if (file == null || file.isEmpty()) {
|
|
|
+ throw new IllegalArgumentException("上传文件不能为空");
|
|
|
+ }
|
|
|
|
|
|
+ OSS ossClient = null;
|
|
|
try {
|
|
|
- // 生成唯一的文件名
|
|
|
-// String fileName = UUID.randomUUID().toString() + "-" + file.getOriginalFilename();
|
|
|
+ ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
|
|
|
+
|
|
|
String originalFilename = file.getOriginalFilename();
|
|
|
- String extension = getFileExtension(originalFilename);
|
|
|
- String fileName = UUID.randomUUID().toString() + "." + extension;
|
|
|
- //上传图片
|
|
|
- PutObjectRequest putObjectRequest = new PutObjectRequest(
|
|
|
- bucketName, fileName, file.getInputStream()
|
|
|
- );
|
|
|
- ossClient.putObject(putObjectRequest);
|
|
|
+ String contentType = file.getContentType();
|
|
|
+ long fileSize = file.getSize();
|
|
|
+
|
|
|
+ // 确定文件类型和扩展名
|
|
|
+ FileTypeInfo fileTypeInfo = determineFileType(originalFilename, contentType);
|
|
|
+ String extension = fileTypeInfo.getExtension();
|
|
|
+ String fileCategory = fileTypeInfo.getCategory();
|
|
|
+
|
|
|
+ // 生成唯一的文件名
|
|
|
+ String fileName = generateUniqueFileName(fileCategory, extension);
|
|
|
|
|
|
- // 拼接文件访问URL
|
|
|
- String endpointWithoutProtocol = endpoint.replaceFirst("^https?://", "");
|
|
|
- String imageUrl = "https://" + bucketName + "." + endpointWithoutProtocol + "/" + fileName;
|
|
|
+ // 创建文件元数据
|
|
|
+ ObjectMetadata metadata = createMetadata(originalFilename, contentType, fileSize, fileCategory);
|
|
|
|
|
|
- System.err.println(imageUrl);
|
|
|
- // 返回文件访问URL
|
|
|
- return imageUrl;
|
|
|
+ // 上传文件
|
|
|
+ try (InputStream inputStream = file.getInputStream()) {
|
|
|
+ PutObjectRequest putObjectRequest = new PutObjectRequest(
|
|
|
+ bucketName,
|
|
|
+ fileName,
|
|
|
+ inputStream,
|
|
|
+ metadata
|
|
|
+ );
|
|
|
+ ossClient.putObject(putObjectRequest);
|
|
|
+ }
|
|
|
|
|
|
+ // 生成文件访问URL
|
|
|
+ String fileUrl = generateFileUrl(fileName);
|
|
|
+ log.info("文件上传成功: {} ({}), 大小: {} KB, URL: {}",
|
|
|
+ originalFilename, fileCategory, fileSize / 1024, fileUrl);
|
|
|
|
|
|
- }catch (IOException e){
|
|
|
- e.printStackTrace();
|
|
|
+ return fileUrl;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("文件上传失败: {}", e.getMessage(), e);
|
|
|
+ throw new IOException("文件上传失败: " + e.getMessage(), e);
|
|
|
} finally {
|
|
|
- // 关闭OSSClient
|
|
|
- ossClient.shutdown();
|
|
|
+ if (ossClient != null) {
|
|
|
+ ossClient.shutdown();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private String generateUniqueFileName(String category, String extension) {
|
|
|
+ return "uploads/" + category + "/" + UUID.randomUUID() + "." + extension;
|
|
|
+ }
|
|
|
+
|
|
|
+ private ObjectMetadata createMetadata(String filename, String contentType,
|
|
|
+ long fileSize, String category) {
|
|
|
+ ObjectMetadata metadata = new ObjectMetadata();
|
|
|
+
|
|
|
+ // 设置基础元数据
|
|
|
+ metadata.setContentType(contentType != null ? contentType : "application/octet-stream");
|
|
|
+ metadata.setContentLength(fileSize);
|
|
|
+
|
|
|
+ // 设置下载时的文件名
|
|
|
+ if (filename != null) {
|
|
|
+ metadata.setContentDisposition("attachment; filename=\"" + filename + "\"");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加自定义元数据
|
|
|
+ metadata.addUserMetadata("Original-Filename", filename != null ? filename : "");
|
|
|
+ metadata.addUserMetadata("File-Category", category);
|
|
|
+
|
|
|
+ return metadata;
|
|
|
+ }
|
|
|
+
|
|
|
+ private FileTypeInfo determineFileType(String filename, String contentType) {
|
|
|
+ String extension = "bin";
|
|
|
+ String category = "other";
|
|
|
+
|
|
|
+ // 1. 尝试从文件名获取扩展名
|
|
|
+ if (filename != null && filename.contains(".")) {
|
|
|
+ String fileExt = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
|
|
|
+ if (isValidExtension(fileExt)) {
|
|
|
+ extension = fileExt;
|
|
|
+ category = determineCategory(extension);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 如果扩展名无效,尝试从内容类型获取
|
|
|
+ if ("bin".equals(extension) && contentType != null) {
|
|
|
+ // 使用映射表获取标准扩展名
|
|
|
+ String mappedExt = MIME_TO_EXTENSION.get(contentType.toLowerCase());
|
|
|
+ if (mappedExt != null) {
|
|
|
+ extension = mappedExt;
|
|
|
+ category = determineCategory(extension);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 最终确定文件分类
|
|
|
+ if ("other".equals(category)) {
|
|
|
+ category = determineCategory(extension);
|
|
|
+ }
|
|
|
+
|
|
|
+ return new FileTypeInfo(extension, category);
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean isValidExtension(String ext) {
|
|
|
+ // 检查扩展名是否有效(不含特殊字符)
|
|
|
+ return ext.matches("[a-z0-9]{1,10}");
|
|
|
+ }
|
|
|
+
|
|
|
+ private String determineCategory(String extension) {
|
|
|
+ if (extension.matches("png|jpe?g|gif|bmp|webp|svg|heic|heif")) {
|
|
|
+ return "images";
|
|
|
+ } else if (extension.matches("mp4|mov|avi|mkv|flv|webm|m4v|wmv|3gp")) {
|
|
|
+ return "videos";
|
|
|
+ } else if (extension.matches("mp3|wav|ogg|flac|aac|m4a|wma|amr")) {
|
|
|
+ return "audios";
|
|
|
+ } else if (extension.matches("pdf|docx?|xlsx?|pptx?|txt|rtf|csv|pages|numbers|key")) {
|
|
|
+ return "documents";
|
|
|
+ } else if (extension.matches("zip|rar|7z|tar|gz")) {
|
|
|
+ return "archives";
|
|
|
+ }
|
|
|
+ return "other";
|
|
|
+ }
|
|
|
+
|
|
|
+ private String generateFileUrl(String fileName) {
|
|
|
+ // 使用CDN加速域名(如果配置了),否则使用标准OSS域名
|
|
|
+ if (useCDN && cdnDomain != null && !cdnDomain.isEmpty()) {
|
|
|
+ return cdnDomain + "/" + fileName;
|
|
|
}
|
|
|
- return null;
|
|
|
+
|
|
|
+ // 构建标准OSS域名
|
|
|
+ String domain = endpoint.startsWith("http") ?
|
|
|
+ endpoint : "https://" + endpoint;
|
|
|
+
|
|
|
+ return domain.replaceFirst("://", "://" + bucketName + ".") + "/" + fileName;
|
|
|
}
|
|
|
- private String getFileExtension(String fileName) {
|
|
|
- if (fileName == null || !fileName.contains(".")) {
|
|
|
- return "jpg";
|
|
|
+
|
|
|
+ // 文件类型信息辅助类
|
|
|
+ private static class FileTypeInfo {
|
|
|
+ private final String extension;
|
|
|
+ private final String category;
|
|
|
+
|
|
|
+ public FileTypeInfo(String extension, String category) {
|
|
|
+ this.extension = extension;
|
|
|
+ this.category = category;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getExtension() {
|
|
|
+ return extension;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getCategory() {
|
|
|
+ return category;
|
|
|
}
|
|
|
- return fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
}
|