目标:
使用docker搭建minio对象存储,使用minio作为shareX截图软件图床,以便于在编辑typora文档时的图片资源可以通过minio管理,移动typora文档时不用再估计图片的存储。
文末有jar包下载提供
- MinIO 是一个基于Apache License v2.0开源协议的对象存储服务。
它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,
例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。
MinIO是一个非常轻量的服务,可以很简单的和其他应用的结合,类似 NodeJS, Redis 或者 MySQL。 - 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
- 设置桶(bucket)的访问权限
- 请求永久性url
url构成:
http://ip:port/bucket名称/图片名称.后缀
示例:
请求 http://www.test.xyz:9000/share-x/mac.jpg 时 响应如下:
3. 使用minio搭建shareX图床
1. 首先需要完成第1步和第2步(先部署minio并且设置用于做图床的bucket为公共的,以便直接访问图片)
2. java实现上传
- 项目结构
- 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
- 项目完成后打jar包
- 上传到服务器运行
我这里打的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截图上传使用该图床
- 设置上传目的地
下图的bucket的名称与第2点minio中创建bucket并设置公共权限中的bucket一致
填写返回的图片url:
这里先用postman请求接口得到返回值如下:
所以返回的格式直接可以通过url获取,填写如下图:
若有些返回是:
{ "code": 0, "msg": "xxx", "data": { "url": xxx, ...... } }
如下填写为:
$json:data.url$
- 快捷键设置
所有设置如下:
设置快捷键,我这里是 Alt + F1 出发矩形框截图
到此,使用shareX—>使用 Alt + F1 进行截图后,会自动将该图片上传到minio的 share-x 桶中。
截图示例:
使用快捷键 Alt + F1 进行截图后进入图片编辑,编辑后点击下图标识处会上传图片到minio,上传后会将响应的图片url复制在剪切板,直接在typora编辑器中粘贴使用即可
`