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.miniogroupId>
    <artifactId>minioartifactId>
    <version>7.1.0version>
dependency>
 
 <dependency>
     <groupId>cn.hutoolgroupId>
     <artifactId>hutool-allartifactId>
     <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 getAllBuckets() {
        try {
            List 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;
    }

    /**
     * 文件下载
     * 

* 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 listObjects() { Iterable> results = minioClient.listObjects( ListObjectsArgs.builder().bucket(bucketName).build()); List fileNames = new ArrayList<>(); try { for (Result 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; } /** * 查看文件内容 *

* 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 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 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> listFiles() {
        try {
            List 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搭建私有对象存储服务的过程!

你可能感兴趣的:(docker,spring,boot,容器)