nginx+fastDFS文件服务器以及MinIO的使用

文章目录

  • fastDFS(不推荐)
    • 机器分布
      • 下载资源
      • 安装依赖
      • 安装公共函数库
      • 安装 FastDFS
      • 启动 Tracker
      • 启动 Storage
    • nginx代理文件服务器
    • 直接上工具类
  • MinIO
    • docker安装MinIO
    • 工具类
    • 使用

fastDFS(不推荐)

fastDFS有两种启动方式,以track启动或以store启动。tracker相当前台,告诉你房间号;store相当于房间;还可以通过nginx访问fastDFS,这种形式适合集群;但是我个人不喜欢用fastFDS,推荐使用MinIO,由apache开源,并有官网,还有后台界面;

机器分布

首先要说明一下:trackerstorage 其实都是 fastdfs,只不过启动时通过不同的配置文件启动,所扮演的角色不同而已。也就是说,安装 trackerstorage 就是在安装 fastdfs,然后通过每个角色具体的配置文件启动即可。

IP 角色
192.168.10.101 Tracker
192.168.10.102 Storage

下载资源

直接通过 Github:https://github.com/happyfish100 下载 libfastcommonfastdfsfastdfs-nginx-module 三个项目对应的压缩包或者使用 git 命令下载,或者通过资源地址:https://sourceforge.net/projects/fastdfs/files/ 下载。

  • libfastcommon:从 fastdfs 项目和 fastdht 项目中提取出来的公共 C 函数库。
  • fastdfs:FastDFS 核心项目。
  • fastdfs-nginx-module:Nginx 整合 FastDFS 时 Nginx 需要添加的模块资源。

安装依赖

FastDFS 是基于 C 语言开发的,安装它之前必须先安装它所依赖的环境。

yum install -y make cmake gcc gcc-c++

安装公共函数库

上传资源 libfastcommon-master.zip 至服务器 /usr/local/src 目录后并解压。

# 安装 unzip 用于解压
yum install -y unzip
# 解压 libfastcommon 至当前所在目录
unzip libfastcommon-master.zip

编译并安装。

# 进入解压后的 libfastcommon-master 目录
cd libfastcommon-master
# 编译并安装
./make.sh && ./make.sh install

libfastcommon 默认安装在 /usr/lib64/usr/include/fastcommon 两个目录中,并且会在 /usr/lib 目录中创建软链接。

nginx+fastDFS文件服务器以及MinIO的使用_第1张图片

安装 FastDFS

上传资源 fastdfs-master.zip 至服务器 /usr/local/src 目录后并解压。

# 解压 fastdfs 至当前所在目录
unzip fastdfs-master.zip

编译并安装。

# 进入解压后的 libfastcommon-master 目录
cd fastdfs-master
# 编译并安装
./make.sh && ./make.sh install

fastdfs 默认安装在以下位置:

  • /usr/bin:可执行文件
  • /etc/fdfs:配置文件
  • /etc/init.d:主程序代码
  • /usr/include/fastdfs:插件组

nginx+fastDFS文件服务器以及MinIO的使用_第2张图片

启动 Tracker

trackerstorage 其实都是 fastdfs,只不过启动时通过不同的配置文件启动,所扮演的角色不同而已。也就是说,安装 trackerstorage 就是在安装 fastdfs,然后通过每个角色具体的配置文件启动即可。

查看 /etc/fdfs 目录下所有配置文件。

[root@localhost ~]# ls /etc/fdfs/
client.conf.sample  storage.conf.sample  storage_ids.conf.sample  tracker.conf.sample
  • client.conf.sample:客户端的配置文件,测试用
  • storage.conf.sample:存储器的配置文件
  • tracker.conf.sample:跟踪器的配置文件

编辑 tracker.conf 配置文件。

# 拷贝文件 tracker.conf.sample 并重命名为 tracker.conf
cp /etc/fdfs/tracker.conf.sample /etc/fdfs/tracker.conf
# 编辑 tracker.conf 配置文件
vi /etc/fdfs/tracker.conf

配置文件中的配置项还是蛮多的,这里暂且关注以下几个即可,后期根据实际情况再对其他配置项作出调整。

# 允许访问 tracker 服务器的 IP 地址,为空则表示不受限制
bind_addr =

# tracker 服务监听端口
port = 22122

# tracker 服务器的运行数据和日志的存储父路径(需要提前创建好)
base_path = /fastdfs/tracker

