背景
在后端开发中,常常会涉及到各种静态资源的操作,比如上传图片、下载图片、上传下载PDF
那么我们在进行迁移开发和部署或者各种分布式开发时就会面对着这些资源的处理,以防止目录丢失或者其他异常。
Minio就是一个对象存储解决方案,一般都做min-io,顾名思义就是:最小输入输出
MinIO有能力在任何地方部署 - 公有云或私有云,裸金属基础设施,编排环境,以及边缘基础设施。所以可以更快速的解决上面提到的一系列问题。
操作
这里以我们常用的windows开发环境为例,开发环境熟练使用后,部署时也能很顺畅的使用了。
先简单说一下minio里的一个概念
bucket桶: 可以简单理解为文件夹,桶是 MinIO 中用于组织和管理对象的容器。每个桶都有一个唯一的名称,在同一个 MinIO 实例中,桶名称必须是唯一的。用户可以通过桶来对对象进行逻辑分组。
1.下载并启动minio
下载地址为:https://dl.minio.org.cn/server/minio/release/windows-amd64/minio.exe
启动命令:在minio.exe文件目录下打开cmd并执行minio.exe server minio
,将会启动minio,并且打印访问的url和用户名、密码
访问minio的url地址,并输入上面的用户名密码:
2. 添加依赖与配置项
pom.xml
<dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.3.6</version>
</dependency>
application.yml
spring:minio:access-key: minioadmin # 用户名secret-key: minioadmin #密码url: http://127.0.0.1:9000 #访问地址
3. 创建MinioConfig配置类信息
通过以下的配置,我们就可以创建一个适用于我们上面启动的minio的配置项,同时完成了MinioClient的注册
@Data
@Configuration
@ConfigurationProperties(prefix = "spring.minio")
public class MinioConfig {private String accessKey;private String secretKey;private String url;// 或者,主要目的是获取yml中的配置内容//@Value("${spring.minio.url}")//private String minioEndpoint;//@Value("${spring.minio.access-key}")//private String minioAccessKey;//@Value("${spring.minio.secret-key}")//private String minioSecretKey;@Beanpublic MinioClient minioClient(){return MinioClient.builder().endpoint(url).credentials(accessKey,secretKey).build();}
}
4. 常用操作
我们可以进入自动注入的minioClient配置类查看常用操作
我们主要介绍常用的一些操作
4.0 创建单元测试
创建单元测试,并自动注入MinioClient
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class test {@Resourceprivate MinioClient minioClient;// 以桶为test,文件为D:/test/test.jpg为案例private final String bucketName = "test";File file = new File("D:/test/test.jpg");
}
4.1 创建桶
@Test
void createBucket() throws Exception {if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());}
}
可以看到已经成功创建了桶
4.2 删除桶
@Test
void deleteBucket() throws Exception {if (minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());}
}
可以看到桶被删除了
4.3 检测桶是否存在
@Test
void bucketExists() throws Exception {boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());System.out.println(exists);
}
当我们分别检测桶发现桶不存在
重新执行创建桶操作,可以看到桶返回存在
4.4 上传文件
@Test
void fileUpload() throws Exception {FileInputStream inputStream = new FileInputStream(file);minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(file.getName()).stream(inputStream, file.length(), -1).contentType(MediaType.IMAGE_JPEG_VALUE).build());
}
test桶中添加了新的文件
4.5 下载文件
@Test
void fileDownload() throws Exception {try (InputStream stream = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(file.getName()).build())) {// Save the file locally or process the stream as neededFiles.copy(stream, Paths.get("D:/test/downloaded_" + file.getName()));}
}
文件按照路径和命名存储到了新的位置
4.6 检测文件是否存在
@Test
void fileExists() throws Exception {try {StatObjectResponse response = minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(file.getName()).build());System.out.println(response);} catch (MinioException e) {System.out.println(e.getMessage());}
}
如果存在,则文件信息被返回
如果不存在,则产生异常,我们可以捕获异常进行处理
4.7 删除文件
@Test
void fileDelete() throws Exception {minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(file.getName()).build());
}
文件被删除了
5. 自定义我们自己的Minio工具类
@Component
public class MinioUtils {@Autowiredprivate MinioClient minioClient;@Autowiredprivate MinioConfig configuration;/*** @param name 名字* @Description description: 判断bucket是否存在,不存在则创建*/public boolean existBucket(String name) {boolean exists;try {exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(name).build());if (!exists) {minioClient.makeBucket(MakeBucketArgs.builder().bucket(name).build());exists = true;}} catch (Exception e) {e.printStackTrace();exists = false;}return exists;}/*** @param bucketName 存储bucket名称* @Description 创建存储bucket*/public Boolean makeBucket(String bucketName) {try {minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());} catch (Exception e) {e.printStackTrace();return false;}return true;}/*** @param bucketName 存储bucket名称* @Description 删除存储bucket*/public Boolean removeBucket(String bucketName) {try {minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());} catch (Exception e) {e.printStackTrace();return false;}return true;}/*** @param fileName 文件名称* @param time 时间* @Description 获取上传临时签名*/@SneakyThrowspublic Map getPolicy(String fileName, ZonedDateTime time,String bucketName) {PostPolicy postPolicy = new PostPolicy(bucketName, time);postPolicy.addEqualsCondition("key", fileName);try {Map<String, String> map = minioClient.getPresignedPostFormData(postPolicy);HashMap<String, String> map1 = new HashMap<>();map.forEach((k, v) -> {map1.put(k.replaceAll("-", ""), v);});map1.put("host", configuration.getUrl() + "/" + bucketName);return map1;} catch (Exception e) {e.printStackTrace();}return null;}/*** @param objectName 对象名称* @param method 方法* @param time 时间* @param timeUnit 时间单位* @Description 获取上传文件的url*/public String getPolicyUrl(String objectName, Method method,String bucketName, int time, TimeUnit timeUnit) {try {return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(method).bucket(bucketName).object(objectName).expiry(time, timeUnit).build());} catch (Exception e) {e.printStackTrace();}return null;}/*** @param file 文件* @param fileName 文件名称* @Description 上传文件*/public void upload(MultipartFile file, String fileName,String bucketName) {// is deviceImg bucket exist ?boolean b = existBucket(bucketName);if (!b) {makeBucket(bucketName);}// 使用putObject上传一个文件到存储桶中。try {InputStream inputStream = file.getInputStream();minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(fileName).stream(inputStream, file.getSize(), -1).contentType(file.getContentType()).build());} catch (Exception e) {e.printStackTrace();}}/*** @param objectName 对象名称* @param time 时间* @param timeUnit 时间单位* @Description 根据filename获取文件访问地址*/public String getUrl(String objectName,String bucketName, int time, TimeUnit timeUnit) {String url = null;try {url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(bucketName).object(objectName).expiry(time, timeUnit).build());} catch (Exception e) {e.printStackTrace();}return url;}/*** @Description description: 下载文件*/public ResponseEntity<byte[]> download(String fileName,String bucketName) {ResponseEntity<byte[]> responseEntity = null;InputStream in = null;ByteArrayOutputStream out = null;try {in = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());out = new ByteArrayOutputStream();IOUtils.copy(in, out);//封装返回值byte[] bytes = out.toByteArray();HttpHeaders headers = new HttpHeaders();try {headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));} catch (UnsupportedEncodingException e) {e.printStackTrace();}headers.setContentLength(bytes.length);headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);headers.setAccessControlExposeHeaders(Arrays.asList("*"));responseEntity = new ResponseEntity<byte[]>(bytes, headers, ResultCode.SUCCESS.code);} catch (Exception e) {e.printStackTrace();} finally {try {if (in != null) {try {in.close();} catch (IOException e) {e.printStackTrace();}}if (out != null) {out.close();}} catch (IOException e) {e.printStackTrace();}}return responseEntity;}/*** @param objectFile 对象文件*/public String getFileUrl(String objectFile,String bucketName) {try {return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(bucketName).object(objectFile).build());} catch (Exception e) {e.printStackTrace();}return null;}
}
public String getFileUrl(String objectFile,String bucketName) {
try {
return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(bucketName).object(objectFile).build());} catch (Exception e) {e.printStackTrace();}return null;
}
}