fastDFS有两种启动方式,以track启动或以store启动。tracker相当前台,告诉你房间号;store相当于房间;还可以通过nginx访问fastDFS,这种形式适合集群;但是我个人不喜欢用fastFDS,推荐使用MinIO,由apache开源,并有官网,还有后台界面;
首先要说明一下:tracker
和 storage
其实都是 fastdfs
,只不过启动时通过不同的配置文件启动,所扮演的角色不同而已。也就是说,安装 tracker
和 storage
就是在安装 fastdfs
,然后通过每个角色具体的配置文件启动即可。
IP | 角色 |
---|---|
192.168.10.101 | Tracker |
192.168.10.102 | Storage |
直接通过 Github:https://github.com/happyfish100 下载 libfastcommon
,fastdfs
,fastdfs-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
目录中创建软链接。
上传资源 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
:插件组tracker
和 storage
其实都是 fastdfs
,只不过启动时通过不同的配置文件启动,所扮演的角色不同而已。也就是说,安装 tracker
和 storage
就是在安装 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
编辑 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
查看 /fastdfs/storage/store
目录可以看到 Storage 服务器创建了 65536
个文件夹用于存储客户端上传的文件。
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:192.168.10.101/group1/M00/00/00/wKgKZl__n1yAYlScAAc6i7gsxhE232.jpg
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;
}
}
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
下次启动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;