# tracker 服务器 HTTP 协议下暴露的端口
http.server_port = 8080

启动 tracker 服务。

# 创建 tracker 服务器的运行数据和日志的存储父路径
mkdir -p /fastdfs/tracker
# 启动 tracker 服务
service fdfs_trackerd start
# 查看 tracker 服务状态
service fdfs_trackerd status
# 重启 tracker 服务
service fdfs_trackerd restart
# 停止 tracker 服务
service fdfs_trackerd stop

nginx+fastDFS文件服务器以及MinIO的使用_第3张图片

启动 Storage

编辑 storage.conf 配置文件。

# 拷贝文件 storage.conf.sample 并重命名为 storage.conf
cp /etc/fdfs/storage.conf.sample /etc/fdfs/storage.conf
# 编辑 storage.conf 配置文件
vi /etc/fdfs/storage.conf

配置文件中的配置项还是蛮多的,这里暂且关注以下几个即可,后期根据实际情况再对其他配置项作出调整。

# storage 组名/卷名,默认为 group1
group_name = group1

# 允许访问 storage 服务器的 IP 地址,为空则表示不受限制
bind_addr =

# storage 服务器的运行数据和日志的存储父路径(需要提前创建好)
base_path = /fastdfs/storage/base

# storage 服务器中客户端上传的文件的存储父路径(需要提前创建好)
store_path0 = /fastdfs/storage/store

# storage 服务器 HTTP 协议下暴露的端口
http.server_port = 8888

# tracker 服务器的 IP 和端口
tracker_server = 192.168.10.101:22122

启动 storage 服务。

# 创建 storage 服务器的运行数据和日志的存储父路径
mkdir -p /fastdfs/storage/base
# 创建 storage 服务器中客户端上传的文件的存储父路径
mkdir -p /fastdfs/storage/store
# 启动 storage 服务
service fdfs_storaged start
# 查看 storage 服务状态
service fdfs_storaged status
# 重启 storage 服务
service fdfs_storaged restart
# 停止 storage 服务
service fdfs_storaged stop

nginx+fastDFS文件服务器以及MinIO的使用_第4张图片

查看 /fastdfs/storage/store 目录可以看到 Storage 服务器创建了 65536 个文件夹用于存储客户端上传的文件。

nginx代理文件服务器

fastdfs的所有节点都要以下操作

添加fastdfs-nginx-module

Nginx 整合 FastDFS 时 Nginx 需要添加 fastdfs-nginx-module 模块。直接通过 Github:https://github.com/happyfish100/fastdfs-nginx-module 下载压缩包或者使用 git 命令下载,或者通过资源地址:https://sourceforge.net/projects/fastdfs/files/ 下载。

下载以后,上传资源 fastdfs-nginx-module-master.zip 至服务器 /usr/local/src 目录后并解压。

#解压
unzip fastdfs-nginx-module-master.zip

#复制配置文件
cp /usr/local/src/fastdfs-nginx-module-master/src/mod_fastdfs.conf /etc/fdfs/

#编辑配置文件关注以下内容
vim /etc/fdfs/mod_fastdfs.conf
###############内容
# tracker 服务器的 IP 和端口
tracker_server = 192.168.10.101:22122
# url 地址是否包含组名/卷名
url_have_group_name = true
# 数据组/卷对应的路径地址
store_path0 = /fastdfs/storage/store
###################

#资源拷贝
cp /usr/local/src/fastdfs-master/conf/http.conf /etc/fdfs/
cp /usr/local/src/fastdfs-master/conf/mime.types /etc/fdfs/

nginx安装

###########################################################################start
#前置环境
yum install -y gcc gcc-c++ pcre pcre-devel zlib zlib-devel openssl-devel
# 下载 nginx 压缩包
wget -P /usr/local/src http://nginx.org/download/nginx-1.18.0.tar.gz

# 解压
tar -zxvf /usr/local/src/nginx-1.18.0.tar.gz -C /usr/local/src



#安装
# 切换至 nginx 的解压目录
cd /usr/local/src/nginx-1.18.0

# 创建 nginx 的安装目录
mkdir -p /usr/local/nginx

# 添加 fastdfs-nginx-module 模块,指定 nginx 的安装路径
./configure --add-module=/usr/local/src/fastdfs-nginx-module-master/src --prefix=/usr/local/nginx/

# 编译并安装
make && make install

# 切换目录
cd /usr/local/nginx

