MinIO祥讲+Docker操作+SpringBoot部署使用

一、介绍

MinIO是一个开源的对象存储服务器,它与Amazon S3兼容,并且专注于高性能和可扩展性。在本教程中,我们将学习如何使用MinIO搭建私有对象存储服务,以便在本地或私有云环境中存储和管理数据。

1.1 安装MinIO

首先,我们需要安装MinIO服务器。MinIO提供了预编译的二进制文件,适用于各种操作系统。您可以从官方网站下载适用于您的操作系统的二进制文件。

  1. 访问MinIO官方网站:https://min.io/
  2. 导航至"Downloads"页面。
  3. 根据您的操作系统下载MinIO二进制文件。
1.2 启动MinIO服务器

在安装完MinIO二进制文件后,我们需要启动MinIO服务器。您可以通过以下命令在默认端口(9000)上启动MinIO:

$ ./minio server /path/to/data/directory

其中,/path/to/data/directory是您希望MinIO用于存储数据的目录路径。执行上述命令后,MinIO服务器将在本地启动。

1.3 访问MinIO管理界面

启动MinIO服务器后,您可以通过浏览器访问MinIO的管理界面。默认情况下,管理界面位于http://localhost:9000

  1. 打开您喜欢的浏览器。
  2. 输入http://localhost:9000并按下Enter键。
  3. 您将被重定向到MinIO的登录页面。
1.4 登录MinIO管理界面

在MinIO的登录页面,您可以使用默认的访客访问密钥来登录:

  • Access Key: minio
  • Secret Key: minio123

登录后,您将可以看到MinIO的仪表板,其中包含有关服务器和存储桶的信息。

1.5 创建存储桶

在MinIO中,您需要创建一个存储桶来存储对象(文件)。存储桶类似于文件系统中的文件夹,您可以使用它来组织和管理数据。

  1. 在MinIO的仪表板上,点击"Create Bucket"按钮。
  2. 输入您想要的存储桶名称,并选择数据保留区域(默认为“us-east-1”)。
  3. 点击"Create"按钮来创建存储桶。
1.6 上传和下载对象

现在,您可以开始上传和下载对象到MinIO服务器中的存储桶中。

  1. 在MinIO的仪表板上,找到您刚刚创建的存储桶。
  2. 点击存储桶名称,进入存储桶的详细页面。
  3. 在详细页面中,您将看到"Upload File"按钮。点击它以上传本地文件到存储桶中。
  4. 同样,在存储桶的详细页面中,您可以点击文件名称来下载对象到本地。
1.7 Docker命令操作
docker run -d -p 9000:9000 -p 9001:9001 --name=minio --restart=always --privileged=true -e "MINIO_ROOT_USER='账号'" -e "MINIO_ROOT_PASSWORD='密码'" -v /usr/local/dev/dockerdata/minio/data:/upload -v /usr/local/dev/dockerdata/minio/config:/root/.minio  minio/minio server /upload --console-address ":9001" --address ":9000"

命令详解:

docker run: 运行一个新的容器。

-d: 在后台运行容器。

-p 9000:9000 -p 9001:9001: 将容器的端口9000和9001映射到主机的9000和9001端口,这样可以通过主机的这两个端口来访问MinIO服务和MinIO Web界面。

--name=minio: 为容器指定一个名称为"minio",方便后续操作时使用。

--restart=always: 设置容器在启动后总是自动重启,以确保MinIO服务始终可用。

--privileged=true: 启用特权模式,允许容器内部的进程拥有访问主机内核的权限。

-e "MINIO_ROOT_USER=用户名" -e "MINIO_ROOT_PASSWORD=密码": 设置MinIO的根用户的用户名和密码。在这里,用户名为"用户名",密码为"密码"。

-v /usr/local/dev/dockerdata/minio/data:/data -v /usr/local/dev/dockerdata/minio/config:/root/.minio: 将主机上的两个目录挂载到容器内部。/usr/local/dev/dockerdata/minio/data用于存储MinIO的数据,/usr/local/dev/dockerdata/minio/config用于存储MinIO的配置信息。

minio/minio: 指定要使用的MinIO镜像。

server /data: 指定MinIO的数据存储路径为/data。

--console-address ":9001" --address ":9000": 设置MinIO的控制台地址为":9001",即可以通过主机的9001端口访问MinIO的Web控制台;同时设置MinIO的服务地址为":9000",即MinIO服务将在主机的9000端口监听。

