zhuyifan

上传下载配置

...@@ -48,6 +48,7 @@ dependencies { ...@@ -48,6 +48,7 @@ dependencies {
48 implementation 'com.aliyun:aliyun-java-sdk-core:4.6.4' //阿里云SDK核心库 48 implementation 'com.aliyun:aliyun-java-sdk-core:4.6.4' //阿里云SDK核心库
49 implementation 'com.aliyun:aliyun-java-sdk-dysmsapi:2.2.1'//阿里云短信服务SDK 49 implementation 'com.aliyun:aliyun-java-sdk-dysmsapi:2.2.1'//阿里云短信服务SDK
50 implementation 'com.aliyun:aliyun-java-sdk-dm:3.3.2'//阿里云邮件服务SDK 50 implementation 'com.aliyun:aliyun-java-sdk-dm:3.3.2'//阿里云邮件服务SDK
51 + implementation "com.aliyun.oss:aliyun-sdk-oss:3.11.1"//阿里云OSS服务
51 52
52 implementation "io.zipkin.brave:brave:5.12.5" 53 implementation "io.zipkin.brave:brave:5.12.5"
53 implementation "io.zipkin.brave:brave-context-slf4j:5.12.5" 54 implementation "io.zipkin.brave:brave-context-slf4j:5.12.5"
......
...@@ -3,11 +3,14 @@ package com.infoloop.tianting.config; ...@@ -3,11 +3,14 @@ package com.infoloop.tianting.config;
3 import com.aliyuncs.DefaultAcsClient; 3 import com.aliyuncs.DefaultAcsClient;
4 import com.aliyuncs.profile.DefaultProfile; 4 import com.aliyuncs.profile.DefaultProfile;
5 import com.fasterxml.jackson.dataformat.xml.XmlMapper; 5 import com.fasterxml.jackson.dataformat.xml.XmlMapper;
6 +import com.infoloop.tianting.server.StorageService;
7 +import com.infoloop.tianting.server.StorageServiceFactory;
6 import com.infoloop.tianting.store.LoginCodeStore; 8 import com.infoloop.tianting.store.LoginCodeStore;
7 import com.infoloop.tianting.utils.SmsUtil; 9 import com.infoloop.tianting.utils.SmsUtil;
8 import io.lettuce.core.api.StatefulRedisConnection; 10 import io.lettuce.core.api.StatefulRedisConnection;
9 import org.springframework.beans.factory.annotation.Autowired; 11 import org.springframework.beans.factory.annotation.Autowired;
10 import org.springframework.beans.factory.annotation.Qualifier; 12 import org.springframework.beans.factory.annotation.Qualifier;
13 +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
11 import org.springframework.context.annotation.Bean; 14 import org.springframework.context.annotation.Bean;
12 import org.springframework.context.annotation.Configuration; 15 import org.springframework.context.annotation.Configuration;
13 import org.springframework.context.annotation.Import; 16 import org.springframework.context.annotation.Import;
...@@ -35,6 +38,12 @@ public class AppConfig { ...@@ -35,6 +38,12 @@ public class AppConfig {
35 } 38 }
36 39
37 @Bean 40 @Bean
41 + @ConditionalOnProperty(name = "aliyun.oss.endpoint")
42 + public StorageService storageService(@Autowired OssConfig ossConfig) {
43 + return StorageServiceFactory.createStorageService(ossConfig.getEndpoint(), ossConfig.getAccessKeyId(), ossConfig.getAccessKeySecret());
44 + }
45 +
46 + @Bean
38 public MappingJackson2XmlHttpMessageConverter mappingJackson2XmlHttpMessageConverter() { 47 public MappingJackson2XmlHttpMessageConverter mappingJackson2XmlHttpMessageConverter() {
39 return new MappingJackson2XmlHttpMessageConverter(new XmlMapper()); 48 return new MappingJackson2XmlHttpMessageConverter(new XmlMapper());
40 } 49 }
......
1 +package com.infoloop.tianting.config;
2 +
3 +import lombok.Data;
4 +import org.springframework.boot.context.properties.ConfigurationProperties;
5 +import org.springframework.context.annotation.PropertySource;
6 +import org.springframework.stereotype.Component;
7 +
8 +@Component
9 +@ConfigurationProperties(prefix = "aliyun.oss")
10 +@Data
11 +@PropertySource(encoding = "UTF-8", value = "classpath:application.properties", ignoreResourceNotFound = true)
12 +public class OssConfig {
13 +
14 + private String accessKeyId;
15 +
16 + private String accessKeySecret;
17 +
18 + private String endpoint;
19 +
20 + private String host;
21 +
22 + private String bucket;
23 +}
1 +package com.infoloop.tianting.controller;
2 +
3 +import cn.dev33.satoken.annotation.SaIgnore;
4 +import cn.hutool.core.io.IoUtil;
5 +import cn.hutool.core.io.file.FileNameUtil;
6 +import cn.hutool.core.util.IdUtil;
7 +import com.github.xiaoymin.knife4j.annotations.ApiSupport;
8 +import com.infoloop.tianting.config.OssConfig;
9 +import com.infoloop.tianting.server.StorageService;
10 +import io.swagger.annotations.Api;
11 +import io.swagger.annotations.ApiOperation;
12 +import lombok.extern.slf4j.Slf4j;
13 +import org.springframework.beans.factory.annotation.Autowired;
14 +import org.springframework.http.HttpStatus;
15 +import org.springframework.http.MediaType;
16 +import org.springframework.util.Assert;
17 +import org.springframework.validation.annotation.Validated;
18 +import org.springframework.web.bind.annotation.GetMapping;
19 +import org.springframework.web.bind.annotation.PostMapping;
20 +import org.springframework.web.bind.annotation.RequestParam;
21 +import org.springframework.web.bind.annotation.ResponseStatus;
22 +import org.springframework.web.bind.annotation.RestController;
23 +import org.springframework.web.multipart.MultipartFile;
24 +
25 +import javax.annotation.Nullable;
26 +import javax.servlet.ServletOutputStream;
27 +import javax.servlet.http.HttpServletResponse;
28 +import java.io.File;
29 +import java.io.InputStream;
30 +import java.net.URLEncoder;
31 +import java.nio.charset.StandardCharsets;
32 +import java.time.LocalDateTime;
33 +import java.time.format.DateTimeFormatter;
34 +
35 +@Api(tags = "上传、下载管理")
36 +@ApiSupport(order = 99)
37 +@Slf4j
38 +@Validated
39 +@RestController
40 +public class UploadController {
41 +
42 + private final OssConfig ossConfig;
43 +
44 + private final StorageService storageService;
45 +
46 + @Autowired(required = false)
47 + public UploadController(OssConfig ossConfig, @Nullable StorageService storageService) {
48 + this.ossConfig = ossConfig;
49 + this.storageService = storageService;
50 + }
51 +
52 + @SaIgnore
53 + @ApiOperation("上传")
54 + @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
55 + @ResponseStatus(HttpStatus.OK)
56 + public String upload(@RequestParam("file") MultipartFile file,
57 + @RequestParam(value = "folder", defaultValue = "upload") String folder) throws Exception {
58 + Assert.notNull(storageService, "存储服务未初始化,请检查配置 aliyun.oss.endpoint");
59 + String extension = FileNameUtil.getSuffix(file.getOriginalFilename());
60 + String path = generateUploadPath(folder, extension);
61 + File tempFile = File.createTempFile(IdUtil.fastSimpleUUID(), null);
62 + try {
63 + file.transferTo(tempFile);
64 + storageService.putObject(ossConfig.getBucket(), path, tempFile);
65 + } catch (Exception e) {
66 + log.error("上传失败: {}", file.getOriginalFilename(), e);
67 + throw new RuntimeException("文件上传失败,请稍后重试");
68 + } finally {
69 + tempFile.delete();
70 + }
71 + return String.format("%s/%s", ossConfig.getHost(), path);
72 + }
73 +
74 + @SaIgnore
75 + @ApiOperation("下载")
76 + @GetMapping(value = "/download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
77 + @ResponseStatus(HttpStatus.OK)
78 + public void download(@RequestParam("path") String path, HttpServletResponse response) {
79 + Assert.notNull(storageService, "存储服务未初始化,请检查配置 aliyun.oss.endpoint");
80 + try (InputStream inputStream = storageService.getObjectInputStream(ossConfig.getBucket(), path);
81 + ServletOutputStream outputStream = response.getOutputStream()) {
82 + String fileName = path.substring(path.lastIndexOf("/") + 1);
83 + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
84 + response.setHeader("Content-Disposition", "attachment; filename=\"" + URLEncoder.encode(fileName, StandardCharsets.UTF_8) + "\"");
85 + IoUtil.copy(inputStream, outputStream);
86 + } catch (Exception e) {
87 + log.error("下载失败: {}", path, e);
88 + throw new RuntimeException("文件下载失败,请稍后重试");
89 + }
90 + }
91 +
92 + private String generateUploadPath(String folder, String extension) {
93 + String datePath = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
94 + return String.format("nutri-api/%s/%s/%s.%s", folder, datePath, IdUtil.fastSimpleUUID(), extension);
95 + }
96 +
97 +}
1 +package com.infoloop.tianting.server;
2 +
3 +import com.aliyun.oss.OSS;
4 +import com.aliyun.oss.OSSClientBuilder;
5 +import com.aliyun.oss.model.GetObjectRequest;
6 +
7 +import java.io.ByteArrayInputStream;
8 +import java.io.File;
9 +import java.io.InputStream;
10 +
11 +public class OssStorageService implements StorageService{
12 + private final OSS ossClient;
13 +
14 + public OssStorageService(String endpoint, String accessKeyId, String accessKeySecret) {
15 + this.ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
16 + }
17 +
18 + @Override
19 + public void putObject(String bucketName, String key, byte[] data) {
20 + ossClient.putObject(bucketName, key, new ByteArrayInputStream(data));
21 + }
22 +
23 + @Override
24 + public void putObject(String bucketName, String key, File file) {
25 + ossClient.putObject(bucketName, key, file);
26 + }
27 +
28 + @Override
29 + public void putObject(String bucketName, String key, InputStream stream) {
30 + ossClient.putObject(bucketName, key, stream);
31 + }
32 +
33 + @Override
34 + public InputStream getObjectInputStream(String bucketName, String key) {
35 + return ossClient.getObject(new GetObjectRequest(bucketName, key)).getObjectContent();
36 + }
37 +
38 + @Override
39 + public void close() throws Exception {
40 + ossClient.shutdown();
41 + }
42 +}
1 +package com.infoloop.tianting.server;
2 +
3 +import java.io.File;
4 +import java.io.InputStream;
5 +
6 +public interface StorageService extends AutoCloseable {
7 +
8 + void putObject(String bucketName, String key, byte[] data) throws Exception;
9 +
10 + void putObject(String bucketName, String key, File file) throws Exception;
11 +
12 + void putObject(String bucketName, String key, InputStream stream) throws Exception;
13 +
14 + InputStream getObjectInputStream(String bucketName, String key);
15 +}
...\ No newline at end of file ...\ No newline at end of file
1 +package com.infoloop.tianting.server;
2 +
3 +public class StorageServiceFactory {
4 + public static StorageService createStorageService(String endpoint, String accessKey, String accessSecret) {
5 + if (endpoint.contains("aliyuncs.com")) {
6 + return new OssStorageService(endpoint, accessKey, accessSecret);
7 + } else {
8 + throw new IllegalArgumentException("Unsupported storage type: " + endpoint);
9 + }
10 + }
11 +}
...\ No newline at end of file ...\ No newline at end of file
...@@ -104,6 +104,12 @@ sa-token.sign.secret-key=4Jk2giMQw8D7lHJcU8fv8CXjSOhjp5Ee ...@@ -104,6 +104,12 @@ sa-token.sign.secret-key=4Jk2giMQw8D7lHJcU8fv8CXjSOhjp5Ee
104 ##\u65E5\u5FD7\u914D\u7F6E 104 ##\u65E5\u5FD7\u914D\u7F6E
105 logging.config=classpath:logback-spring.xml 105 logging.config=classpath:logback-spring.xml
106 106
107 +aliyun.oss.endPoint=oss-cn-shanghai.aliyuncs.com
108 +aliyun.oss.host=infoloop-public-qa.oss-cn-shanghai.aliyuncs.com
109 +aliyun.oss.accessKeyId=LTAIo9gcY5sjIZdN
110 +aliyun.oss.accessKeySecret=gOOkVY4Euj3MSbPyg1tZvPbZbUssj5
111 +aliyun.oss.bucket=infoloop-public-qa
112 +
107 ##\u77ED\u4FE1\u914D\u7F6E 113 ##\u77ED\u4FE1\u914D\u7F6E
108 sms-config.accessKeyId= 114 sms-config.accessKeyId=
109 sms-config.accessKeySecret= 115 sms-config.accessKeySecret=
......