#编制配置文件加上
#location ~/group[0-9]/ {
#    ngx_fastdfs_module;
#}
vim /usr/local/nginx/conf/nginx.conf

#########################################################################完成配置,下一步启动



###########################################命令速查
# 帮助
[root@localhost nginx]# sbin/nginx -h
nginx version: nginx/1.18.0
Usage: nginx [-?hvVtTq] [-s signal] [-c filename] [-p prefix] [-g directives]

Options:
  -?,-h         : this help
  -v            : show version and exit
  -V            : show version and configure options then exit
  -t            : test configuration and exit
  -T            : test configuration, dump it and exit
  -q            : suppress non-error messages during configuration testing
  -s signal     : send signal to a master process: stop, quit, reopen, reload
  -p prefix     : set prefix path (default: /usr/local/nginx//)
  -c filename   : set configuration file (default: conf/nginx.conf)
  -g directives : set global directives out of configuration file
  
  
# 查看版本
[root@localhost nginx]# sbin/nginx -v
nginx version: nginx/1.18.0

# 查看版本及配置选项
[root@localhost nginx]# sbin/nginx -V
nginx version: nginx/1.18.0
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC) 
configure arguments: --prefix=/usr/local/nginx/

# 检测配置文件是否有语法错误,默认检测 conf/nginx.conf
[root@localhost nginx]# sbin/nginx -t -c /usr/local/nginx/conf/nginx.conf
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful


#生命周期管理
# 启动并通过 -c 指定启动时加载的配置文件,默认加载 conf/nginx.conf
sbin/nginx -c /usr/local/nginx/conf/nginx.conf
# 平滑的重启,重新加载配置
sbin/nginx -s reload
# 快速停止
sbin/nginx -s stop

# 完整有序的停止
sbin/nginx -s quit
# 重新打开日志文件(日志切割)。
sbin/nginx -s reopen

整合完成

使用工具类上传

nginx+fastDFS文件服务器以及MinIO的使用_第5张图片

访问nginx:192.168.10.101/group1/M00/00/00/wKgKZl__n1yAYlScAAc6i7gsxhE232.jpg

nginx+fastDFS文件服务器以及MinIO的使用_第6张图片

group1是我们在配置文件中定义的组,后面的一串是调用工具类返回的文件路径;

直接上工具类

1.jar包

FastDFS 的作者余庆先生已经为我们开发好了 Java 对应的 SDK。这里需要解释一下:作者余庆并没有及时更新最新的 Java SDK 至 Maven 中央仓库,目前中央仓库最新版仍旧是 1.27 版。所以我们需要通过 Github:https://github.com/happyfish100/fastdfs-client-java 下载项目源码,再通过命令 mvn clean install 编译打包导入 Maven 本地仓库使用。

2.配置文件

dfs.properties: classPath下,放resources里

# 超时时间
fastdfs.connect_timeout_in_seconds = 10
fastdfs.network_timeout_in_seconds = 30
# 编码字符集
fastdfs.charset = UTF-8
# tracker 服务器 HTTP 协议下暴露的端口
fastdfs.http_tracker_http_port = 8080
# tracker 服务器的 IP 和端口
fastdfs.tracker_servers = 192.168.10.101:22122

3.配置文件需要的扩张属性

