文件存储服务

This commit is contained in:
jiang 2024-06-26 10:42:08 +08:00
parent b5f03d1c1a
commit 166a36c7b6
10 changed files with 173 additions and 167 deletions

View File

@ -9,7 +9,6 @@
<version>3.6.4</version>
</parent>
<groupId>com.bonus.mongoDB</groupId>
<artifactId>bonus-modules-mongodb</artifactId>
<properties>

View File

@ -1,8 +1,8 @@
package com.bonus.obs.controller;
import com.bonus.common.core.domain.R;
import com.bonus.common.core.utils.file.FileUtils;
import com.bonus.obs.service.ObsService;
import com.bonus.obs.utils.FileUtils;
import com.obs.services.model.DeleteObjectResult;
import com.obs.services.model.ObsObject;
import com.obs.services.model.PutObjectResult;

View File

@ -10,10 +10,10 @@ public interface ObsService {
/**
* 文件上传
*
* @param param 文件流
* @param file 文件流
* @return 文件信息
*/
R<PutObjectResult> uploadFile(MultipartFile param);
R<PutObjectResult> uploadFile(MultipartFile file);
/**
* 删除文件从OBS
@ -28,4 +28,13 @@ public interface ObsService {
* @param objectKey 文件在OBS中的键
*/
public R<ObsObject> downloadFile(String objectKey);
/**
* 文件上传
*
* @param files 文件流
* @return 文件信息
*/
R<PutObjectResult> uploadFiles(MultipartFile[] files);
}

View File