二、SpringBoot+MinIO部署使用

2.1 导入依赖
<dependency>
    <groupId>io.minio</groupId>
    <artifactId>minio</artifactId>
    <version>7.1.0</version>
</dependency>
 <!-- hutool -->
 <dependency>
     <groupId>cn.hutool</groupId>
     <artifactId>hutool-all</artifactId>
     <version>${hutool.version}</version>
 </dependency>
2.2 yml配置
spring:
  name: minio
  servlet:
    multipart:
      max-file-size: 10MB
      max-request-size: 10MB
  #minio配置
  minio:
    access-key: 账号
    secret-key: 密码
    url: http://你的IP:9000/
    bucket-name: 桶名称
2.3 Config配置
import io.minio.MinioClient;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@Data
public class MinioConfig {

	@Value("${spring.minio.access-key}")
	private String accessKey;

	@Value("${spring.minio.secret-key}")
	private String secretKey;

	@Value("${spring.minio.url}")
	private String url;

	@Value("${spring.minio.bucket-name}")
	private String bucketName;

	@Bean
	public MinioClient minioClient() {
		return MinioClient.builder()
				.endpoint(url)
				.credentials(accessKey, secretKey)
				.build();
	}
}
2.4 Util工具类
import cn.hutool.core.io.FastByteArrayOutputStream;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

@Component
@Slf4j
@RequiredArgsConstructor
public class MinioUtil {
    //必须使用注入的方式否则会出现空指针
    @Autowired
    MinioClient minioClient;

    @Value("${spring.minio.bucket-name}")
    private String bucketName;

