Minio是一款开源的云存储服务器,旨在为企业和个人用户提供简单、易用、高性能的对象存储服务。与传统的云存储产品不同,Minio是构建在HTTP服务之上的对象存储服务器,它支持AWS S3 API,可以为用户提供简单易用的对象存储服务,可用于存储各种类型的文件,包括图片、音频、视频等。
我们需要先到minio官网进行下载:下载地址
下载完成后,由于minio.exe是不能直接执行的,需要cmd进入下载目录执行命令:
minio.exe server D:\data
出现以上界面说明启动成功,打开浏览器访问:http://localhost:9000,就可以看到minio自带的管理后台了,登录的用户名和密码都是minioadmin
由于每次都需要打开cmd窗口运行命令后才能访问minio,关掉就不能访问了,非常不方便,所以我们可以将minio集成到服务中,这样就不用每次都用cmd启动了。
集成minio到服务需要下载WinSW.exe,下载地址
下载完成后将WinSW.exe和nimio.exe放到一个自定义的目录下,可以将WinSW.exe重命名成一个喜欢的名字,比如:minio-server.exe
在目录下新建一个minio-server.xml文件,内容如下:
minio-server minio-server minio文件存储服务器 %BASE%\minio.exe server "%BASE%\data"%BASE%\logs 10240 yyyyMMdd 00:00:005 yyyyMMdd
需在环境变量中新建MINIO_HOME,值就是存放minio.exe的目录
打开cmd进入到目录下,运行命令安装minio服务:
minio-server.exe install
如果想卸载minio-server,运行:
minio-server.exe uninstall
访问http://localhost:9000,出现minio的管理后台,说明服务安装成功
io.minio minio8.4.3
minio: url: http://localhost:9000 access-key: minioadmin secret-key: minioadmin bucket: test
@Data @Configuration @ConfigurationProperties(prefix = "minio") public class MinioConfig { private String accessKey; private String secretKey; private String url; @Bean public MinioClient minioClient() { try { return MinioClient.builder().endpoint(url).credentials(accessKey, secretKey).build(); }catch (Exception e) { throw new RuntimeException("初始化MinioClient失败", e); } } }
@Component public class MinioUtils { @Resource private MinioClient minioClient; @Value("${minio.bucket}") private String bucketName; /** * 判断存储桶是否存在 * * @return 桶是否存在 */ public boolean bucketExists() { try { return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); } catch (Exception e) { throw new RuntimeException("桶不存在", e); } } /** * 创建桶 */ public void makeBucket() { try { minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); } catch (Exception e) { throw new RuntimeException("创建桶失败", e); } } /** * 上传单个对象 * * @param file 文件 * @return 上传成功 */ public SysResult putObject(MultipartFile file) { if (file == null || file.isEmpty()) throw new IllegalArgumentException("文件不能为空"); try (InputStream inputStream = file.getInputStream()){ //检查存储桶是否存在 if (!bucketExists()) makeBucket(); // 获取文件名 String fileName = file.getOriginalFilename(); assert fileName != null; // 生成随机的对象名称 String objectName = IdUtils.simpleUUID() + "." + FileUtil.getFileSuffix(fileName); // 添加自定义的元数据 Mapmeta = new HashMap<>(); meta.put("file-name", fileName); // 添加对象 minioClient.putObject(PutObjectArgs.builder() .bucket(bucketName) .object(objectName) .userMetadata(meta) .stream(inputStream, file.getSize(), -1) .contentType(file.getContentType()) .build()); return SysResult.success("上传成功", objectName); } catch (Exception e) { throw new RuntimeException("上传失败", e); } } /** * 上传多个对象 * * @param files 文件数组 * @return 上传成功 */ public SysResult putObject(MultipartFile[] files) { for (MultipartFile file : files) { putObject(file); } return SysResult.success("上传成功"); } /** * 删除一个对象 * * @param objectName 对象名称 * @return 是否删除成功 */ public boolean removeObject(String objectName) { if (bucketExists()) { try { minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); return true; } catch (Exception e) { throw new RuntimeException("删除失败", e); } } return false; } /** * 删除多个对象,返回删除失败的对象列表 * * @param objectNames 对象List * @return 删除失败的对象List */ public List removeObjects(List objectNames) { List deleteErrorNames = new ArrayList<>(); if (bucketExists()) { for (String objectName : objectNames) { if (!removeObject(objectName)) { deleteErrorNames.add(objectName); } } } return deleteErrorNames; } /** * 获取对象的元数据 * * @param objectName 对象名称 * @return 元数据 */ public SysResult statObject(String objectName) { try { if (bucketExists()) { StatObjectResponse response = minioClient.statObject(StatObjectArgs.builder() .bucket(bucketName) .object(objectName) .build()); // 输出对象的元数据信息 Map metadata = response.userMetadata(); // 返回 return SysResult.data(metadata); } } catch (Exception e) { throw new RuntimeException("获取元数据失败", e); } return null; } /** * 获取文件的临时访问路径 * * @param objectName 对象名称 * @return 临时访问url */ public String getObjectUrl(String objectName) { String url = ""; try { if (bucketExists()) { // 设置预签名URL的有效期(以秒为单位) int expiresInSeconds = 3600; // 一小时 url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder() .method(Method.GET) //只能get请求访问 .bucket(bucketName) .object(objectName) .expiry(expiresInSeconds) // 设置有效时间 .build()); } } catch (Exception e) { throw new RuntimeException("获取失败", e); } return url; } /** * 获取(下载)单个文件 * * @param fileName 文件名 * @return 输出流 */ public ResponseEntity getObject(String fileName){ // 获取输入流 try (InputStream inputStream = minioClient.getObject(GetObjectArgs.builder() .bucket(bucketName) .object(fileName).build())) { byte[] byteArray = IOUtils.toByteArray(inputStream); ByteArrayResource resource = new ByteArrayResource(byteArray); //设置请求头 HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + fileName); //开始下载 return ResponseEntity.ok() .headers(headers) .contentLength(byteArray.length) .contentType(MediaType.APPLICATION_OCTET_STREAM) .body(resource); }catch (Exception e) { throw new RuntimeException("下载失败", e); } } /** * 获取(下载)多个文件,写进zip压缩包返回 * * @param objectNames 资源名数组 * @return 输出流 */ public ResponseEntity getObjectList(String[] objectNames) { // 创建一个输出流,将文件写入压缩包 try (ByteArrayOutputStream aos = new ByteArrayOutputStream(); ZipOutputStream zos = new ZipOutputStream(aos)) { for (String objectName : objectNames) { // 获取对象 InputStream inputStream = minioClient.getObject( GetObjectArgs.builder() .bucket(bucketName) .object(objectName) .build()); // 创建一个新的ZIP条目,并将文件写入压缩包 ZipEntry zipEntry = new ZipEntry(objectName); zos.putNextEntry(zipEntry); byte[] buffer = new byte[8192]; int length; while ((length = inputStream.read(buffer)) > 0) { zos.write(buffer, 0, length); } // 关闭当前的ZIP条目 zos.closeEntry(); inputStream.close(); } // 关闭 ZIP 输出流 zos.close(); // 将压缩包的字节数组作为响应体返回 byte[] zipBytes = aos.toByteArray(); //设置请求头 HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=output.zip"); //开始下载 return ResponseEntity.ok() .headers(headers) .contentLength(zipBytes.length) .contentType(MediaType.APPLICATION_OCTET_STREAM) .body(zipBytes); } catch (Exception e) { throw new RuntimeException("文件下载失败", e); } } }
@RestController @RequestMapping(value = "/file") public class FileController { @Resource private MinioUtils minioUtils; /** * 获取元数据 * @param fileName 文件名 * @return 元数据 */ @GetMapping(value = "/stat") public SysResult stat(String fileName) { return minioUtils.statObject(fileName); } /** * 根据文件名获取临时访问URL * @param fileName 文件名 * @return 临时访问URL */ @GetMapping(value = "/getUrl") public SysResult get(String fileName) { return SysResult.data(minioUtils.getObjectUrl(fileName)); } /** * 下载单个文件 * * @param fileName 文件名 * @return 流 */ @GetMapping(value = "/download") public ResponseEntitydownloadFile(String fileName) { return minioUtils.getObject(fileName); } /** * 下载多个文件到压缩包返回 * @param fileName 要下载的文件名数组 * @return 压缩流 */ @GetMapping(value = "/package") public ResponseEntity list(String[] fileName) { return minioUtils.getObjectList(fileName); } /** * 上传单个文件 * @param file 文件 * @return 是否上传成功 */ @PostMapping(value = "/upload") public SysResult upload(@RequestParam("file") MultipartFile file) { return minioUtils.putObject(file); } /** * 上传多个文件 * * @param file 文件数组 * @return 上传成功 */ @PostMapping(value = "/uploadList") public SysResult uploadList(@RequestParam("file") MultipartFile[] file) { return minioUtils.putObject(file); } /** * 删除单个文件 * * @param fileName 文件名 * @return 删除成功 */ @GetMapping(value = "/remove") public SysResult remove(String fileName) { minioUtils.removeObject(fileName); return SysResult.success("删除成功", fileName); } }
接下来就可以跟接口愉快得玩耍了。