代码展示
api接口层
@Api(tags = "文件相关接口")
@RequestMapping("/file")
public interface IFileApi {
@ApiOperation(value = "文件上传")
@PostMapping("/upload")
FileUrlResponse upload(@RequestPart("file") MultipartFile file, @RequestParam("bucket") String bucket);
@ApiOperation(value = "文件上传")
@PostMapping("/uploadByPath")
FileUrlResponse uploadByPath(@RequestPart("file") MultipartFile file, @RequestParam("bucket") String bucket, @RequestParam("path") String path);
@ApiOperation(value = "文件删除")
@PostMapping("/delete")
void delete(@RequestParam String bucket, @NotBlank(message = "文件id不能为空") @RequestParam String id);
@ApiOperation(value = "下载文件")
@GetMapping("/download")
void download(@Validated @RequestParam DownloadInputRequest downLoadInputDTO, @RequestParam HttpServletResponse response);
@ApiOperation(value = "批量下载文件")
@GetMapping("/batchDownload")
FileInputStreamResponse batchDownload(@Validated BatchDownloadInputRequest downLoadInputDTO);
@ApiOperation(value = "获取文件列表")
@GetMapping("/getList/{bucket}/**")
List<FileUrlResponse> getList(@PathVariable("bucket") String bucket, HttpServletRequest request);
}
config包配置
@Configuration
public class MinioConfig {
@Autowired
private MinioProperties minIoProperties;
@Bean
public MinioClient minioClient() {
MinioClient minioClient = MinioClient.builder().endpoint(minIoProperties.getUrl())
.credentials(minIoProperties.getAccessKey(), minIoProperties.getSecretKey()).build();
MinioUtil.minioClient = minioClient;
return minioClient;
}
}
controller层
@RestController
@Slf4j
public class FileServiceController implements IFileApi {
@Autowired
private MinoService minoService;
private final AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public FileUrlResponse upload(final MultipartFile file, final String bucket) {
FileServiceController.log.info("调用上传文件start");
final FileUrlResponse fileUrlResp = new FileUrlResponse();
this.fillFileUrlResponse(fileUrlResp, file);
fileUrlResp.setUrl(this.minoService.uploadFile(bucket, file));
FileServiceController.log.info("调用上传文件end");
return fileUrlResp;
}
@Override
public FileUrlResponse uploadByPath(MultipartFile file, String bucket, String path) {
FileServiceController.log.info("调用上传文件start");
final FileUrlResponse fileUrlResp = new FileUrlResponse();
this.fillFileUrlResponse(fileUrlResp, file);
fileUrlResp.setUrl(this.minoService.uploadFile(bucket, file, path));
FileServiceController.log.info("调用上传文件end");
return fileUrlResp;
}
/**
* 文件属性填充
*
* @param fileUrlResp 目标对象
* @param file 文件信息
*/
private void fillFileUrlResponse(final FileUrlResponse fileUrlResp, final MultipartFile file) {
final long size = file.getSize();
fileUrlResp.setFileSize(BigDecimal.valueOf(size)
.divide(new BigDecimal("1024"), 2, RoundingMode.HALF_DOWN));
fileUrlResp.setFileName(file.getOriginalFilename());
}
@Override
public void delete(final String bucket, @NotBlank(message = "文件路径不能为空") final String filepath) {
FileServiceController.log.info("调用删除文件start");
this.minoService.deleteFile(bucket, filepath);
FileServiceController.log.info("调用删除文件end");
}
@Override
public void download(final DownloadInputRequest downLoadInputDTO, final HttpServletResponse response) {
this.minoService.downLoadFile(downLoadInputDTO.getBucket(), downLoadInputDTO.getFileName(), downLoadInputDTO.getFilePath(), response);
}
@Override
public FileInputStreamResponse batchDownload(final BatchDownloadInputRequest downLoadInputDTO) {
final List<DownloadInputRequest> fileList = downLoadInputDTO.getFileList();
final InputStream[] inputStreams = new InputStream[fileList.size()];
for (int i = 0; i < fileList.size(); i++) {
final DownloadInputRequest downloadInputRequest = fileList.get(i);
final InputStream inputStream = this.minoService.downLoadFile(downloadInputRequest.getBucket(),
downloadInputRequest.getFileName(), downloadInputRequest.getFilePath());
inputStreams[i] = inputStream;
}
final FileInputStreamResponse fileInputStreamResponse = new FileInputStreamResponse();
fileInputStreamResponse.setInputStreams(inputStreams);
return fileInputStreamResponse;
}
@Override
public List<FileUrlResponse> getList(final String bucket, final HttpServletRequest request) {
// 获取完整的路径
final String uri = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
// 获取映射的路径
final String pattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
// 截取带“/”的参数
final String customPath = this.antPathMatcher.extractPathWithinPattern(pattern, uri);
return this.minoService.getList(bucket, customPath);
}
}
dto层
@Getter
@Setter
@ApiModel(value = "BatchDownloadInputRequest", description = "BatchDownloadInputRequest")
public class BatchDownloadInputRequest {
@NotNull(message = "文件集合不能为空")
@ApiModelProperty("文件集合")
List<DownloadInputRequest> fileList;
}
@Getter
@Setter
@ApiModel(value = "DownloadInputDTO", description = "下载文件DTO")
public class DownloadInputRequest {
@ApiModelProperty(value = "文件名称")
@NotBlank(message = "文件名称不能为空")
private String fileName;
@ApiModelProperty(value = "文件路径")
@NotBlank(message = "文件路径不能为空")
private String filePath;
@ApiModelProperty(value = "文件路径")
@NotBlank(message = "文件路径不能为空")
String bucket;
}
@Data
public class FileInputStreamResponse extends BaseDTO {
InputStream[] inputStreams;
}
@Data
public class FileUrlResponse extends BaseDTO {
private String url;
private BigDecimal fileSize;
private String fileName;
}
properties层
@Data
@Configuration
@ConfigurationProperties(prefix = "minio")
public class MinioProperties {
/**
* minio地址+端口号
*/
private String url;
/**
* minio用户名
*/
private String accessKey;
/**
* minio密码
*/
private String secretKey;
/**
* 桶
*/
private String bucket;
/**
* 文件访问地址(nginx地址)
* 注意:Bucket Policy:* Read Only策略 才可以访问
* 访问地址格式:地址:端口/Bucket名称/路径地址/文件名称
*/
private String webUrl;
}
service层
@Service
@Slf4j
public class MinoService {
@Autowired
private MinioProperties minioProperties;
public String uploadFile(String bucket, MultipartFile file) {
// 获取文件后缀名
final String suffix = FileUtil.getSuffix(file.getOriginalFilename());
// 2021-04-21/2021-04-21 12:12:12 -- 2023-03-31 18:02:19 废弃
// 2023/2023-03/2023-03-31/20230331180158.*
final String filePath = StrUtil.builder()
.append(DateUtil.format(new Date(), "yyyy/yyyy-MM/yyyy-MM-dd"))
.append("/")
.append(DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN))
.append(".")
.append(suffix)
.toString();
return uploadFile(bucket, file, filePath);
}
public String uploadFile(String bucket, final MultipartFile file, String filePath) {
if (StringUtils.isEmpty(bucket)) {
bucket = this.minioProperties.getBucket();
}
String result = null;
try {
result = MinioUtil.uploadFile(bucket, file, filePath);
} catch (final Exception e) {
CommonResponseEnum.SERVER_ERROR.assertFail("上传失败");
}
return this.minioProperties.getWebUrl() + "/" + this.minioProperties.getBucket() + "/" + result;
}
public void deleteFile(String bucket, final String path) {
if (StringUtils.isEmpty(bucket)) {
bucket = this.minioProperties.getBucket();
}
try {
MinioUtil.removeFile(bucket, path);
} catch (final Exception e) {
MinoService.log.error("deleteFile [{}]", e.getMessage());
}
}
public void downLoadFile(String bucket, final String fileName, final String filePath, final HttpServletResponse response) {
if (StringUtils.isEmpty(bucket)) {
bucket = this.minioProperties.getBucket();
}
try {
final InputStream inputStream = MinioUtil.downloadFile(bucket, filePath);
ServletUtil.write(response, inputStream, MediaType.APPLICATION_OCTET_STREAM_VALUE, fileName);
} catch (final Exception e) {
MinoService.log.error("downLoad [{}]", e.getMessage());
}
}
public InputStream downLoadFile(String bucket, final String fileName, final String filePath) {
if (StringUtils.isEmpty(bucket)) {
bucket = this.minioProperties.getBucket();
}
try {
return MinioUtil.downloadFile(bucket, filePath);
} catch (final Exception e) {
MinoService.log.error("downLoad [{}]", e.getMessage());
}
return null;
}
public List<FileUrlResponse> getList(final String bucket, final String prefix) {
final List<FileUrlResponse> list = MinioUtil.listFile(bucket, prefix);
return list;
}
}
utils工具层
public class MinioUtil {
public static final int DEFAULT_MULTIPART_SIZE = 10 * 1024 * 1024;
public static MinioClient minioClient;
/**
* 创建桶
*
* @param bucket
* @throws Exception
*/
public static void createBucket(final String bucket) throws Exception {
final boolean found = MinioUtil.minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucket).build());
if (!found) {
MinioUtil.minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());
}
}
/**
* 查看所有桶
*
* @return
* @throws Exception
*/
public List<String> getBuckets() throws Exception {
return MinioUtil.minioClient.listBuckets().stream().map(Bucket::name).collect(Collectors.toList());
}
/**
* 删除桶
*
* @param bucket
* @throws Exception
*/
public static void deleteBucket(final String bucket) throws Exception {
MinioUtil.minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucket).build());
}
/**
* 上传文件
*
* @param file
* @return
* @throws Exception
*/
public static String uploadFile(final String bucket, final MultipartFile file, String filePath) throws Exception {
final ContentInfo info = ContentInfoUtil.findExtensionMatch(file.getOriginalFilename());
final String contentType = info == null ? "application/octet-stream" : info.getMimeType();
// 获取文件后缀名
final String suffix = FileUtil.getSuffix(StrUtil.isBlank(file.getOriginalFilename()) ? file.getName() : file.getOriginalFilename());
String end = "." + suffix;
if (!filePath.endsWith(end)) {
filePath = filePath + "/" + DateUtil.today() + "/" + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_PATTERN) + end;
} else {
// 覆盖文件
MinioUtil.minioClient.putObject(PutObjectArgs.builder()
.bucket(bucket)
.object(filePath)
.contentType(contentType)
.stream(file.getInputStream(), -1, MinioUtil.DEFAULT_MULTIPART_SIZE)
.build());
// 修改自定义路径, 重新上传文件
String[] split = filePath.split("/");
String[] fileName = split[split.length - 1].split("\\.");
split[split.length - 1] = DateUtil.today() + "/" + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN) + "." + fileName[fileName.length - 1];
filePath = IterUtil.join(Arrays.stream(split).iterator(), "/");
}
MinioUtil.minioClient.putObject(PutObjectArgs.builder()
.bucket(bucket)
.object(filePath)
.contentType(contentType)
.stream(file.getInputStream(), -1, MinioUtil.DEFAULT_MULTIPART_SIZE)
.build());
return filePath;
}
public static void main(final String[] args) {
final ContentInfo info1 = ContentInfoUtil.findExtensionMatch("123.svg");
System.out.println(info1.getMimeType());
}
/**
* 上传文件,流的方式
*
* @param inputStream
* @param fileName
* @return
* @throws Exception
*/
public static String uploadFile(final InputStream inputStream, final String bucket, final String fileName) throws Exception {
// 获取文件后缀名
final String suffix = FileUtil.getSuffix(fileName);
// yyyyMMdd/yyyyMMddHHmmssSSS.suffix
final String filePath = StrUtil.builder()
.append(DateUtil.today())
.append("/")
.append(DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_PATTERN))
.append(".")
.append(suffix)
.toString();
MinioUtil.minioClient.putObject(PutObjectArgs.builder()
.bucket(bucket)
.object(filePath)
.stream(inputStream, -1, MinioUtil.DEFAULT_MULTIPART_SIZE)
.build());
return filePath;
}
/**
* 下载文件
*
* @param bucket
* @param fileName
* @return
* @throws Exception
*/
public static InputStream downloadFile(final String bucket, final String fileName) throws Exception {
return MinioUtil.minioClient.getObject(GetObjectArgs.builder()
.bucket(bucket)
.object(fileName).build());
}
/**
* 删除文件
*
* @param fileName
* @throws Exception
*/
public static void removeFile(final String bucket, final String fileName) throws Exception {
MinioUtil.minioClient.removeObject(RemoveObjectArgs.builder()
.bucket(bucket)
.object(fileName).build());
}
public static List<FileUrlResponse> listFile(final String bucket, final String prefix) {
final ListObjectsArgs args = ListObjectsArgs.builder().bucket(bucket).prefix(prefix).recursive(true).build();
final Iterable<Result<Item>> results = MinioUtil.minioClient.listObjects(args);
final ArrayList<FileUrlResponse> result = new ArrayList<>();
for (final Result<Item> item : results) {
try {
if (item.get().isDir()) {
continue;
}
final String name = item.get().objectName();
final FileUrlResponse response = new FileUrlResponse();
final String[] split = name.split("/");
response.setFileName(split[split.length - 1]);
response.setUrl(name);
result.add(response);
} catch (final Exception e) {
}
}
return result;
}
}
附加配置
引入minio的pom文件
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>7.1.0</version>
</dependency>
<dependency>
<groupId>com.j256.simplemagic</groupId>
<artifactId>simplemagic</artifactId>
<version>1.16</version>
</dependency>
yml配置
spring:
# 配置文件上传大小限制
servlet:
multipart:
max-file-size: 200MB
max-request-size: 200MB
enabled: true
minio:
endpoint: http://127.0.0.1:9000
accessKey: 你的minio账号 #minioadmin
secretKey: 你的minio密码 #minioadmin
bucketName: 所要上传桶的名称 #bucket-qhj