目录
本文提供文件操作接口的实现(上传+下载)
附件资源表实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@TableName("attachment_info")
@ApiModel(value="AttachmentInfoEntity", description="附件资源表")
public class AttachmentInfoEntity extends BaseEntity<AttachmentInfoEntity> {
@ApiModelProperty(value = "附件id")
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@ApiModelProperty(value = "关联id")
@TableField("file_id")
private String fileId;
@ApiModelProperty(value = "关联业务模块(存储桶名)")
@TableField("bucket_name")
private String bucketName;
@ApiModelProperty(value = "请求头文件类型")
@TableField("content_type")
private String contentType;
@ApiModelProperty(value = "文件类型 默认 普通文件 file ,可选 电子签章 electronic_sign_file")
@TableField("file_type")
private String fileType;
@ApiModelProperty(value = "文件名称")
@TableField("file_name")
private String fileName;
@ApiModelProperty(value = "文件大小")
@TableField("file_size")
private BigDecimal fileSize;
@ApiModelProperty(value = "文件存储路径")
@TableField("file_path")
private String filePath;
@ApiModelProperty(value = "文件内容")
@TableField("file_data")
private byte[] fileData;
@ApiModelProperty(value = "删除标识(0未删除/1已删除)")
@TableField("is_delete")
@TableLogic(value="0",delval="1")
private Integer isDelete;
}
具体代码实现
controller层
@Api(tags = "文件操作相关接口")
@RestController
@RequestMapping("/file")
@Slf4j
public class FileStorageController {
@Resource(name = "localFileStorageServiceImpl")
private FileStorageService fileStorageService;
@ApiOperation("上传文件")
@PostMapping(value = "/upload")
@ApiImplicitParams({
@ApiImplicitParam(name = "file", value = "文件", required = true, dataType = "MultipartFile", paramType = "form"),
@ApiImplicitParam(name = "bucketName", value = "桶名(以小写字母或数字开头和结尾,中间可以包含小写字母、数字、点(.)或短横线(-)的字符串)", required = true, dataType = "String", paramType = "form")
})
@Transactional(rollbackFor = Exception.class)
public ApiResult<Object> upload(@RequestPart(value = "file") MultipartFile fileList, @RequestParam(value = "bucketName") String bucketName) {
return fileStorageService.upload(fileList, bucketName);
}
@ApiOperation("下载指定的MinIO文件2")
@GetMapping("view2")
public void view2(@RequestParam("fileId") String fileId, HttpServletResponse httpServletResponse) {
fileStorageService.download(fileId, httpServletResponse);
}
}
service层
public interface FileStorageService {
/**
* 上传文件
*
* @param fileList 需要上传的文件
* @param bucketName MinIo文件服务器桶名称(从枚举类中获取)
* @return 上传成功之后保存到文档服务器上的文件名称
*/
ApiResult<Object> upload(MultipartFile fileList, String bucketName);
/**
* 文件下载
*
* @param fileId 文件id
*/
void download(String fileId, HttpServletResponse httpServletResponse);
}
上传到MinIO服务器
pom依赖
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.2.1</version>
<exclusions>
<exclusion>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.12.0</version>
</dependency>
yml配置
minio:
endpoint: http://10.10.xx.xxx:xxxx
accesskey: admin
secretkey: admin123
MinIO配置
@Data
@Component
@ConfigurationProperties(prefix = "minio")
public class MinIOInfo {
// MinIo服务器地址
private String endpoint;
// MinIo服务器账号
private String accesskey;
// MinIo服务器密码
private String secretkey;
}
@Configuration
public class MinIOConfiguration {
@Resource
private MinIOInfo minIOInfo;
@Bean
public MinioClient minioClient() {
// 构建MinioClient对象
return MinioClient.builder()
.endpoint(minIOInfo.getEndpoint())
.credentials(minIOInfo.getAccesskey(), minIOInfo.getSecretkey())
.build();
}
}
服务实现类
@Service
@Slf4j
@RequiredArgsConstructor
public class MinioFileStorageServiceImpl implements FileStorageService {
private final MinioClient minioClient;
private final AttachmentInfoService attachmentInfoService;
private static final String FOLDER = "/usr/local/uploadFile/";
/**
* 将文件上传到指定的MinIO存储桶中
*
* @param file 要上传的文件
* @param bucketName 存储桶名称
* @return 返回上传到MinIO后保存的文件名
* @throws Exception 如果上传过程中发生错误,则抛出异常
*/
@Override
@Transactional(rollbackFor = Exception.class)
public ApiResult<Object> upload(MultipartFile file, String bucketName) {
try {
log.info("开始上传文件,桶名:{}", bucketName);
// 检查并创建存储桶(如果不存在)
boolean exist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
if (!exist) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
log.info("创建存储桶:{}", bucketName);
}
// 获取文件原始名称和内容类型
String orgName = file.getOriginalFilename();
String contentType = file.getContentType();
log.info("上传文件:文件名={}, 内容类型={}", orgName, contentType);
// 检查文件名是否合法
if (StringUtils.isEmpty(orgName) || orgName.contains("..")) {
throw new BizException("文件名不合法,请检查上传的文件");
}
// 生成唯一文件名
String fileName = DateUtil.current() + "." + StringUtils.substringAfterLast(orgName, ".");
// 提取文件类型
String fileType = StringUtils.substringAfterLast(orgName, ".");
// 准备上传文件的参数和流
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
.bucket(bucketName)
.contentType(contentType)
.object(fileName)
.stream(file.getInputStream(), file.getSize(), -1)
.build();
// 上传文件到MinIO
minioClient.putObject(putObjectArgs);
log.info("文件已上传到 MinIO:存储桶={}, 文件名={}", bucketName, fileName);
AttachmentInfoEntity attachmentInfo = new AttachmentInfoEntity()
.setFileId(IdUtil.simpleUUID())
.setFileName(orgName)
.setFilePath(fileName)
.setContentType(contentType)
.setBucketName(bucketName)
.setFileType(fileType)
.setFileSize(BigDecimal.valueOf(file.getSize()));
attachmentInfoService.save(attachmentInfo);
return ApiResult.success(attachmentInfo.getFileId());
} catch (Exception e) {
log.error("文件上传失败:{}", e.getMessage());
return ApiResult.fail();
}
}
/**
* 从MinIO存储系统下载文件
*
* @param fileId 文件id
*/
@Override
public void download(String fileId, HttpServletResponse response) {
try {
log.info("开始下载文件,文件id为:{}", fileId);
// 获取文件信息
AttachmentInfoEntity attachment = attachmentInfoService.getAttachmentInfoByFileId(fileId);
if (attachment == null) {
throw new BizException("文件不存在,请检查文件ID");
}
// 检查存储桶是否存在
String bucketName = attachment.getBucketName();
boolean exist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
if (!exist) {
throw new BizException("存储桶不存在:{}", bucketName);
}
// 设置响应头
String fileName = attachment.getFileName();
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
response.setCharacterEncoding("UTF-8");
response.setContentType(attachment.getContentType());
// 从 MinIO 下载文件
String objectName = attachment.getFilePath();
GetObjectArgs getObjectArgs = GetObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build();
try (InputStream in = minioClient.getObject(getObjectArgs)) {
IoUtil.copy(in, response.getOutputStream());
}
log.info("文件下载成功:文件ID={}, 存储桶={}, 文件名={}", fileId, bucketName, attachment.getFileName());
} catch (Exception e) {
log.error("文件下载服务器错误:{}", e.getMessage());
throw new BizException("文件下载服务器错误");
}
}
}
保存到数据库
@Service
@Slf4j
@RequiredArgsConstructor
public class MinioFileStorageServiceImpl implements FileStorageService {
private final MinioClient minioClient;
private final AttachmentInfoService attachmentInfoService;
private static final String FOLDER = "/usr/local/uploadFile/";
/**
* 将文件上传到指定的MinIO存储桶中
*
* @param file 要上传的文件
* @param bucketName 存储桶名称
* @return 返回上传到MinIO后保存的文件名
* @throws Exception 如果上传过程中发生错误,则抛出异常
*/
@Override
@Transactional(rollbackFor = Exception.class)
public ApiResult<Object> upload(MultipartFile file, String bucketName) {
try {
log.info("开始上传文件,桶名:{}", bucketName);
// 检查并创建存储桶(如果不存在)
boolean exist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
if (!exist) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
log.info("创建存储桶:{}", bucketName);
}
// 获取文件原始名称和内容类型
String orgName = file.getOriginalFilename();
String contentType = file.getContentType();
log.info("上传文件:文件名={}, 内容类型={}", orgName, contentType);
// 检查文件名是否合法
if (StringUtils.isEmpty(orgName) || orgName.contains("..")) {
throw new BizException("文件名不合法,请检查上传的文件");
}
// 生成唯一文件名
String fileName = DateUtil.current() + "." + StringUtils.substringAfterLast(orgName, ".");
// 提取文件类型
String fileType = StringUtils.substringAfterLast(orgName, ".");
// 准备上传文件的参数和流
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
.bucket(bucketName)
.contentType(contentType)
.object(fileName)
.stream(file.getInputStream(), file.getSize(), -1)
.build();
// 上传文件到MinIO
minioClient.putObject(putObjectArgs);
log.info("文件已上传到 MinIO:存储桶={}, 文件名={}", bucketName, fileName);
AttachmentInfoEntity attachmentInfo = new AttachmentInfoEntity()
.setFileId(IdUtil.simpleUUID())
.setFileName(orgName)
.setFilePath(fileName)
.setContentType(contentType)
.setBucketName(bucketName)
.setFileType(fileType)
.setFileSize(BigDecimal.valueOf(file.getSize()));
attachmentInfoService.save(attachmentInfo);
return ApiResult.success(attachmentInfo.getFileId());
} catch (Exception e) {
log.error("文件上传失败:{}", e.getMessage());
return ApiResult.fail();
}
}
/**
* 从MinIO存储系统下载文件
*
* @param fileId 文件id
*/
@Override
public void download(String fileId, HttpServletResponse response) {
try {
log.info("开始下载文件,文件id为:{}", fileId);
// 获取文件信息
AttachmentInfoEntity attachment = attachmentInfoService.getAttachmentInfoByFileId(fileId);
if (attachment == null) {
throw new BizException("文件不存在,请检查文件ID");
}
// 检查存储桶是否存在
String bucketName = attachment.getBucketName();
boolean exist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
if (!exist) {
throw new BizException("存储桶不存在:{}", bucketName);
}
// 设置响应头
String fileName = attachment.getFileName();
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
response.setCharacterEncoding("UTF-8");
response.setContentType(attachment.getContentType());
// 从 MinIO 下载文件
String objectName = attachment.getFilePath();
GetObjectArgs getObjectArgs = GetObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build();
try (InputStream in = minioClient.getObject(getObjectArgs)) {
IoUtil.copy(in, response.getOutputStream());
}
log.info("文件下载成功:文件ID={}, 存储桶={}, 文件名={}", fileId, bucketName, attachment.getFileName());
} catch (Exception e) {
log.error("文件下载服务器错误:{}", e.getMessage());
throw new BizException("文件下载服务器错误");
}
}
}