是在 Apache License v2.0 下发布的对象存储服务器,学习成本低,安装运维简单,主流语⾔的客户端整合都有,号称最强的对象存储⽂件服务器,且可以和容器化技术docker/k8s等结合,优点:功能强、可以根据业务做二次的定制,新一代分布式文件存储系统。
下载
wget http://dl.minio.org.cn/server/minio/release/linux-amd64/minio
赋权
chmod +x minio
启动 (指定的url是文件存储地址)
./minio server /usr/local/wulei/minio_store 2>&1 &
访问
http:192.168.200.100:9000
界面非常友好,登陆成功后右下角可以 create bucket 也可以直接上传文件。create bucket 有点类似分组的概念,建议每个项目都各自一个分组。
还可以在分组下面继续创建文件夹,右边还提供了各种功能(视频、文档 等资源不支持预览)。
1. 加入配置和依赖
<!--文件存储--> <dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>8.0.3</version> </dependency> minio: # 地址 endpoint: http://192.168.200.100:9000 # 账号 accessKey: minioadmin # 密码 secretKey: minioadmin
2. 配置客户端
import io.minio.MinioClient; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Data @Configuration @ConfigurationProperties(prefix = "minio") /** * 文件服务器配置类 */ public class MinioConfig { private String endpoint; private String accessKey; private String secretKey; @Bean public MinioClient minioClient(){ return MinioClient.builder() .endpoint(endpoint) .credentials(accessKey, secretKey) .build(); } }
3. 工具类
import io.minio.*; import io.minio.http.Method; import io.minio.messages.Bucket; import io.minio.messages.DeleteError; import io.minio.messages.DeleteObject; import io.minio.messages.Item; import org.springframework.stereotype.Component; import org.springframework.util.FastByteArrayOutputStream; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * 文件服务器工具类 */ @Component public class MinioUtil { private String bucketName = "wulei-service"; @Resource private MinioClient minioClient; /** * 查看存储bucket是否存在 * @return boolean */ public Boolean bucketExists(String bucketName) { Boolean found; try { found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); } catch (Exception e) { //e.printStackTrace(); return false; } return found; } /** * 创建存储bucket * @return Boolean */ public Boolean makeBucket(String bucketName) { try { minioClient.makeBucket(MakeBucketArgs.builder() .bucket(bucketName) .build()); } catch (Exception e) { e.printStackTrace(); return false; } return true; } /** * 删除存储bucket * @return Boolean */ public Boolean removeBucket(String bucketName) { try { minioClient.removeBucket(RemoveBucketArgs.builder() .bucket(bucketName) .build()); } catch (Exception e) { e.printStackTrace(); return false; } return true; } /** * 获取全部bucket */ public List<Bucket> getAllBuckets() { try { return minioClient.listBuckets(); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 文件上传 * @param file 文件 * @return Boolean */ public Boolean upload(MultipartFile file) { // 修饰过的文件名 非源文件名 String fileName = "2021-07/21/"; fileName = fileName+file.getOriginalFilename(); try { PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(bucketName).object(fileName) .stream(file.getInputStream(),file.getSize(),-1).contentType(file.getContentType()).build(); //文件名称相同会覆盖 minioClient.putObject(objectArgs); } catch (Exception e) { e.printStackTrace(); return false; } return true; } /** * 预览图片 * @param fileName * @return */ public String preview(String fileName){ // 查看文件地址 GetPresignedObjectUrlArgs build = new GetPresignedObjectUrlArgs().builder().bucket(bucketName).object(fileName).method(Method.GET).build(); try { String url = minioClient.getPresignedObjectUrl(build); return url; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 文件下载 * @param fileName 文件名称 * @param res response * @return Boolean */ public void download(String fileName, HttpServletResponse res) { 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(); res.setCharacterEncoding("utf-8"); //设置强制下载不打开 //res.setContentType("application/force-download"); res.addHeader("Content-Disposition", "attachment;fileName=" + fileName); try (ServletOutputStream stream = res.getOutputStream()){ stream.write(bytes); stream.flush(); } } } catch (Exception e) { e.printStackTrace(); } } /** * 查看文件对象 * @return 存储bucket内文件对象信息 */ public List<Item> listObjects() { Iterable<Result<Item>> results = minioClient.listObjects( ListObjectsArgs.builder().bucket(bucketName).build()); List<Item> items = new ArrayList<>(); try { for (Result<Item> result : results) { items.add(result.get()); } } catch (Exception e) { e.printStackTrace(); return null; } return items; } /** * 删除 * @param fileName * @return * @throws Exception */ public boolean remove(String fileName){ try { minioClient.removeObject( RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build()); }catch (Exception e){ return false; } return true; } /** * 批量删除文件对象(没测试) * @param objects 对象名称集合 */ public Iterable<Result<DeleteError>> removeObjects(List<String> objects) { List<DeleteObject> dos = objects.stream().map(e -> new DeleteObject(e)).collect(Collectors.toList()); Iterable<Result<DeleteError>> results = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(dos).build()); return results; } }
4. 测试
import com.wulei.common.util.JsonData; import com.wulei.user.util.MinioUtil; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; @Api(tags = "文件管理模块") @RestController @RequestMapping("/api/file/v1") @Slf4j public class MinioController { @Autowired private MinioUtil minioUtil; @ApiOperation(value = "查看存储bucket是否存在") @GetMapping("/bucketExists") public JsonData bucketExists(String bucketName) { return JsonData.buildSuccess(minioUtil.bucketExists(bucketName)); } @ApiOperation(value = "创建存储bucket") @GetMapping("/makeBucket") public JsonData makeBucket(String bucketName) { minioUtil.makeBucket(bucketName); return JsonData.buildSuccess(); } @ApiOperation(value = "删除存储bucket") @GetMapping("/removeBucket") public JsonData removeBucket(String bucketName) { minioUtil.removeBucket(bucketName); return JsonData.buildSuccess(); } @ApiOperation(value = "获取全部bucket") @GetMapping("/getAllBuckets") public JsonData getAllBuckets() { return JsonData.buildSuccess(minioUtil.getAllBuckets()); } @ApiOperation(value = "文件上传") @GetMapping("/upload") public JsonData upload(MultipartFile file) { minioUtil.upload(file); return JsonData.buildSuccess(); } @ApiOperation(value = "图片预览") @GetMapping("/preview") public JsonData preview(String fileName) { return JsonData.buildSuccess(minioUtil.preview(fileName)); } @ApiOperation(value = "文件下载") @GetMapping("/download") public JsonData download(String fileName, HttpServletResponse res) { minioUtil.download(fileName,res); return JsonData.buildSuccess(); } @ApiOperation(value = "文件删除") @GetMapping("/remove") public JsonData remove(String fileName) { minioUtil.remove(fileName); return JsonData.buildSuccess(); } }
。