    /**
     * 查看存储bucket是否存在
     * bucketName 需要传入桶名
     *
     * @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
     * bucketName 需要传入桶名
     *
     * @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
     * bucketName 需要传入桶名
     *
     * @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 {
            List<Bucket> buckets = minioClient.listBuckets();
            return buckets;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 文件上传
     *
     * @param file 文件
     *             BucketName 需要传入桶名
     * @return Boolean
     */
    public String upload(MultipartFile file) {
        String originalFilename = file.getOriginalFilename();
        if (StringUtils.isBlank(originalFilename)) {
            throw new RuntimeException();
        }
        String objectName = String.valueOf(System.currentTimeMillis()) + "【tswl】" + originalFilename;
        try {
            PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(bucketName).object(objectName)
                    .stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build();
            //文件名称相同会覆盖
            minioClient.putObject(objectArgs);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return objectName;
    }

    /**
     * 预览
     *
     * @param fileName BucketName 需要传入桶名
     * @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;
    }

    /**
     * 文件下载
     * <p>
     * bucketName 桶名
     *
     * @param fileName 文件名称(包含扩展名)
     * @param res      response
     */
    public void download(String fileName, HttpServletResponse res) {
        GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucketName).object(fileName).build();
        try {
            InputStream is = minioClient.getObject(objectArgs);
            byte[] buf = new byte[1024];
            int len;
            try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()) {
                while ((len = is.read(buf)) != -1) {
                    os.write(buf, 0, len);
                }
                os.flush();
                byte[] bytes = os.toByteArray();
                res.setCharacterEncoding("UTF-8");
                res.setContentType("application/octet-stream"); // 设置内容类型为二进制流
                res.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
                try (ServletOutputStream stream = res.getOutputStream()) {
                    stream.write(bytes);
                    stream.flush();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 查看文件对象
     * BucketName 需要传入桶名
     *
     * @return 存储bucket内文件对象信息
     */
    public List<String> listObjects() {
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).build());
        List<String> fileNames = new ArrayList<>();
        try {
            for (Result<Item> result : results) {
                Item item = result.get();
                fileNames.add(item.objectName());
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return fileNames;
    }

    /**
     * 删除
     *
     * @param fileName BucketName 需要传入桶名
     * @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;
    }

    /**
     * 查看文件内容
     * <p>
     * bucketName 桶名
     *
     * @param fileName 文件名称(包含扩展名)
     * @return 文件内容字符串
     */
    public byte[] viewFile(String fileName) {
        try {
            GetObjectArgs objectArgs = GetObjectArgs.builder()
                    .bucket(bucketName)
                    .object(fileName)
                    .build();

            InputStream is = minioClient.getObject(objectArgs);

            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }

            return outputStream.toByteArray();
        } catch (Exception e) {
            log.error("查看文件内容失败: " + e.getMessage(), e);
            return null;
        }
    }
}
2.5 方法测试
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * MinIO操作的Controller。
 *
 * @Author: xufan_yang
 * @Date 2023/7/24/024 14:22
 */
@RestController
@RequestMapping("/minio")
public class MinioController {

    @Autowired
    private MinioUtil minioUtil;

    /**
     * 检查存储桶是否存在。
     *
     * @param bucketName 要检查的桶名。
     * @return 存在返回true,否则返回false。
     */
    @GetMapping("/bucketExists")
    public Boolean bucketExists(@RequestParam("bucketName") String bucketName) {
        return minioUtil.bucketExists(bucketName);
    }

    /**
     * 创建存储桶。
     *
     * @param bucketName 要创建的桶名。
     * @return 成功创建返回true,否则返回false。
     */
    @PostMapping("/makeBucket")
    public Boolean makeBucket(@RequestParam("bucketName") String bucketName) {
        return minioUtil.makeBucket(bucketName);
    }

    /**
     * 上传文件到MinIO。
     *
     * @param file 要上传的文件。
     * @return ResponseEntity,如果上传成功返回成功消息,否则返回错误消息。
     */
    @PostMapping("/upload")
    public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) {
        try {
            String objectName = minioUtil.upload(file);
            return ResponseEntity.ok("文件上传成功 对象名:" + objectName);
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("文件上传失败:" + e.getMessage());
        }
    }

    /**
     * 从MinIO下载文件。
     *
     * @param objectName 要下载的文件对象名。
     * @param response   HttpServletResponse用于将文件内容写入。
     */
    @GetMapping("/download")
    public void downloadFile(@RequestParam("objectName") String objectName, HttpServletResponse response) {
        try {
            minioUtil.download(objectName, response);
        } catch (Exception e) {
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            response.setContentType("text/plain");
            try {
                response.getWriter().write("文件下载失败:" + e.getMessage());
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
        }
    }

    /**
     * 从MinIO删除文件。
     *
     * @param objectName 要删除的文件对象名。
     * @return ResponseEntity,如果删除成功返回成功消息,否则返回错误消息。
     */
    @DeleteMapping("/delete")
    public ResponseEntity<String> deleteFile(@RequestParam("objectName") String objectName) {
        try {
            minioUtil.remove(objectName);
            return ResponseEntity.ok("文件删除成功。对象名:" + objectName);
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("文件删除失败:" + e.getMessage());
        }
    }

    /**
     * 列出MinIO存储桶中的所有文件。
     *
     * @return ResponseEntity,如果成功返回文件名列表,否则返回错误消息。
     */
    @GetMapping("/list")
    public ResponseEntity<List<String>> listFiles() {
        try {
            List<String> fileNames = minioUtil.listObjects();
            // 对文件名进行URL解码,恢复成原始的中文字符
            for (int i = 0; i < fileNames.size(); i++) {
                String decodedFileName = URLDecoder.decode(fileNames.get(i), String.valueOf(StandardCharsets.UTF_8));
                fileNames.set(i, decodedFileName);
            }
            return ResponseEntity.ok(fileNames);
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
        }
    }

    /**
     * 查看文件内容
     *
     * bucketName 桶名
     * @param fileName   文件名称(包含扩展名)
     * @return 文件内容字符串
     */
    @GetMapping("/viewFile")
    public void viewImage(@RequestParam("fileName") String fileName, HttpServletResponse response) {
        try {
            byte[] imageData = minioUtil.viewFile(fileName);
            if (imageData != null) {
                response.setContentType("image/jpeg"); // 设置响应的内容类型为图片
                response.getOutputStream().write(imageData);
            } else {
                response.setStatus(HttpStatus.NOT_FOUND.value());
            }
        } catch (Exception e) {
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            e.printStackTrace();
        }
    }
}

结语

恭喜!您已经成功地搭建了一个私有对象存储服务并学会了如何使用MinIO来上传和下载对象。MinIO是一个功能强大且易于使用的开源工具,适用于构建各种应用程序,包括数据备份、图像存储和大规模数据分析等。

请注意,本教程只涵盖了MinIO的基本功能。您可以进一步深入研究MinIO的高级特性,并根据您的需求进行配置和定制化。希望您享受使用MinIO搭建私有对象存储服务的过程!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小杨同学_丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值