赞
踩
文件服务算是一个基础服务了,比较专业的组件有FastDFS,也可以采用基本的保存在本地的文件上传方式,但是其安全性不高。对于中小公司来说,可以直接购买云存储服务,当然也可以自己搭建,下面就聊聊springboot集成文件服务组件MinIO,看看这个组件带来了那些惊喜。
MinIO是一个基于Apache License v2.0开源协议的对象存储服务,非常合适存储大容量非结构化的数据,如图片、视频、日志文件等,一个对象文件的大小可以是几KB到5T不等。MinIO是一个轻量级的服务,类似redis、mysql,可以很方便的和其他应用结合,并且自带非常方便的可视化界面,让人心生喜欢。
MinIO的特点请查看官网,https://min.io/。
1、MinIO安装
进入官网下载对应的版本,可以选择linux、windows、docker等,本次选择windows环境
windows环境minio安装启动
查看minio版本
minio.exe -version
首先进入minio.exe所在目录,在cmd命令行中输入如下命令即完成安装启动,其中server后面的地址是文件上传的存储目录。
minio.exe server D:\minio
api端口是9000,所以下面的yml文件中端口需要配置为9000,accessKey和secretKey需要配置为启动窗口上对应的值。
启动成功后,访问默认地址http://127.0.0.1:9000/minio进入到minio控制台,输入账号密码登录即可。
可以在控制台创建bucketName,也可以在代码中创建。注意在启动服务后使用minio。
linux环境minio安装启动
创建data目录
mkdir/usr/local/minio/data
添加可执行权限
chmod +x minio
设置登录的access_key
export MINIO_ACCESS_KEY=minioadmin
设置登录的secret key
export MINIO_SECRET_KEY=minioadmin
启动 minio
./minio server /usr/local/minio/data
或指定端口号
./minio server /usr/local/minio/data --address=0.0.0.0:9001
其他同windows环境。
2、springboot集成MinIO
创建springboot工程,引入web依赖、minio依赖
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.2.1</version>
</dependency>
配置application.yml文件
spring:
servlet:
multipart:
max-file-size: 200MB
max-request-size: 200MB
minio:
endpoint: http://localhost:9000
accessKey: minioadmin
secretKey: minioadmin
nginxUrl: http://localhost:9000
创建Properties类接收上面四个属性
@ConfigurationProperties(prefix = "minio") public class MinioProperties { /** * 连接地址 */ private String endpoint; /** * 用户名 */ private String accessKey; /** * 密码 */ private String secretKey; /** * 域名 */ private String nginxUrl; public String getEndpoint() { return endpoint; } public void setEndpoint(String endpoint) { this.endpoint = endpoint; } public String getAccessKey() { return accessKey; } public void setAccessKey(String accessKey) { this.accessKey = accessKey; } public String getSecretKey() { return secretKey; } public void setSecretKey(String secretKey) { this.secretKey = secretKey; } public String getNginxUrl() { return nginxUrl; } public void setNginxUrl(String nginxUrl) { this.nginxUrl = nginxUrl; } }
创建配置类,注入MinIOClient
@Configuration @EnableConfigurationProperties(MinioProperties.class) public class MinioConfig { @Autowired private MinioProperties minioProperties; /** * 获取MinioClient */ @Bean public MinioClient minioClient() { return MinioClient .builder() .endpoint(minioProperties.getEndpoint()) .credentials(minioProperties.getAccessKey(), minioProperties.getSecretKey()) .build(); } }
创建Client工具类,方便操作文件
@Component public class MinioUtil { @Autowired private MinioProperties minioProperties; @Autowired private MinioClient minioClient; /** * 创建bucket */ public void createBucket(String bucketName) throws Exception { if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) { minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); } } /** * 删除bucket */ public void removeBucket(String bucketName) throws Exception { minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); } /** * 获取全部bucket */ public List<Bucket> getBuckets() throws Exception { return minioClient.listBuckets(); } /** * 根据bucketName获取bucket * @param bucketName bucket名称 */ public Optional<Bucket> getBucket(String bucketName) throws IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, InvalidResponseException, InternalException, ErrorResponseException, ServerException, XmlParserException, ServerException { return minioClient.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst(); } /** * 上传文件 */ public MinioObject uploadFile(MultipartFile file, String bucketName) throws Exception { //判断文件是否为空,为空则返回 if (null == file || 0 == file.getSize()) { return null; } //判断存储桶是否存在,不存在则创建 createBucket(bucketName); //文件名 String originalFilename = file.getOriginalFilename(); //新的文件名 = 存储桶文件名_时间戳.后缀名 assert originalFilename != null; String fileName = bucketName + "_" + new SimpleDateFormat("yyyy-MM-dd").format(new Date()) + "_" + new Random().nextInt(1000) + originalFilename.substring(originalFilename.lastIndexOf(".")); //开始上传 minioClient.putObject( PutObjectArgs .builder() .bucket(bucketName) .object(fileName) .stream(file.getInputStream(), file.getSize(), -1) .contentType(file.getContentType()) .build()); String url = minioProperties.getEndpoint() + "/" + bucketName + "/" + fileName; String urlHost = minioProperties.getNginxUrl() + "/" + bucketName + "/" + fileName; return new MinioObject(url, urlHost); } /** * 下载文件 */ public void downloadFile(String filename, String bucketName, HttpServletResponse httpServletResponse){ GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucketName).object(filename).build(); try (GetObjectResponse response = minioClient.getObject(objectArgs)){ byte[] buf = new byte[1024]; int len; try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()){ while ((len=response.read(buf))!=-1){ os.write(buf,0,len); } os.flush(); byte[] bytes = os.toByteArray(); httpServletResponse.setCharacterEncoding("utf-8"); // 设置强制下载不打开 httpServletResponse.setContentType("application/force-download"); httpServletResponse.addHeader("Content-Disposition", "attachment;fileName=" + filename); try ( ServletOutputStream stream = httpServletResponse.getOutputStream()){ stream.write(bytes); stream.flush(); } } } catch (Exception e) { e.printStackTrace(); } } /** * 下载文件 */ public void downloadObject(String fileName, String bucketName, HttpServletResponse httpServletResponse) { try { GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucketName).object(fileName).build(); InputStream file = minioClient.getObject(objectArgs); String filename = new String(fileName.getBytes("ISO8859-1"), "utf-8"); httpServletResponse.setHeader("Content-Disposition", "attachment;filename=" + filename); ServletOutputStream servletOutputStream = httpServletResponse.getOutputStream(); int len; byte[] buffer = new byte[1024]; while ((len = file.read(buffer)) > 0) { servletOutputStream.write(buffer, 0, len); } servletOutputStream.flush(); file.close(); servletOutputStream.close(); } catch (ErrorResponseException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } /** * 上传⽂件 * * @param bucketName bucket名称 * @param objectName ⽂件名称 * @param stream ⽂件流 * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject */ public void putObject(String bucketName, String objectName, InputStream stream) throws Exception { minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(stream, stream.available(), -1).contentType(objectName.substring(objectName.lastIndexOf("."))).build()); } /** * 上传⽂件 * * @param bucketName bucket名称 * @param objectName ⽂件名称 * @param stream ⽂件流 * @param size ⼤⼩ * @param contextType 类型 * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject */ public void putObject(String bucketName, String objectName, InputStream stream, long size, String contextType) throws Exception { minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(stream, size, -1).contentType(contextType).build()); } /** * 删除⽂件 * * @param bucketName bucket名称 * @param objectName ⽂件名称 * @throws Exception https://docs.minio.io/cn/java-client-apireference.html#removeObject */ public void removeObject(String bucketName, String objectName) throws Exception { minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); } /** * 获取⽂件url * * @param bucketName bucket名称 * @param objectName ⽂件名称 * @param expires 过期时间 <=7 * @return url */ public String getObjectURL(String bucketName, String objectName, Integer expires) throws Exception { return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(bucketName).object(objectName).expiry(expires).build()); } /** * 获取⽂件输入流 * * @param bucketName bucket名称 * @param objectName ⽂件名称 * @return ⼆进制流 */ public InputStream getObject(String bucketName, String objectName) throws Exception { return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build()); } /** * 获取⽂件信息 * * @param bucketName bucket名称 * @param objectName ⽂件名称 * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#statObject */ public StatObjectResponse getObjectInfo(String bucketName, String objectName) throws Exception { return minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build()); } /** * 查看文件对象列表 * * @param bucketName 存储bucket名称 * @return 存储bucket内文件对象信息 */ public List<MinioObject> listObjects(String bucketName) { Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).build()); List<MinioObject> objectItems = new ArrayList<>(); try { for (Result<Item> result : results) { Item item = result.get(); MinioObject minioObject = new MinioObject(); minioObject.setMinIoUrl(item.objectName());; minioObject.setSize(item.size()); objectItems.add(minioObject); } } catch (Exception e) { e.printStackTrace(); return null; } return objectItems; } }
访问接口
@PostMapping("/upload")
public String fileUpload(MultipartFile file) throws Exception {
MinioObject minioObject = minioUtil.uploadFile(file, "public");
System.out.println("bucket01.getMinIoUrl() = " + minioObject.getMinIoUrl());
System.out.println("bucket01.getNginxUrl() = " + minioObject.getNginxUrl());
return minioObject.getMinIoUrl();
}
@GetMapping("download")
public Object download(HttpServletResponse response) throws Exception {
minioUtil.downloadObject("","image",response);
return "success";
}
文件上传完成之后如图
注意:可以通过可视化界面设置存储桶的权限级别,是否开发,访问的url有效期等,具体看组件提供的方法,如
getObjectURL()获取桶内文件的url地址,如果设置了只写权限,用户直接访问地址是查看不了的;
getObgect()可以获取桶内对应文件的流对象,进行文件流传输;
presignedGetObject()返回的是进行加密算法的地址,通过它可以直接访问文件;
getPresignedObjectUrl(Method.GET,"asiatrip","minio.png",10,null)有效期10秒,可以通过get方式访问;
makeBucket 创建桶
listBuckets 列出当前所有桶
bucketExists 判断桶是否存在
removeBucket 移除桶
listObjects 获取桶内的内容
listIncompleteUploads 获取上传中断的内容,支持断点续传
getObject 下载文件
putObject 上传文件
文件存储可以使用之后也可以通过配置nginx的方式对静态资源进行代理,前端直接通过url进行访问。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。