目标:
使用docker搭建minio对象存储,使用minio作为shareX截图软件图床,以便于在编辑typora文档时的图片资源可以通过minio管理,移动typora文档时不用再估计图片的存储。

文末有jar包下载提供

  1. MinIO 是一个基于Apache License v2.0开源协议的对象存储服务。
    它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,
    例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。
    MinIO是一个非常轻量的服务,可以很简单的和其他应用的结合,类似 NodeJS, Redis 或者 MySQL。
  2. shareX 是开源的高级截图工具和屏幕记录器。

1. 单体部署

MINIO_ACCESS_KEY:登录账号
MINIO_SECRET_KEY:登录密码
/mnt/data:映射的宿主机文件存放地址
/mnt/config:映射的宿主机配置文件

# 1. 创建目录
mkdir -p /mnt/minio/data
mkdir -p /mnt/minio/config
mkdir /etc/minio
cd /etc/minio
touch localtime
# 2. 启动容器
 docker run -di -p 9000:9000 --name minio \
--restart=always \
-e "MINIO_ACCESS_KEY=minioadmin123"  \
-e "MINIO_SECRET_KEY=minioadmin123" \
-v /mnt/minio/data:/data \
-v /mnt/minio/config:/root/.minio \
-v /etc/minio/localtime:/etc/localtime \
minio/minio:RELEASE.2020-10-12T21-53-21Z server /data    #minio/minio:镜像版本 server /data


# 3. 若有防火墙,需要开启端口
 firewall-cmd --zone=public --add-port=9000/tcp --permanent //开放端口
 firewall-cmd --reload

2. minio中创建bucket并设置公共权限

minio中分享图片的url都是有时效性的,下面来生成永久性的url

  1. 设置桶(bucket)的访问权限


docker兰空图床 图床 docker_docker兰空图床

  1. 请求永久性url
    url构成:
http://ip:port/bucket名称/图片名称.后缀

示例:

请求 http://www.test.xyz:9000/share-x/mac.jpg 时 响应如下:

docker兰空图床 图床 docker_java_02

3. 使用minio搭建shareX图床

1. 首先需要完成第1步和第2步(先部署minio并且设置用于做图床的bucket为公共的,以便直接访问图片)

2. java实现上传

  1. 项目结构
  2. java代码
    pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.jane</groupId>
    <artifactId>minioImagesBed</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <minio.version>6.0.8</minio.version>
        <lombok.version>1.18.0</lombok.version>
        <qiniu.version>7.2.23</qiniu.version>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.5.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>${minio.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.qiniu</groupId>
            <artifactId>qiniu-java-sdk</artifactId>
            <version>${qiniu.version}</version>
        </dependency>

    </dependencies>

    <!--maven构建-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.yml

server:
  port: 8008
minio:
  # minio 地址 (ip:port 或者 域名)
  endpoint: 127.0.0.1:9000
  # minio登录的 accessKey
  accessKey: minioadmin123
  # minio登录的 secretKey
  secretKey: minioadmin123

MinioClientConfig

package com.jane.imgbed.config;

import io.minio.MinioClient;
import io.minio.errors.InvalidEndpointException;
import io.minio.errors.InvalidPortException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;

/**
 * @author Jane
 * @date 2021/8/5
 */
@Configuration
public class MinioClientConfig {
    @Value("${minio.endpoint}")
    private String endpoint;
    @Value("${minio.accessKey}")
    private String accessKey;
    @Value("${minio.secretKey}")
    private String secretKey;

    @Bean
    public MinioClient minioClient() throws InvalidPortException, InvalidEndpointException, IOException {
        if (!endpoint.contains("http://") && !endpoint.contains("https://")){
            endpoint = "http://" + endpoint;
        }
        return new MinioClient(endpoint, accessKey, secretKey);
    };
}

MinioUtils

package com.jane.imgbed.utils;


import com.qiniu.util.Md5;
import io.minio.MinioClient;
import lombok.Data;
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 java.nio.charset.StandardCharsets;

/**
 * Minio 工具类
 *
 * @author Jane
 */
@Data
@Component
@Slf4j
public class MinioUtils {

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

    @Autowired
    private MinioClient minioClient;

    /**
     * 创建bucket
     *
     * @param bucketName 存储桶名称
     */
    public void createBucket(String bucketName) throws Exception {
        if (!minioClient.bucketExists(bucketName)) {
            //创建bucket
            minioClient.makeBucket(bucketName);
        }

    }

    /**
     * 上传文件
     *
     * @param file       文件
     * @param bucketName 存储桶名称
     * @return
     */
    public String uploadFile(MultipartFile file, String bucketName) throws Exception {
        //频道上传文件是否为空
        if (null == file || 0 == file.getSize()) {
            throw new RuntimeException("上传文件不能为空");
        }
        if (bucketName == null || bucketName.length() == 0) {
            throw new RuntimeException("存储桶名称不能为空");
        }
        //文件名
        String[] split = file.getOriginalFilename().split("\\.");
        String name = split[0] + System.currentTimeMillis();
        String md5FileName = Md5.md5(name.getBytes(StandardCharsets.UTF_8)) + "." + split[1];
        //开始上传
        minioClient.putObject(bucketName, md5FileName, file.getInputStream(), file.getContentType());

        return url + "/" + bucketName + "/" + md5FileName;
    }

    public boolean isImage(String fileName) {
        //设置允许上传文件类型
        String suffixList = "jpg,gif,png,ico,bmp,jpeg,jfif";
        //获取文件后缀
        String suffix = fileName.substring(fileName.lastIndexOf(".")
                + 1, fileName.length());
        if (suffixList.contains(suffix.trim().toLowerCase())) {
            return true;
        }
        return false;
    }

    /**
     * 删除文件
     *
     * @param bucketName 存储桶名称
     * @param fileName   文件名称
     * @throws Exception
     */
    public void removeObject(String bucketName, String fileName) throws Exception {
        try {
            if (minioClient.bucketExists(bucketName)) {
                minioClient.removeObject(bucketName, fileName);
            }
        } catch (Exception ee) {
            throw new RuntimeException("删除文件失败," + ee.getMessage());
        }
    }
}

响应实体 R

package com.jane.imgbed.utils;

import org.apache.http.HttpStatus;

import java.util.HashMap;
import java.util.Map;

/**
 * 返回数据
 *
 * @author Jane
 */
public class R extends HashMap<String, Object> {
	private static final long serialVersionUID = 1L;

	public R() {
		put("code", 0);
		put("msg", "success");
	}

	public static R error() {
		return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, "未知异常,请联系管理员");
	}

	public static R error(String msg) {
		return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, msg);
	}

	public static R error(int code, String msg) {
		R r = new R();
		r.put("code", code);
		r.put("msg", msg);
		return r;
	}

	public static R ok(String msg) {
		R r = new R();
		r.put("msg", msg);
		return r;
	}

	public static R ok(Map<String, Object> map) {
		R r = new R();
		r.putAll(map);
		return r;
	}

	public static R ok() {
		return new R();
	}

	public R put(String key, Object value) {
		super.put(key, value);
		return this;
	}
}