@ -1,8 +1,8 @@
package com.bonus.obs.service.impl;
import com.bonus.common.core.domain.R;
import com.bonus.common.core.utils.file.FileUtils;
import com.bonus.obs.service.ObsService;
import com.bonus.obs.utils.FileUtils;
import com.bonus.obs.utils.ObsUtils;
import com.obs.services.model.DeleteObjectResult;
import com.obs.services.model.ObsObject;
@ -21,16 +21,15 @@ public class ObsServiceImpl implements ObsService {
/**
* 文件上传
*
* @param param 文件流
* @param file 文件流
* @return 文件信息
*/
@Override
public R<PutObjectResult> uploadFile(MultipartFile param) {
public R<PutObjectResult> uploadFile(MultipartFile file) {
try {
String objectKey = param.getOriginalFilename();
File file = FileUtils.multipartFileToFile(param);
String objectKey = file.getOriginalFilename();
objectKey = FileUtils.generateObjectName(objectKey);
return obsUtils.uploadFile(objectKey, file);
return obsUtils.uploadFile(objectKey, FileUtils.multipartFileToFile(file));
} catch (Exception e) {
return R.fail(e.getMessage());
}
@ -55,4 +54,15 @@ public class ObsServiceImpl implements ObsService {
public R<ObsObject> downloadFile(String objectKey) {
return obsUtils.downloadFile(objectKey);
}
/**
* 文件上传
*
* @param files 文件流
* @return 文件信息
*/
@Override
public R<PutObjectResult> uploadFiles(MultipartFile[] files) {
return null;
}
}

View File

@ -1,47 +1,40 @@
package com.bonus.oss.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* 配置OSS对象存储服务的属性类
* 使用@ConfigurationProperties注解将类绑定到配置文件中以oss为前缀的属性上
* 使用@Configuration注解将此类标记为一个配置类它会被Spring Boot自动检测并用于配置上下文
* 使用@Data注解来自动生成getter和setter方法简化了属性访问
*/
@Configuration
@ConfigurationProperties(prefix = "oss")
@Data
public class OSSConfig {
/**
* OSS的访问密钥ID
* 用于身份验证的重要凭证之一
*/
private String accessKeyId;
/**
* OSS的访问密钥秘钥
* 用于身份验证的重要凭证之一应保密
*/
private String accessKeySecret;
/**
* OSS的存储桶名称
* 存储桶是OSS中用于存储对象的容器
*/
private String bucket;
/**
* OSS的Endpoint
* Endpoint指定了与OSS交互时使用的域名或区域
*/
private String endpoint;
// Getters and Setters
public String getAccessKeyId() {
return accessKeyId;
}
public void setAccessKeyId(String accessKeyId) {
this.accessKeyId = accessKeyId;
}
public String getAccessKeySecret() {
return accessKeySecret;
}
public void setAccessKeySecret(String accessKeySecret) {
this.accessKeySecret = accessKeySecret;
}
public String getBucket() {
return bucket;
}
public void setBucket(String bucket) {
this.bucket = bucket;
}
public String getEndpoint() {
return endpoint;
}
public void setEndpoint(String endpoint) {
this.endpoint = endpoint;
}
}

View File

@ -2,9 +2,11 @@ package com.bonus.oss.controller;
import com.aliyun.oss.model.OSSObject;
import com.bonus.common.core.domain.R;
import com.bonus.common.core.utils.StringUtils;
import com.bonus.common.core.utils.file.FileUtils;
import com.bonus.oss.domain.OssInfo;
import com.bonus.oss.service.OssService;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
@ -13,15 +15,16 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/oss")
public class OssController {
@ -34,9 +37,9 @@ public class OssController {
* @param file 文件流
* @return 文件信息
*/
@PostMapping("/upload")
public R<OssInfo> upload(MultipartFile file) {
return ossService.upload(file);
@PostMapping("/uploadFile")
public R<OssInfo> uploadFile(MultipartFile file) {
return ossService.uploadFile(file);
}
@ -54,21 +57,30 @@ public class OssController {
/**
* 文件下载
*
* @param ossFilePath oss文件存储地址
* @param objectKey oss文件存储地址
*/
@GetMapping("/download")
public void download(HttpServletRequest request, HttpServletResponse response, @RequestParam String ossFilePath) {
try (OSSObject ossObject = ossService.download(ossFilePath)) {
if (ossObject == null) {
@GetMapping("/downloadFile")
public void downloadFile(HttpServletResponse response, @RequestParam String objectKey) throws UnsupportedEncodingException {
R<OSSObject> ossObjectR = ossService.downloadFile(objectKey);
if (ossObjectR.getData() == null) {
response.setStatus(HttpStatus.NOT_FOUND.value());
return;
}
InputStream inputStream = ossObject.getObjectContent();
String safeFileName = FileUtils.getFileNameFromPath(ossFilePath);
OSSObject ossObject = ossObjectR.getData();
String safeFileName = FileUtils.getName(objectKey); // 假设这个方法进行了恰当的文件名清理和验证
if (!StringUtils.hasText(safeFileName)) {
response.setStatus(HttpStatus.BAD_REQUEST.value());
return;
}
String encodedFileName = URLEncoder.encode(safeFileName, StandardCharsets.UTF_8.toString());
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + encodedFileName + "\"");
try (OutputStream outputStream = response.getOutputStream()) {
try (InputStream inputStream = ossObject.getObjectContent();
OutputStream outputStream = response.getOutputStream()) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
@ -77,10 +89,11 @@ public class OssController {
outputStream.flush();
} catch (IOException e) {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
}
} catch (IllegalArgumentException e) {
response.setStatus(HttpStatus.BAD_REQUEST.value());
} catch (Exception e) {
// 记录异常信息到日志
log.error("文件下载过程中出现未知异常,原因是:", e);
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
}
}
@ -89,11 +102,11 @@ public class OssController {
/**
* 文件删除
*
* @param ossFilePath 文件路径
* @param objectKey 文件路径
* @return 删除结果
*/
@PostMapping("/delete")
public R<T> delete(@RequestParam String ossFilePath) {
return ossService.delete(ossFilePath);
@PostMapping("/deleteFile")
public R<T> deleteFile(@RequestParam String objectKey) {
return ossService.deleteFile(objectKey);
}
}

View File

@ -1,9 +1,7 @@
package com.bonus.oss.domain;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* OssInfo类用于封装OSSObject Storage Service对象的相关信息
@ -41,4 +39,5 @@ public class OssInfo {
* Bucket是OSS中用于存储对象的容器每个对象必须属于某个Bucket
*/
private String bucketName;
}

View File

@ -16,23 +16,23 @@ public interface OssService {
* @param param 文件流
* @return 文件信息
*/
R<OssInfo> upload(MultipartFile param);
R<OssInfo> uploadFile(MultipartFile param);
/**
* 文件下载
*
* @param ossFilePath oss文件存储地址
* @param objectKey oss文件存储地址
* @return OSSObject对象
*/
OSSObject download(String ossFilePath);
R<OSSObject> downloadFile(String objectKey);
/**
* 文件删除
*
* @param ossFilePath oss文件存储地址
* @param objectKey oss文件存储地址
* @return 操作结果
*/
R<T> delete(String ossFilePath);
R<T> deleteFile(String objectKey);
/**
* 多文件上传
*

View File

@ -1,7 +1,9 @@
package com.bonus.oss.service.impl;
import com.alibaba.nacos.common.utils.UuidUtils;
import com.aliyun.oss.model.OSSObject;
import com.bonus.common.core.domain.R;
import com.bonus.common.core.utils.StringUtils;
import com.bonus.common.core.utils.file.FileUtils;
import com.bonus.oss.domain.OssInfo;
import com.bonus.oss.service.OssService;
@ -15,6 +17,9 @@ import javax.annotation.Resource;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Service
public class OssServiceImpl implements OssService {
@ -22,19 +27,25 @@ public class OssServiceImpl implements OssService {
@Resource
private OssUtils ossUtils;
private ExecutorService executor = Executors.newFixedThreadPool(5); // 限制并发线程数为5
/**
* 文件上传
*
* @param multipartFile 文件流
* @param file 文件流
* @return 文件信息
*/
@Override
public R<OssInfo> upload(MultipartFile multipartFile) {
public R<OssInfo> uploadFile(MultipartFile file) {
if (ObjectUtils.isEmpty(file)) {
return R.fail("File is null.");
}
try {
String objectKey = multipartFile.getOriginalFilename();
File file = FileUtils.multipartFileToFile(multipartFile);
String originalFilename = Objects.requireNonNull(file.getOriginalFilename(), "文件名不能为空");
String extension = originalFilename.substring(originalFilename.lastIndexOf('.'));
String objectKey = UuidUtils.generateUuid() + extension;
objectKey = FileUtils.generateObjectName(objectKey);
return ossUtils.upload(objectKey, file);
return ossUtils.upload(objectKey, FileUtils.multipartFileToFile(file));
} catch (Exception e) {
return R.fail("File upload failed.");
}
@ -43,25 +54,28 @@ public class OssServiceImpl implements OssService {
/**
* 文件下载
*
* @param ossFilePath oss文件存储地址
* @param objectKey oss文件存储地址
*/
@Override
public OSSObject download(String ossFilePath) {
return ossUtils.download(ossFilePath);
public R<OSSObject> downloadFile(String objectKey) {
if (ObjectUtils.isEmpty(objectKey)) {
return R.fail("objectKey is null.");
}
return ossUtils.download(objectKey);
}
/**
* 文件删除
*
* @param ossFilePath oss文件存储地址
* @param objectKey oss文件存储地址
*/
@Override
public R<T> delete(String ossFilePath) {
public R<T> deleteFile(String objectKey) {
try {
if (ObjectUtils.isNotEmpty(ossFilePath)) {
return ossUtils.delete(ossFilePath);
if (ObjectUtils.isNotEmpty(objectKey)) {
return ossUtils.delete(objectKey);
} else {
return R.fail("ossFilePath is null.");
return R.fail("objectKey is null.");
}
} catch (Exception e) {
return R.fail("File delete failed.");
@ -79,12 +93,14 @@ public class OssServiceImpl implements OssService {
try {
List<OssInfo> ossInfos = new ArrayList<>();
for (MultipartFile multipartFile : files) {
String objectKey = multipartFile.getOriginalFilename();
String originalFilename = Objects.requireNonNull(multipartFile.getOriginalFilename(), "文件名不能为空");
String extension = originalFilename.substring(originalFilename.lastIndexOf('.'));
String objectKey = UuidUtils.generateUuid() + extension;
File file = FileUtils.multipartFileToFile(multipartFile);
objectKey = FileUtils.generateObjectName(objectKey);
ossInfos.add(ossUtils.upload(objectKey, file).getData());
}
return R.ok(ossInfos);
return R.ok(ossInfos, "文件上传成功");
} catch (Exception e) {
return R.fail("File upload failed.");
}

View File

@ -6,21 +6,20 @@ import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.ObjectMetadata;
import com.bonus.common.core.domain.R;
import com.bonus.common.core.text.Convert;
import com.bonus.common.core.utils.file.FileUtils;
import com.bonus.oss.config.OSSConfig;
import com.bonus.oss.domain.OssInfo;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.poi.ss.formula.functions.T;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.File;
/**
* OSS存储工具
* OSS存储服务
*/
@Service
public class OssUtils {
@ -31,13 +30,12 @@ public class OssUtils {
private OSSConfig ossConfig;
/**
* oss 客户端
* OSS客户端
*/
private OSS ossClient;
/**
* 初始化信息
* 构建OSS客户端实例
* 初始化OSS客户端
*/
@PostConstruct
public void init() {
@ -45,22 +43,20 @@ public class OssUtils {
}
/**
* 上传文件
* 上传文件到OSS
*
* @param file File
* @return 包含上传文件信息的R对象
* @param objectKey 文件在OSS中的键
* @param file 要上传的文件
* @return 包含上传文件信息的结果对象
*/
public R<OssInfo> upload(String objectKey, File file) {
try {
// 上传文件到OSS
ossClient.putObject(ossConfig.getBucket(), objectKey, file);
// 获取上传文件的信息并返回
return R.ok(getInfo(objectKey));
return R.ok(getInfo(objectKey), "文件上传成功");
} catch (Exception e) {
LOGGER.error("文件上传失败", e);
return R.fail("文件上传失败");
} finally {
// 删除临时文件
if (!file.delete()) {
LOGGER.warn("临时文件删除失败");
}
@ -68,102 +64,73 @@ public class OssUtils {
}
/**
* 获取文件信息
* 获取文件在OSS中的信息
*
* @param ossFilePath 文件路径
* @return 包含文件信息的OssInfo对象
* @param objectKey 文件路径
* @return 文件信息对象
*/
public OssInfo getInfo(String ossFilePath) {
public OssInfo getInfo(String objectKey) {
try {
// 获取文件的元数据
ObjectMetadata objectMetadata = ossClient.getObjectMetadata(ossConfig.getBucket(), ossFilePath);
OssInfo ossInfo = OssInfo.builder()
ObjectMetadata objectMetadata = ossClient.getObjectMetadata(ossConfig.getBucket(), objectKey);
return OssInfo.builder()
.name(FileUtils.getName(objectKey))
.bucketName(ossConfig.getBucket())
.fileType(objectMetadata.getContentType())
.length(Convert.toStr(objectMetadata.getContentLength()))
.path(ossFilePath).build();
return ossInfo;
.path(objectKey).build();
} catch (Exception e) {
LOGGER.error("获取文件信息失败", e);
return null;
return OssInfo.builder().build();
}
}
/**
* 下载文件
* 从OSS下载文件
*
* @param ossFilePath 文件路径
* @return OSSObject对象
* @param objectKey 文件路径
* @return OSSObject对象包含下载的文件
*/
public OSSObject download(String ossFilePath) {
if (ObjectUtils.isEmpty(ossFilePath)) {
return null;
}
if (!doesObjectExist(ossConfig.getBucket(), ossFilePath)) {
return null;
}
try {
// 从OSS下载文件
return ossClient.getObject(ossConfig.getBucket(), ossFilePath);
} catch (Exception e) {
LOGGER.error("文件下载失败", e);
return null;
}
}
/**
* 删除文件
*
* @param ossFilePath 文件路径
*/
public R<T> delete(String ossFilePath) {
try {
if (!doesObjectExist(ossConfig.getBucket(), ossFilePath)) {
public R<OSSObject> download(String objectKey) {
if (doesObjectExist(ossConfig.getBucket(), objectKey)) {
return R.fail("文件不存在");
}
// 从OSS删除文件
ossClient.deleteObject(ossConfig.getBucket(), ossFilePath);
try {
return R.ok(ossClient.getObject(ossConfig.getBucket(), objectKey));
} catch (Exception e) {
LOGGER.error("文件下载失败", e);
return R.fail("文件下载失败");
}
}
/**
* 删除OSS中的文件
*
* @param objectKey 文件路径
* @return 删除操作的结果
*/
public R<T> delete(String objectKey) {
try {
if (doesObjectExist(ossConfig.getBucket(), objectKey)) {
return R.fail("文件不存在");
}
ossClient.deleteObject(ossConfig.getBucket(), objectKey);
return R.ok(null,"文件删除成功");
} catch (Exception e) {
LOGGER.error("文件删除失败", e);
return R.fail("文件删除失败");
}
return R.ok();
}
/**
* 复制文件
*
* @param source 源文件路径
* @param path 目标文件路径
*/
public R<T> copy(String source, String path) {
if (!doesObjectExist(ossConfig.getBucket(), source)) {
return R.fail("文件不存在");
}
try {
// 复制文件
ossClient.copyObject(ossConfig.getBucket(), source, ossConfig.getBucket(), path);
return R.ok();
} catch (Exception e) {
LOGGER.error("文件复制失败", e);
return R.fail("文件复制失败");
}
}
/**
* 判断文件是否存在
* 检查OSS中是否存在指定的文件
*
* @param bucketName Bucket名称
* @param objectName 不包含Bucket名称在内的Object完整路径
* @return 如果文件存在返回true否则返回false
* @param objectKey 文件路径
* @return 如果文件存在返回true否则返回false
*/
private boolean doesObjectExist(String bucketName, String objectName) {
private boolean doesObjectExist(String bucketName, String objectKey) {
try {
// 判断文件是否存在
return ossClient.doesObjectExist(bucketName, objectName);
return !ossClient.doesObjectExist(bucketName, objectKey);
} catch (Exception e) {
LOGGER.error("判断文件是否存在失败", e);
return true;