zhuyifan

上传下载配置

......@@ -48,6 +48,7 @@ dependencies {
implementation 'com.aliyun:aliyun-java-sdk-core:4.6.4' //阿里云SDK核心库
implementation 'com.aliyun:aliyun-java-sdk-dysmsapi:2.2.1'//阿里云短信服务SDK
implementation 'com.aliyun:aliyun-java-sdk-dm:3.3.2'//阿里云邮件服务SDK
implementation "com.aliyun.oss:aliyun-sdk-oss:3.11.1"//阿里云OSS服务
implementation "io.zipkin.brave:brave:5.12.5"
implementation "io.zipkin.brave:brave-context-slf4j:5.12.5"
......
......@@ -3,11 +3,14 @@ package com.infoloop.tianting.config;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.profile.DefaultProfile;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.infoloop.tianting.server.StorageService;
import com.infoloop.tianting.server.StorageServiceFactory;
import com.infoloop.tianting.store.LoginCodeStore;
import com.infoloop.tianting.utils.SmsUtil;
import io.lettuce.core.api.StatefulRedisConnection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
......@@ -35,6 +38,12 @@ public class AppConfig {
}
@Bean
@ConditionalOnProperty(name = "aliyun.oss.endpoint")
public StorageService storageService(@Autowired OssConfig ossConfig) {
return StorageServiceFactory.createStorageService(ossConfig.getEndpoint(), ossConfig.getAccessKeyId(), ossConfig.getAccessKeySecret());
}
@Bean
public MappingJackson2XmlHttpMessageConverter mappingJackson2XmlHttpMessageConverter() {
return new MappingJackson2XmlHttpMessageConverter(new XmlMapper());
}
......
package com.infoloop.tianting.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "aliyun.oss")
@Data
@PropertySource(encoding = "UTF-8", value = "classpath:application.properties", ignoreResourceNotFound = true)
public class OssConfig {
private String accessKeyId;
private String accessKeySecret;
private String endpoint;
private String host;
private String bucket;
}
package com.infoloop.tianting.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.util.IdUtil;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.infoloop.tianting.config.OssConfig;
import com.infoloop.tianting.server.StorageService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Nullable;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@Api(tags = "上传、下载管理")
@ApiSupport(order = 99)
@Slf4j
@Validated
@RestController
public class UploadController {
private final OssConfig ossConfig;
private final StorageService storageService;
@Autowired(required = false)
public UploadController(OssConfig ossConfig, @Nullable StorageService storageService) {
this.ossConfig = ossConfig;
this.storageService = storageService;
}
@SaIgnore
@ApiOperation("上传")
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseStatus(HttpStatus.OK)
public String upload(@RequestParam("file") MultipartFile file,
@RequestParam(value = "folder", defaultValue = "upload") String folder) throws Exception {
Assert.notNull(storageService, "存储服务未初始化,请检查配置 aliyun.oss.endpoint");
String extension = FileNameUtil.getSuffix(file.getOriginalFilename());
String path = generateUploadPath(folder, extension);
File tempFile = File.createTempFile(IdUtil.fastSimpleUUID(), null);
try {
file.transferTo(tempFile);
storageService.putObject(ossConfig.getBucket(), path, tempFile);
} catch (Exception e) {
log.error("上传失败: {}", file.getOriginalFilename(), e);
throw new RuntimeException("文件上传失败,请稍后重试");
} finally {
tempFile.delete();
}
return String.format("%s/%s", ossConfig.getHost(), path);
}
@SaIgnore
@ApiOperation("下载")
@GetMapping(value = "/download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
@ResponseStatus(HttpStatus.OK)
public void download(@RequestParam("path") String path, HttpServletResponse response) {
Assert.notNull(storageService, "存储服务未初始化,请检查配置 aliyun.oss.endpoint");
try (InputStream inputStream = storageService.getObjectInputStream(ossConfig.getBucket(), path);
ServletOutputStream outputStream = response.getOutputStream()) {
String fileName = path.substring(path.lastIndexOf("/") + 1);
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
response.setHeader("Content-Disposition", "attachment; filename=\"" + URLEncoder.encode(fileName, StandardCharsets.UTF_8) + "\"");
IoUtil.copy(inputStream, outputStream);
} catch (Exception e) {
log.error("下载失败: {}", path, e);
throw new RuntimeException("文件下载失败,请稍后重试");
}
}
private String generateUploadPath(String folder, String extension) {
String datePath = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
return String.format("nutri-api/%s/%s/%s.%s", folder, datePath, IdUtil.fastSimpleUUID(), extension);
}
}
package com.infoloop.tianting.server;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.GetObjectRequest;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
public class OssStorageService implements StorageService{
private final OSS ossClient;
public OssStorageService(String endpoint, String accessKeyId, String accessKeySecret) {
this.ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
}
@Override
public void putObject(String bucketName, String key, byte[] data) {
ossClient.putObject(bucketName, key, new ByteArrayInputStream(data));
}
@Override
public void putObject(String bucketName, String key, File file) {
ossClient.putObject(bucketName, key, file);
}
@Override
public void putObject(String bucketName, String key, InputStream stream) {
ossClient.putObject(bucketName, key, stream);
}
@Override
public InputStream getObjectInputStream(String bucketName, String key) {
return ossClient.getObject(new GetObjectRequest(bucketName, key)).getObjectContent();
}
@Override
public void close() throws Exception {
ossClient.shutdown();
}
}
package com.infoloop.tianting.server;
import java.io.File;
import java.io.InputStream;
public interface StorageService extends AutoCloseable {
void putObject(String bucketName, String key, byte[] data) throws Exception;
void putObject(String bucketName, String key, File file) throws Exception;
void putObject(String bucketName, String key, InputStream stream) throws Exception;
InputStream getObjectInputStream(String bucketName, String key);
}
\ No newline at end of file
package com.infoloop.tianting.server;
public class StorageServiceFactory {
public static StorageService createStorageService(String endpoint, String accessKey, String accessSecret) {
if (endpoint.contains("aliyuncs.com")) {
return new OssStorageService(endpoint, accessKey, accessSecret);
} else {
throw new IllegalArgumentException("Unsupported storage type: " + endpoint);
}
}
}
\ No newline at end of file
......@@ -104,6 +104,12 @@ sa-token.sign.secret-key=4Jk2giMQw8D7lHJcU8fv8CXjSOhjp5Ee
##\u65E5\u5FD7\u914D\u7F6E
logging.config=classpath:logback-spring.xml
aliyun.oss.endPoint=oss-cn-shanghai.aliyuncs.com
aliyun.oss.host=infoloop-public-qa.oss-cn-shanghai.aliyuncs.com
aliyun.oss.accessKeyId=LTAIo9gcY5sjIZdN
aliyun.oss.accessKeySecret=gOOkVY4Euj3MSbPyg1tZvPbZbUssj5
aliyun.oss.bucket=infoloop-public-qa
##\u77ED\u4FE1\u914D\u7F6E
sms-config.accessKeyId=
sms-config.accessKeySecret=
......