0


SpringBoot整合MinIO 「看这一篇就够了」

文章目录

MinIo安装及启动

1. 通过 docker 安装 MinIo

# 搜索是否有 minio 镜像docker search minio
# 有则拉取镜像docker pull minio/minio

2. 启动 及配置 MinIo

# 先创建minio 文件存放的位置mkdir -p /opt/docker/minio/data

# 启动并指定端口docker run \
  -p 5000:9000 \
  -p 5001:5001 \
  --name minio \
  -v /opt/docker/minio/data:/data \
  -e "MINIO_ROOT_USER=Jonny"\
  -e "MINIO_ROOT_PASSWORD=minioadmin"\
  -d minio/minio server /data --console-address ":5001"# 设置为和 docker 绑定启动,docker 启动则 minio 就启动docker update --restart=always

这里解释一下 docker 里面的几个参数

-p: 指定端口映射,格式为:主机(宿主)端口:容器端口

-e “MINIO_ROOT_USER=Jonny”: 设置环境变量;

-d: 后台运行容器,并返回容器ID;

-v: 绑定一个卷



3.其他OPTIONS解释

docker run [OPTIONS] IMAGE [COMMAND] [ARG…]

OPTIONS参数说明:
-a stdin: 指定标准输入输出内容类型,可选 STDIN/STDOUT/STDERR 三项;
-d: 后台运行容器,并返回容器ID;
-i: 以交互模式运行容器,通常与 -t 同时使用;
-P: 随机端口映射,容器内部端口随机映射到主机的端口
-p: 指定端口映射,格式为:主机(宿主)端口:容器端口
-t: 为容器重新分配一个伪输入终端,通常与 -i 同时使用;
name=“nginx-lb”: 为容器指定一个名称;
dns 8.8.8.8: 指定容器使用的DNS服务器,默认和宿主一致;
dns-search example.com: 指定容器DNS搜索域名,默认和宿主一致;
-h “mars”: 指定容器的hostname;
-e username=“ritchie”: 设置环境变量;
env-file=[]: 从指定文件读入环境变量;
cpuset=“0-2” or cpuset=“0,1,2”: 绑定容器到指定CPU运行;
-m :设置容器使用内存最大值;
net=“bridge”: 指定容器的网络连接类型,支持 bridge/host/none/container: 四种类型;
link=[]: 添加链接到另一个容器;
expose=[]: 开放一个端口或一组端口;
volume , -v: 绑定一个卷
例如,使用docker镜像nginx:latest以后台模式启动一个容器,并将容器命名为mynginx:
docker run name mynginx -d nginx:latest
使用镜像nginx:latest以后台模式启动一个容器,并将容器的80端口映射到主机随机端口:
docker run -P -d nginx:latest
使用镜像 nginx:latest,以后台模式启动一个容器,将容器的 80 端口映射到主机的 80 端口,主机的目录 /data 映射到容器的 /data:
docker run -p 80:80 -v /data:/data -d nginx:latest
绑定容器的 8080 端口,并将其映射到本地主机 127.0.0.1 的 80 端口上:
docker run -p 127.0.0.1:80:8080/tcp ubuntu bash
使用镜像nginx:latest以交互模式启动一个容器,在容器内执行/bin/bash命令:
docker run -it nginx:latest /bin/bash

SpringBoot 整合 MinIO

1.导入依赖

<!-- https://mvnrepository.com/artifact/io.minio/minio --><dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.4.0</version></dependency>

2.application.yml 配置信息

minio:endpoint: http://127.0.0.1:9000#Minio服务所在地址bucketName: tulaoda #存储桶名称accessKey: Jonny #访问的keysecretKey: minioadmin #访问的秘钥

3.MinioConfig.class配置类

@Data@Configuration@ConfigurationProperties(prefix ="minio")publicclassMinioConfig{privateString endpoint;privateString accessKey;privateString secretKey;privateString bucketName;@BeanpublicMinioClientminioClient(){returnMinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build();}}

4.minio工具类