按上面安装的不需要加属性

 public static void initByProperties(Properties props) throws IOException, MyException {
        String trackerServersConf = props.getProperty("fastdfs.tracker_servers");
        if (trackerServersConf != null && trackerServersConf.trim().length() != 0) {
            initByTrackers(trackerServersConf.trim());
            String connectTimeoutInSecondsConf = props.getProperty("fastdfs.connect_timeout_in_seconds");
            String networkTimeoutInSecondsConf = props.getProperty("fastdfs.network_timeout_in_seconds");
            String charsetConf = props.getProperty("fastdfs.charset");
            String httpAntiStealTokenConf = props.getProperty("fastdfs.http_anti_steal_token");
            String httpSecretKeyConf = props.getProperty("fastdfs.http_secret_key");
            String httpTrackerHttpPortConf = props.getProperty("fastdfs.http_tracker_http_port");
            String poolEnabled = props.getProperty("fastdfs.connection_pool.enabled");
            String poolMaxCountPerEntry = props.getProperty("fastdfs.connection_pool.max_count_per_entry");
            String poolMaxIdleTime = props.getProperty("fastdfs.connection_pool.max_idle_time");
            String poolMaxWaitTimeInMS = props.getProperty("fastdfs.connection_pool.max_wait_time_in_ms");
            if (connectTimeoutInSecondsConf != null && connectTimeoutInSecondsConf.trim().length() != 0) {
                g_connect_timeout = Integer.parseInt(connectTimeoutInSecondsConf.trim()) * 1000;
            }

            if (networkTimeoutInSecondsConf != null && networkTimeoutInSecondsConf.trim().length() != 0) {
                g_network_timeout = Integer.parseInt(networkTimeoutInSecondsConf.trim()) * 1000;
            }

            if (charsetConf != null && charsetConf.trim().length() != 0) {
                g_charset = charsetConf.trim();
            }

            if (httpAntiStealTokenConf != null && httpAntiStealTokenConf.trim().length() != 0) {
                g_anti_steal_token = Boolean.parseBoolean(httpAntiStealTokenConf);
            }

            if (httpSecretKeyConf != null && httpSecretKeyConf.trim().length() != 0) {
                g_secret_key = httpSecretKeyConf.trim();
            }

            if (httpTrackerHttpPortConf != null && httpTrackerHttpPortConf.trim().length() != 0) {
                g_tracker_http_port = Integer.parseInt(httpTrackerHttpPortConf);
            }

            if (poolEnabled != null && poolEnabled.trim().length() != 0) {
                g_connection_pool_enabled = Boolean.parseBoolean(poolEnabled);
            }

            if (poolMaxCountPerEntry != null && poolMaxCountPerEntry.trim().length() != 0) {
                g_connection_pool_max_count_per_entry = Integer.parseInt(poolMaxCountPerEntry);
            }

            if (poolMaxIdleTime != null && poolMaxIdleTime.trim().length() != 0) {
                g_connection_pool_max_idle_time = Integer.parseInt(poolMaxIdleTime) * 1000;
            }

            if (poolMaxWaitTimeInMS != null && poolMaxWaitTimeInMS.trim().length() != 0) {
                g_connection_pool_max_wait_time_in_ms = Integer.parseInt(poolMaxWaitTimeInMS);
            }

        } else {
            throw new MyException(String.format("configure item %s is required", "fastdfs.tracker_servers"));
        }
    }

4.工具类

package com.xxxx.utils;

import org.apache.commons.lang3.StringUtils;
import org.csource.common.MyException;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.ClientGlobal;
import org.csource.fastdfs.FileInfo;
import org.csource.fastdfs.StorageClient;
import org.csource.fastdfs.StorageServer;
import org.csource.fastdfs.TrackerClient;
import org.csource.fastdfs.TrackerServer;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * FastDFS 分布式文件系统 Java 客户端工具类
 * 具体功能:文件上传、下载、替换、删除、查询文件元数据、查看文件详情
 */
public class FastDFSClient {

	// 获取配置文件地址windows写法
	//private static final String CONF_FILENAME = "E:\\IDEA Workspace\\fastdfs-demo\\src\\main\\resources\\fdfs_client.conf";
	//linux写法,在windows里找不到
//	private static final String CONF_FILENAME =
//			Thread.currentThread()
//			.getContextClassLoader().getResource("fdfs_client.conf").getPath();

	//为了统一,写一个properties文件,这样就可以兼容了  ClientGlobal.initByProperties(pro);
	// Storage 存储服务器客户端
	private static StorageClient storageClient = null;