上传接口 ImgBedController

package com.jane.imgbed.controller;

import com.jane.imgbed.utils.MinioUtils;
import com.jane.imgbed.utils.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;


/**
 * @author Jane
 * @date 2021/8/5
 */
@RestController
@RequestMapping(value = "/minio/imgbed")
public class ImgBedController {

    @Autowired
    private MinioUtils minioUtils;

    @PostMapping(value = "/upload/{bucketName}")
    public R upload(@RequestBody MultipartFile file, @PathVariable String bucketName) {
        try{
            String url = minioUtils.uploadFile(file, bucketName);
            if (!url.contains("http://") && !url.contains("https://")){
                url = "http://" + url;
            }
            return R.ok().put("url", url);
        }catch (Exception e){
            e.printStackTrace();
            return R.error("上传失败!");
        }
    }
}

MinioImgBedApplication

package com.jane.imgbed;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author Jane
 * @date 2021/8/5
 */
@SpringBootApplication
public class MinioImgBedApplication {

    public static void main(String[] args) {
        SpringApplication.run(MinioImgBedApplication.class, args);
    }
}

4. 部署jar

  1. 项目完成后打jar包
  2. 上传到服务器运行

我这里打的jar包名称为 minioImagesBed-1.0-SNAPSHOT.jar

部署时,需要根据自己需求修改jar包中application.yml中的minio参数

我的接口url为:127.0.0.1:8008/minio/imgbed/upload/share-x

nohup java -jar minioImagesBed-1.0-SNAPSHOT.jar > nohup.out 2>&1 &

5. 设置shareX截图上传使用该图床

  1. 设置上传目的地


    下图的bucket的名称与第2点minio中创建bucket并设置公共权限中的bucket一致

docker兰空图床 图床 docker_java_03

填写返回的图片url:

这里先用postman请求接口得到返回值如下:

docker兰空图床 图床 docker_java_04

所以返回的格式直接可以通过url获取,填写如下图:

若有些返回是:

{ "code": 0, "msg": "xxx", "data": { "url": xxx, ...... } }

如下填写为:$json:data.url$

docker兰空图床 图床 docker_上传_05

  1. 快捷键设置

docker兰空图床 图床 docker_docker兰空图床_06

所有设置如下:

docker兰空图床 图床 docker_docker兰空图床_07

设置快捷键,我这里是 Alt + F1 出发矩形框截图

docker兰空图床 图床 docker_spring_08

到此,使用shareX—>使用 Alt + F1 进行截图后,会自动将该图片上传到minio的 share-x 桶中。

截图示例:

使用快捷键 Alt + F1 进行截图后进入图片编辑,编辑后点击下图标识处会上传图片到minio,上传后会将响应的图片url复制在剪切板,直接在typora编辑器中粘贴使用即可

docker兰空图床 图床 docker_docker_09


`