@Component@Slf4jpublicclassMinioUtil{@AutowiredprivateMinioConfig prop;@ResourceprivateMinioClient minioClient;@AutowiredprivateCodeService codeService;/**
     * 查看存储bucket是否存在
     * @return boolean
     */publicBooleanbucketExists(String bucketName){Boolean found;try{
            found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());}catch(Exception e){
            e.printStackTrace();returnfalse;}return found;}/**
     * 创建存储bucket
     * @return Boolean
     */publicBooleanmakeBucket(String bucketName){try{
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());}catch(Exception e){
            e.printStackTrace();returnfalse;}returntrue;}/**
     * 删除存储bucket
     * @return Boolean
     */publicBooleanremoveBucket(String bucketName){try{
            minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());}catch(Exception e){
            e.printStackTrace();returnfalse;}returntrue;}/**
     * 获取全部bucket
     */publicList<Bucket>getAllBuckets(){try{List<Bucket> buckets = minioClient.listBuckets();return buckets;}catch(Exception e){
            e.printStackTrace();}returnnull;}/**
     * 文件上传
     *
     * @param file 文件
     * @return Boolean
     */publicStringupload(MultipartFile file){String originalFilename = file.getOriginalFilename();if(StringUtils.isBlank(originalFilename)){thrownewRuntimeException();}String fileName =UuidUtils.generateUuid()+ originalFilename.substring(originalFilename.lastIndexOf("."));String objectName =CommUtils.getNowDateLongStr("yyyy-MM/dd")+"/"+ fileName;try{PutObjectArgs objectArgs =PutObjectArgs.builder().bucket(prop.getBucketName()).object(objectName).stream(file.getInputStream(), file.getSize(),-1).contentType(file.getContentType()).build();//文件名称相同会覆盖
            minioClient.putObject(objectArgs);}catch(Exception e){
            e.printStackTrace();returnnull;}return objectName;}/**
     * 预览图片
     * @param fileName
     * @return
     */publicStringpreview(String fileName){// 查看文件地址GetPresignedObjectUrlArgs build =newGetPresignedObjectUrlArgs().builder().bucket(prop.getBucketName()).object(fileName).method(Method.GET).build();try{String url = minioClient.getPresignedObjectUrl(build);return url;}catch(Exception e){
            e.printStackTrace();}returnnull;}/**
     * 文件下载
     * @param fileName 文件名称
     * @param res response
     * @return Boolean
     */publicvoiddownload(String fileName,HttpServletResponse res){GetObjectArgs objectArgs =GetObjectArgs.builder().bucket(prop.getBucketName()).object(fileName).build();try(GetObjectResponse response = minioClient.getObject(objectArgs)){byte[] buf =newbyte[1024];int len;try(FastByteArrayOutputStream os =newFastByteArrayOutputStream()){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内文件对象信息
     */publicList<Item>listObjects(){Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(prop.getBucketName()).build());List<Item> items =newArrayList<>();try{for(Result<Item> result : results){
                items.add(result.get());}}catch(Exception e){
            e.printStackTrace();returnnull;}return items;}/**
     * 删除
     * @param fileName
     * @return
     * @throws Exception
     */publicbooleanremove(String fileName){try{
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(prop.getBucketName()).object(fileName).build());}catch(Exception e){returnfalse;}returntrue;}}

5.文件处理接口

@Api(tags ="文件相关接口")@Slf4j@RestController@RequestMapping(value ="product/file")publicclassFileController{@AutowiredprivateMinioUtil minioUtil;@AutowiredprivateMinioConfig prop;@ApiOperation(value ="查看存储bucket是否存在")@GetMapping("/bucketExists")publicRbucketExists(@RequestParam("bucketName")String bucketName){returnR.ok().put("bucketName",minioUtil.bucketExists(bucketName));}@ApiOperation(value ="创建存储bucket")@GetMapping("/makeBucket")publicRmakeBucket(String bucketName){returnR.ok().put("bucketName",minioUtil.makeBucket(bucketName));}@ApiOperation(value ="删除存储bucket")@GetMapping("/removeBucket")publicRremoveBucket(String bucketName){returnR.ok().put("bucketName",minioUtil.removeBucket(bucketName));}@ApiOperation(value ="获取全部bucket")@GetMapping("/getAllBuckets")publicRgetAllBuckets(){List<Bucket> allBuckets = minioUtil.getAllBuckets();returnR.ok().put("allBuckets",allBuckets);}@ApiOperation(value ="文件上传返回url")@PostMapping("/upload")publicRupload(@RequestParam("file")MultipartFile file){String objectName = minioUtil.upload(file);if(null!= objectName){returnR.ok().put("url",(prop.getEndpoint()+"/"+ prop.getBucketName()+"/"+ objectName));}returnR.error();}@ApiOperation(value ="图片/视频预览")@GetMapping("/preview")publicRpreview(@RequestParam("fileName")String fileName){returnR.ok().put("filleName",minioUtil.preview(fileName));}@ApiOperation(value ="文件下载")@GetMapping("/download")publicRdownload(@RequestParam("fileName")String fileName,HttpServletResponse res){
        minioUtil.download(fileName,res);returnR.ok();}@ApiOperation(value ="删除文件", notes ="根据url地址删除文件")@PostMapping("/delete")publicRremove(String url){String objName = url.substring(url.lastIndexOf(prop.getBucketName()+"/")+ prop.getBucketName().length()+1);
        minioUtil.remove(objName);returnR.ok().put("objName",objName);}}

验证结果

上传

**

注意这里的 file 要选择一下类型用 File 而不是用 text

**