	static {
		try {
			// 加载配置文件
			//ClientGlobal.init(CONF_FILENAME);
			Properties pro=new Properties();
			pro.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("dfs.properties"));
			ClientGlobal.initByProperties(pro);
			// 初始化 Tracker 客户端
			TrackerClient trackerClient = new TrackerClient(ClientGlobal.g_tracker_group);
			// 初始化 Tracker 服务端
			TrackerServer trackerServer = trackerClient.getTrackerServer();
			// 初始化 Storage 服务端
			StorageServer storageServer = trackerClient.getStoreStorage(trackerServer);
			// 初始化 Storage 客户端
			storageClient = new StorageClient(trackerServer, storageServer);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (MyException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 文件上传
	 *
	 * @param inputStream 上传的文件的字节输入流
	 * @param fileName    上传的文件的原始名
	 * @return
	 */
	public static String[] uploadFile(InputStream inputStream, String fileName) {
		try {
			// 准备字节数组
			byte[] fileBuff = null;
			// 文件元数据
			NameValuePair[] metaList = null;
			if (inputStream != null) {
				// 查看文件的长度
				int len = inputStream.available();
				// 初始化元数据数组
				metaList = new NameValuePair[2];
				// 第一组元数据,文件的原始名称
				metaList[0] = new NameValuePair("file_name", fileName);
				// 第二组元数据,文件的长度
				metaList[1] = new NameValuePair("file_length", String.valueOf(len));
				// 创建对应长度的字节数组
				fileBuff = new byte[len];
				// 将输入流中的字节内容,读到字节数组中
				inputStream.read(fileBuff);
			}
            /*
                上传文件。
                参数含义:要上传的文件的内容(使用字节数组传递),上传的文件的类型(扩展名),元数据
             */
			String[] fileids = storageClient.upload_file(fileBuff, getFileExt(fileName), metaList);
			return fileids;
		} catch (IOException e) {
			e.printStackTrace();
		} catch (MyException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 文件上传
	 *
	 * @param file     上传的文件
	 * @param fileName 上传的文件的原始名
	 * @return
	 */
	public static String[] uploadFile(File file, String fileName) {
		try (FileInputStream fis = new FileInputStream(file)) {
			return uploadFile(fis, fileName);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 获取文件后缀名(不带点)
	 *
	 * @param fileName
	 * @return 如:"jpg" or ""
	 */
	private static String getFileExt(String fileName) {
		if (StringUtils.isBlank(fileName) || !fileName.contains(".")) {
			return "";
		}
		return fileName.substring(fileName.lastIndexOf(".") + 1); // 不带最后的点
	}

	/**
	 * 获取文件详情
	 *
	 * @param groupName      组/卷名,默认值:group1
	 * @param remoteFileName 文件名,例如:"M00/00/00/wKgKZl9tkTCAJAanAADhaCZ_RF0495.jpg"
	 * @return 文件详情
	 */
	public static FileInfo getFileInfo(String groupName, String remoteFileName) {
		try {
			return storageClient.get_file_info(groupName == null ? "group1" : groupName, remoteFileName);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (MyException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 获取元数据
	 *
	 * @param groupName      组/卷名,默认值:group1
	 * @param remoteFileName 文件名,例如:"M00/00/00/wKgKZl9tkTCAJAanAADhaCZ_RF0495.jpg"
	 * @return 文件的元数据数组
	 */
	public static NameValuePair[] getMetaData(String groupName, String remoteFileName) {
		try {
			// 根据组名和文件名通过 Storage 客户端获取文件的元数据数组
			return storageClient.get_metadata(groupName == null ? "group1" : groupName, remoteFileName);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (MyException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 文件下载
	 *
	 * @param groupName      组/卷名,默认值:group1
	 * @param remoteFileName 文件名,例如:"M00/00/00/wKgKZl9tkTCAJAanAADhaCZ_RF0495.jpg"
	 * @return 文件的字节输入流
	 */
	public static InputStream downloadFile(String groupName, String remoteFileName) {
		try {
			// 根据组名和文件名通过 Storage 客户端获取文件的字节数组
			byte[] bytes = storageClient.download_file(groupName == null ? "group1" : groupName, remoteFileName);
			// 返回字节流对象
			InputStream inputStream = new ByteArrayInputStream(bytes);
			return inputStream;
		} catch (IOException e) {
			e.printStackTrace();
		} catch (MyException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 文件删除
	 *
	 * @param groupName      组/卷名,默认值:group1
	 * @param remoteFileName 文件名,例如:"M00/00/00/wKgKZl9tkTCAJAanAADhaCZ_RF0495.jpg"
	 * @return 0为成功,非0为失败
	 */
	public static int deleteFile(String groupName, String remoteFileName) {
		int result = -1;
		try {
			// 根据组名和文件名通过 Storage 客户端删除文件
			result = storageClient.delete_file(groupName == null ? "group1" : groupName, remoteFileName);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (MyException e) {
			e.printStackTrace();
		}
		return result;
	}

	/**
	 * 修改一个已经存在的文件
	 *
	 * @param oldGroupName 旧组名
	 * @param oldFileName  旧文件名
	 * @param file         新文件
	 * @param fileName     新文件名
	 * @return
	 */
	public static String[] modifyFile(String oldGroupName, String oldFileName, File file, String fileName) {
		// 先上传
		String[] fileids = uploadFile(file, fileName);
		if (fileids == null) {
			return null;
		}
		// 再删除
		int delResult = deleteFile(oldGroupName, oldFileName);
		if (delResult != 0) {
			return null;
		}
		return fileids;
	}

}

MinIO

docker安装MinIO

docker run -p 9000:9000 \
  -e "MINIO_ACCESS_KEY=AKIAIOSFODNN7EXAMPLE" \
  -e "MINIO_SECRET_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" \
  minio/minio server /data

登录:http://192.168.10.100:9000/ access:AKIAIOSFODNN7EXAMPLE secret:wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY 添加桶,点击桶,设置权限:* read and write
nginx+fastDFS文件服务器以及MinIO的使用_第7张图片

下次启动minio: docker start 容器id

工具类

1.jar包



        
            io.minio
            minio
            7.0.2
        

2.配置文件

application.yml添加

#minio文件客户端配置
minio:
  endpoint: localhost             #192.168.10.100
  port: 9001
  accessKey: AKIAIOSFODNN7EXAMPLE
  secretKey: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
  secure: false
  bucketName: "lwf"
  configDir: "/data/"

3.配置类

import io.minio.MinioClient;
import io.minio.errors.InvalidEndpointException;
import io.minio.errors.InvalidPortException;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

/**
 * @author lwf
 * @title: MinioConfig
 * @projectName yeb
 * @description: TODO
 * @date 2020/12/239:29
 */


@Data
@Component
@ConfigurationProperties(prefix = "minio")
@ApiModel("minio配置")
public class MinioConfig {

    @ApiModelProperty("endPoint是一个URL,域名,IPv4或者IPv6地址")
    private String endpoint;

    @ApiModelProperty("TCP/IP端口号")
    private int port;

    @ApiModelProperty("accessKey类似于用户ID,用于唯一标识你的账户")
    private String accessKey;

    @ApiModelProperty("secretKey是你账户的密码")
    private String secretKey;

    @ApiModelProperty("如果是true,则用的是https而不是http,默认值是true")
    private Boolean secure;

    @ApiModelProperty("默认存储桶")
    private String bucketName;

    @ApiModelProperty("配置目录")
    private String configDir;

    @Bean
    public MinioClient getMinioClient() throws InvalidEndpointException, InvalidPortException {
        MinioClient minioClient = new MinioClient(endpoint, port, accessKey, secretKey,secure);
        return minioClient;
    }
}

3.工具类

import io.minio.MinioClient;
import io.minio.ObjectStat;
import io.minio.PutObjectOptions;
import io.minio.Result;
import io.minio.errors.ErrorResponseException;
import io.minio.errors.InvalidExpiresRangeException;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteError;
import io.minio.messages.Item;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
 * @author lwf
 * @title: MinioUtil
 * @projectName yeb
 * @description: TODO
 * @date 2020/12/239:31
 */
@Component
public class MinioUtil {

    @Autowired
    private MinioClient minioClient;

    private static final int DEFAULT_EXPIRY_TIME = 7 * 24 * 3600;

    /**
     * 检查存储桶是否存在
     *
     * @param bucketName 存储桶名称
     * @return
     */
    @SneakyThrows
    public boolean bucketExists(String bucketName) {
        boolean flag = false;
        flag = minioClient.bucketExists(bucketName);
        if (flag) {
            return true;
        }
        return false;
    }

    /**
     * 创建存储桶
     *
     * @param bucketName 存储桶名称
     */
    @SneakyThrows
    public boolean makeBucket(String bucketName) {
        boolean flag = bucketExists(bucketName);
        if (!flag) {
            minioClient.makeBucket(bucketName);
            return true;
        } else {
            return false;
        }
    }

    /**
     * 列出所有存储桶名称
     *
     * @return
     */
    @SneakyThrows
    public List listBucketNames() {
        List bucketList = listBuckets();
        List bucketListName = new ArrayList<>();
        for (Bucket bucket : bucketList) {
            bucketListName.add(bucket.name());
        }
        return bucketListName;
    }

    /**
     * 列出所有存储桶
     *
     * @return
     */
    @SneakyThrows
    public List listBuckets() {
        return minioClient.listBuckets();
    }

    /**
     * 删除存储桶
     *
     * @param bucketName 存储桶名称
     * @return
     */
    @SneakyThrows
    public boolean removeBucket(String bucketName) {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            Iterable> myObjects = listObjects(bucketName);
            for (Result result : myObjects) {
                Item item = result.get();
                // 有对象文件,则删除失败
                if (item.size() > 0) {
                    return false;
                }
            }
            // 删除存储桶,注意,只有存储桶为空时才能删除成功。
            minioClient.removeBucket(bucketName);
            flag = bucketExists(bucketName);
            if (!flag) {
                return true;
            }

        }
        return false;
    }

    /**
     * 列出存储桶中的所有对象名称
     *
     * @param bucketName 存储桶名称
     * @return
     */
    @SneakyThrows
    public List listObjectNames(String bucketName) {
        List listObjectNames = new ArrayList<>();
        boolean flag = bucketExists(bucketName);
        if (flag) {
            Iterable> myObjects = listObjects(bucketName);
            for (Result result : myObjects) {
                Item item = result.get();
                listObjectNames.add(item.objectName());
            }
        }
        return listObjectNames;
    }

    /**
     * 列出存储桶中的所有对象
     *
     * @param bucketName 存储桶名称
     * @return
     */
    @SneakyThrows
    public Iterable> listObjects(String bucketName) {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            return minioClient.listObjects(bucketName);
        }
        return null;
    }

    /**
     * 通过文件上传到对象
     *
     * @param bucketName 存储桶名称
     * @param objectName 存储桶里的对象名称
     * @param fileName   File name
     * @return
     */
    @SneakyThrows
    public boolean putObject(String bucketName, String objectName, String fileName) {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            minioClient.putObject(bucketName, objectName, fileName, null);
            ObjectStat statObject = statObject(bucketName, objectName);
            if (statObject != null && statObject.length() > 0) {
                return true;
            }
        }
        return false;

    }

    /**
     * 文件上传
     *
     * @param bucketName
     * @param multipartFile
     */
    @SneakyThrows
    public void putObject(String bucketName, MultipartFile multipartFile, String filename) {
        PutObjectOptions putObjectOptions = new PutObjectOptions(multipartFile.getSize(), PutObjectOptions.MIN_MULTIPART_SIZE);
        putObjectOptions.setContentType(multipartFile.getContentType());
        minioClient.putObject(bucketName, filename, multipartFile.getInputStream(), putObjectOptions);
    }

    /**
     * 通过InputStream上传对象
     *
     * @param bucketName 存储桶名称
     * @param objectName 存储桶里的对象名称
     * @param stream     要上传的流
     * @return
     */
    @SneakyThrows
    public boolean putObject(String bucketName, String objectName, InputStream stream) {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            minioClient.putObject(bucketName, objectName, stream, new PutObjectOptions(stream.available(), -1));
            ObjectStat statObject = statObject(bucketName, objectName);
            if (statObject != null && statObject.length() > 0) {
                return true;
            }
        }
        return false;
    }

    /**
     * 以流的形式获取一个文件对象
     *
     * @param bucketName 存储桶名称
     * @param objectName 存储桶里的对象名称
     * @return
     */
    @SneakyThrows
    public InputStream getObject(String bucketName, String objectName) {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            ObjectStat statObject = statObject(bucketName, objectName);
            if (statObject != null && statObject.length() > 0) {
                InputStream stream = minioClient.getObject(bucketName, objectName);
                return stream;
            }
        }
        return null;
    }

    /**
     * 以流的形式获取一个文件对象(断点下载)
     *
     * @param bucketName 存储桶名称
     * @param objectName 存储桶里的对象名称
     * @param offset     起始字节的位置
     * @param length     要读取的长度 (可选,如果无值则代表读到文件结尾)
     * @return
     */
    @SneakyThrows
    public InputStream getObject(String bucketName, String objectName, long offset, Long length) {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            ObjectStat statObject = statObject(bucketName, objectName);
            if (statObject != null && statObject.length() > 0) {
                InputStream stream = minioClient.getObject(bucketName, objectName, offset, length);
                return stream;
            }
        }
        return null;
    }

    /**
     * 下载并将文件保存到本地
     *
     * @param bucketName 存储桶名称
     * @param objectName 存储桶里的对象名称
     * @param fileName   File name
     * @return
     */
    @SneakyThrows
    public boolean getObject(String bucketName, String objectName, String fileName) {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            ObjectStat statObject = statObject(bucketName, objectName);
            if (statObject != null && statObject.length() > 0) {
                minioClient.getObject(bucketName, objectName, fileName);
                return true;
            }
        }
        return false;
    }

    /**
     * 删除一个对象
     *
     * @param bucketName 存储桶名称
     * @param objectName 存储桶里的对象名称
     */
    @SneakyThrows
    public boolean removeObject(String bucketName, String objectName) {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            minioClient.removeObject(bucketName, objectName);
            return true;
        }
        return false;
    }

    /**
     * 删除指定桶的多个文件对象,返回删除错误的对象列表,全部删除成功,返回空列表
     *
     * @param bucketName  存储桶名称
     * @param objectNames 含有要删除的多个object名称的迭代器对象
     * @return
     */
    @SneakyThrows
    public List removeObject(String bucketName, List objectNames) {
        List deleteErrorNames = new ArrayList<>();
        boolean flag = bucketExists(bucketName);
        if (flag) {
            Iterable> results = minioClient.removeObjects(bucketName, objectNames);
            for (Result result : results) {
                DeleteError error = result.get();
                deleteErrorNames.add(error.objectName());
            }
        }
        return deleteErrorNames;
    }

    /**
     * 生成一个给HTTP GET请求用的presigned URL。
     * 浏览器/移动端的客户端可以用这个URL进行下载,即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间,默认值是7天。
     *
     * @param bucketName 存储桶名称
     * @param objectName 存储桶里的对象名称
     * @param expires    失效时间(以秒为单位),默认是7天,不得大于七天
     * @return
     */
    @SneakyThrows
    public String presignedGetObject(String bucketName, String objectName, Integer expires) {
        boolean flag = bucketExists(bucketName);
        String url = "";
        if (flag) {
            if (expires < 1 || expires > DEFAULT_EXPIRY_TIME) {
                throw new InvalidExpiresRangeException(expires,
                        "expires must be in range of 1 to " + DEFAULT_EXPIRY_TIME);
            }
            url = minioClient.presignedGetObject(bucketName, objectName, expires);
        }
        return url;
    }

    /**
     * 生成一个给HTTP PUT请求用的presigned URL。
     * 浏览器/移动端的客户端可以用这个URL进行上传,即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间,默认值是7天。
     *
     * @param bucketName 存储桶名称
     * @param objectName 存储桶里的对象名称
     * @param expires    失效时间(以秒为单位),默认是7天,不得大于七天
     * @return
     */
    @SneakyThrows
    public String presignedPutObject(String bucketName, String objectName, Integer expires) {
        boolean flag = bucketExists(bucketName);
        String url = "";
        if (flag) {
            if (expires < 1 || expires > DEFAULT_EXPIRY_TIME) {
                throw new InvalidExpiresRangeException(expires,
                        "expires must be in range of 1 to " + DEFAULT_EXPIRY_TIME);
            }
            url = minioClient.presignedPutObject(bucketName, objectName, expires);
        }
        return url;
    }

    /**
     * 获取对象的元数据
     *
     * @param bucketName 存储桶名称
     * @param objectName 存储桶里的对象名称
     * @return
     */
    @SneakyThrows
    public ObjectStat statObject(String bucketName, String objectName) {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            ObjectStat statObject = minioClient.statObject(bucketName, objectName);
            return statObject;
        }
        return null;
    }

    /**
     * 文件访问路径
     *
     * @param bucketName 存储桶名称
     * @param objectName 存储桶里的对象名称
     * @return
     */
    @SneakyThrows
    public String getObjectUrl(String bucketName, String objectName) {
        boolean flag = bucketExists(bucketName);
        String url = "";
        if (flag) {
            url = minioClient.getObjectUrl(bucketName, objectName);
        }
        return url;
    }



    public void downloadFile(String bucketName, String fileName, String originalName, HttpServletResponse response) {
        try {

            InputStream file = minioClient.getObject(bucketName, fileName);
            String filename = new String(fileName.getBytes("ISO8859-1"), StandardCharsets.UTF_8);
            if (StringUtils.isNotEmpty(originalName)) {
                fileName = originalName;
            }
            response.setHeader("Content-Disposition", "attachment;filename=" + filename);
            ServletOutputStream servletOutputStream = response.getOutputStream();
            int len;
            byte[] buffer = new byte[1024];
            while ((len = file.read(buffer)) > 0) {
                servletOutputStream.write(buffer, 0, len);
            }
            servletOutputStream.flush();
            file.close();
            servletOutputStream.close();
        } catch (ErrorResponseException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

使用

直接注入工具类,调用即可

 @Resource
   private MinioUtil minioUtil;

你可能感兴趣的:(spring,fastdfs,docker,java)