OssUtil.java 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. package com.zhentao.osspicture;
  2. import com.aliyun.oss.OSS;
  3. import com.aliyun.oss.OSSClientBuilder;
  4. import com.aliyun.oss.model.ObjectMetadata;
  5. import com.aliyun.oss.model.PutObjectRequest;
  6. import lombok.extern.slf4j.Slf4j;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.stereotype.Component;
  9. import org.springframework.web.bind.annotation.PostMapping;
  10. import org.springframework.web.bind.annotation.RequestMapping;
  11. import org.springframework.web.bind.annotation.RestController;
  12. import org.springframework.web.multipart.MultipartFile;
  13. import java.io.IOException;
  14. import java.io.InputStream;
  15. import java.util.HashMap;
  16. import java.util.Map;
  17. import java.util.UUID;
  18. @Component
  19. @Slf4j
  20. public class OssUtil {
  21. // 从配置文件中读取OSS相关信息
  22. private static final String endpoint = "https://oss-cn-beijing.aliyuncs.com";
  23. private static final String accessKeyId = "LTAI5tH9VHPZwGJu4UX3hrL5";
  24. private static final String accessKeySecret = "mbsutFJYLkzosvvKNr0DD28XSg4mqA";
  25. private static final String bucketName = "fjj1";
  26. private static final boolean useCDN = true;
  27. private static final String cdnDomain = "https://cdn.yourdomain.com";
  28. // MIME类型到扩展名的映射
  29. private static final Map<String, String> MIME_TO_EXTENSION = new HashMap<>();
  30. static {
  31. // 图片类型
  32. MIME_TO_EXTENSION.put("image/jpeg", "jpg");
  33. MIME_TO_EXTENSION.put("image/png", "png");
  34. MIME_TO_EXTENSION.put("image/gif", "gif");
  35. MIME_TO_EXTENSION.put("image/bmp", "bmp");
  36. MIME_TO_EXTENSION.put("image/webp", "webp");
  37. MIME_TO_EXTENSION.put("image/svg+xml", "svg");
  38. // 视频类型
  39. MIME_TO_EXTENSION.put("video/mp4", "mp4");
  40. MIME_TO_EXTENSION.put("video/quicktime", "mov");
  41. MIME_TO_EXTENSION.put("video/x-msvideo", "avi");
  42. MIME_TO_EXTENSION.put("video/x-matroska", "mkv");
  43. MIME_TO_EXTENSION.put("video/x-flv", "flv");
  44. MIME_TO_EXTENSION.put("video/webm", "webm");
  45. // 音频类型
  46. MIME_TO_EXTENSION.put("audio/mpeg", "mp3");
  47. MIME_TO_EXTENSION.put("audio/wav", "wav");
  48. MIME_TO_EXTENSION.put("audio/ogg", "ogg");
  49. MIME_TO_EXTENSION.put("audio/flac", "flac");
  50. MIME_TO_EXTENSION.put("audio/aac", "aac");
  51. // 文档类型
  52. MIME_TO_EXTENSION.put("application/pdf", "pdf");
  53. MIME_TO_EXTENSION.put("application/msword", "doc");
  54. MIME_TO_EXTENSION.put("application/vnd.openxmlformats-officedocument.wordprocessingml.document", "docx");
  55. MIME_TO_EXTENSION.put("application/vnd.ms-excel", "xls");
  56. MIME_TO_EXTENSION.put("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "xlsx");
  57. MIME_TO_EXTENSION.put("application/vnd.ms-powerpoint", "ppt");
  58. MIME_TO_EXTENSION.put("application/vnd.openxmlformats-officedocument.presentationml.presentation", "pptx");
  59. MIME_TO_EXTENSION.put("text/plain", "txt");
  60. }
  61. public String uploadFile(MultipartFile file) throws IOException {
  62. if (file == null || file.isEmpty()) {
  63. throw new IllegalArgumentException("上传文件不能为空");
  64. }
  65. OSS ossClient = null;
  66. try {
  67. ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
  68. String originalFilename = file.getOriginalFilename();
  69. String contentType = file.getContentType();
  70. long fileSize = file.getSize();
  71. // 确定文件类型和扩展名
  72. FileTypeInfo fileTypeInfo = determineFileType(originalFilename, contentType);
  73. String extension = fileTypeInfo.getExtension();
  74. String fileCategory = fileTypeInfo.getCategory();
  75. // 生成唯一的文件名
  76. String fileName = generateUniqueFileName(fileCategory, extension);
  77. // 创建文件元数据
  78. ObjectMetadata metadata = createMetadata(originalFilename, contentType, fileSize, fileCategory);
  79. // 上传文件
  80. try (InputStream inputStream = file.getInputStream()) {
  81. PutObjectRequest putObjectRequest = new PutObjectRequest(
  82. bucketName,
  83. fileName,
  84. inputStream,
  85. metadata
  86. );
  87. ossClient.putObject(putObjectRequest);
  88. }
  89. // 生成文件访问URL
  90. String fileUrl = generateFileUrl(fileName);
  91. log.info("文件上传成功: {} ({}), 大小: {} KB, URL: {}",
  92. originalFilename, fileCategory, fileSize / 1024, fileUrl);
  93. return fileUrl;
  94. } catch (Exception e) {
  95. log.error("文件上传失败: {}", e.getMessage(), e);
  96. throw new IOException("文件上传失败: " + e.getMessage(), e);
  97. } finally {
  98. if (ossClient != null) {
  99. ossClient.shutdown();
  100. }
  101. }
  102. }
  103. private String generateUniqueFileName(String category, String extension) {
  104. return "uploads/" + category + "/" + UUID.randomUUID() + "." + extension;
  105. }
  106. private ObjectMetadata createMetadata(String filename, String contentType,
  107. long fileSize, String category) {
  108. ObjectMetadata metadata = new ObjectMetadata();
  109. // 设置基础元数据
  110. metadata.setContentType(contentType != null ? contentType : "application/octet-stream");
  111. metadata.setContentLength(fileSize);
  112. // 设置下载时的文件名
  113. if (filename != null) {
  114. metadata.setContentDisposition("attachment; filename=\"" + filename + "\"");
  115. }
  116. // 添加自定义元数据
  117. metadata.addUserMetadata("Original-Filename", filename != null ? filename : "");
  118. metadata.addUserMetadata("File-Category", category);
  119. return metadata;
  120. }
  121. private FileTypeInfo determineFileType(String filename, String contentType) {
  122. String extension = "bin";
  123. String category = "other";
  124. // 1. 尝试从文件名获取扩展名
  125. if (filename != null && filename.contains(".")) {
  126. String fileExt = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
  127. if (isValidExtension(fileExt)) {
  128. extension = fileExt;
  129. category = determineCategory(extension);
  130. }
  131. }
  132. // 2. 如果扩展名无效,尝试从内容类型获取
  133. if ("bin".equals(extension) && contentType != null) {
  134. // 使用映射表获取标准扩展名
  135. String mappedExt = MIME_TO_EXTENSION.get(contentType.toLowerCase());
  136. if (mappedExt != null) {
  137. extension = mappedExt;
  138. category = determineCategory(extension);
  139. }
  140. }
  141. // 3. 最终确定文件分类
  142. if ("other".equals(category)) {
  143. category = determineCategory(extension);
  144. }
  145. return new FileTypeInfo(extension, category);
  146. }
  147. private boolean isValidExtension(String ext) {
  148. // 检查扩展名是否有效(不含特殊字符)
  149. return ext.matches("[a-z0-9]{1,10}");
  150. }
  151. private String determineCategory(String extension) {
  152. if (extension.matches("png|jpe?g|gif|bmp|webp|svg|heic|heif")) {
  153. return "images";
  154. } else if (extension.matches("mp4|mov|avi|mkv|flv|webm|m4v|wmv|3gp")) {
  155. return "videos";
  156. } else if (extension.matches("mp3|wav|ogg|flac|aac|m4a|wma|amr")) {
  157. return "audios";
  158. } else if (extension.matches("pdf|docx?|xlsx?|pptx?|txt|rtf|csv|pages|numbers|key")) {
  159. return "documents";
  160. } else if (extension.matches("zip|rar|7z|tar|gz")) {
  161. return "archives";
  162. }
  163. return "other";
  164. }
  165. private String generateFileUrl(String fileName) {
  166. // 使用CDN加速域名(如果配置了),否则使用标准OSS域名
  167. if (useCDN && cdnDomain != null && !cdnDomain.isEmpty()) {
  168. return cdnDomain + "/" + fileName;
  169. }
  170. // 构建标准OSS域名
  171. String domain = endpoint.startsWith("http") ?
  172. endpoint : "https://" + endpoint;
  173. return domain.replaceFirst("://", "://" + bucketName + ".") + "/" + fileName;
  174. }
  175. // 文件类型信息辅助类
  176. private static class FileTypeInfo {
  177. private final String extension;
  178. private final String category;
  179. public FileTypeInfo(String extension, String category) {
  180. this.extension = extension;
  181. this.category = category;
  182. }
  183. public String getExtension() {
  184. return extension;
  185. }
  186. public String getCategory() {
  187. return category;
  188. }
  189. }
  190. }