CSDN 机制问题图片加载不出来,可以查看本人博客:兔老大-SpringBoot整合MinIO 「看这一篇就够了」

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RS6AO3oW-1652372464067)(https://cdn.jsdelivr.net/gh/Jonny-Chi/picgo_imgs/BLog/202205130001555.png)]

删除

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-13e1zEbq-1652372464068)(https://cdn.jsdelivr.net/gh/Jonny-Chi/picgo_imgs/BLog/202205130002859.png)]

预览

image-20220513001539704


这一块的接口慢慢试,主要用的几个接口在上面


报错解决

image-20220512220445285

错误原因:
minio版本太新需要更换成8.2.2的版本

<!-- https://mvnrepository.com/artifact/io.minio/minio --><dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.2.2</version></dependency>
error occurred
ErrorResponse(code = RequestTimeTooSkewed, message = The difference between the request time and the server's time is too large., bucketName = null, objectName = null, resource = /gulimall-product, requestId = null, hostId = bffd91de-450a-47ac-b4b2-3d3b45ddd054)
request={method=GET, url=http://192.168.1.17:5000/gulimall-product?location=, headers=Host: 192.168.1.17:5000
Accept-Encoding: identity
User-Agent: MinIO (Mac OS X; x86_64) minio-java/8.2.2
Content-MD5: 1B2M2Y8AsgTpgAmY7PhCfg==
x-amz-content-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
x-amz-date: 20220512T140028Z
Authorization: AWS4-HMAC-SHA256 Credential=*REDACTED*/20220512/us-east-1/s3/aws4_request, SignedHeaders=content-md5;host;x-amz-content-sha256;x-amz-date, Signature=*REDACTED*
}
response={code=403, headers=Accept-Ranges: bytes
Content-Length: 299
Content-Type: application/xml
Server: MinIO
Vary: Origin
Date: Thu, 12 May 2022 13:32:53 GMT
}

主要是因为 :

系统时区与硬件时区不一致导致的

解决步骤如下

  1. 安装ntp ntpdateyum -y install ntp ntpdate
  2. 与时间服务器同步时间ntpdate 0.asia.pool.ntp.org
  3. 将系统时间写入硬件时间hwclock --systohc

image-20220512222616705

报错是因为没有给buckts配置权限策略导致。

解决步骤如下:

方法一 :客户端操作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ceQpFmSN-1652372464070)(https://cdn.jsdelivr.net/gh/Jonny-Chi/picgo_imgs/BLog/202205122234336.png)]

方法二: 在创建 Bucket 的时候直接设置权限策略[后端修改]

将 MinIOUtil 工具类的创建 Bucket 的方法优化一下

/**
    * 创建存储bucket
    * @return Boolean
    */publicBooleanmakeBucket(String bucketName){try{if(!bucketExists(bucketName)){
               minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());String policyJson ="{\n"+"\t\"Version\": \""+newSimpleDateFormat("yyyy-mm-dd").format(System.currentTimeMillis())+"\",\n"+"\t\"Statement\": [{\n"+"\t\t\"Effect\": \"Allow\",\n"+"\t\t\"Principal\": {\n"+"\t\t\t\"AWS\": [\"*\"]\n"+"\t\t},\n"+"\t\t\"Action\": [\"s3:GetBucketLocation\", \"s3:ListBucket\", \"s3:ListBucketMultipartUploads\"],\n"+"\t\t\"Resource\": [\"arn:aws:s3:::"+ bucketName +"\"]\n"+"\t}, {\n"+"\t\t\"Effect\": \"Allow\",\n"+"\t\t\"Principal\": {\n"+"\t\t\t\"AWS\": [\"*\"]\n"+"\t\t},\n"+"\t\t\"Action\": [\"s3:AbortMultipartUpload\", \"s3:DeleteObject\", \"s3:GetObject\", \"s3:ListMultipartUploadParts\", \"s3:PutObject\"],\n"+"\t\t\"Resource\": [\"arn:aws:s3:::"+ bucketName +"/*\"]\n"+"\t}]\n"+"}\n";
               minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config(policyJson).build());
               log.info("buckets:【{}】,创建[readwrite]策略成功!", bucketName);}else{
               log.info("minio bucket->>>【{}】already exists", bucketName);}}catch(Exception e){
           e.printStackTrace();returnfalse;}returntrue;}

image-20220512233535741

这一块报错是因为文件名写错了🤣,注意在将文件上传到服务器的过程中修改了文件名:

文件名的格式为:yyyy-mm/dd/
  • uuid
    
  • 文件后缀
    

本文转载自: https://blog.csdn.net/Darling_qi/article/details/124743303
版权归原作者 兔老大-RabbitBoss 所有, 如有侵权,请联系我们删除。

“SpringBoot整合MinIO 「看这一篇就够了」”的评论:

还没有评论