FastDFS-Client 源码的gitHub 地址:
https://github.com/happyfish100/fastdfs-client-java
这个Client的简单用法可自行去看博客:FastDFSClient集成SpringBoot 基础用法
简单用法主要是根据fdfs_client.conf 配置文件的配置来进行的,这样在打包发布后,配置文件不方便修改,对维护成本也有所增加。所以在这根据FastDFS-Client的源码进行改造,修改为使用application.yml配置文件就行fastdfs的配置,同时将配置文件存在Nacos中,以便后续生产改动服务器地址提供便利。
先看看改造后的工程(这里是做成了微服务的组件,通过加入依赖就可以使用):
工程的pom.xml如下(最主要的就是fastdfs-client-java 和 commons-pool2)另外的是工程的核心组件:
syiti-components
com.syiti.dev
2.0.0
4.0.0
component-fastdfs
org.csource
fastdfs-client-java
org.slf4j
slf4j-log4j12
1.27
com.syiti.dev
component-core
org.apache.commons
commons-pool2
2.6.0
然后在properties包下创建FastdfsProperties配置文件实体类
package com.syiti.dev.component.fastdfs.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author LinYoufeng
* @date 2020-01-03 下午4:05
*/
@Data
@ConfigurationProperties(prefix = "fastdfs")
public class FastdfsProperties {
/**
* 连接超时时间
*/
private String connectTimeout = "5";
/**
* 网络超时时间
*/
private String networkTimeout = "30";
/**
* 字符集编码
*/
private String charset = "UTF-8";
/**
* 是否使用Token
*/
private String httpAntiStealToken = "false";
/**
* Token加密密钥
*/
private String httpSecretKey = "";
/**
* 跟踪器IP地址,多个使用分号隔开
*/
private String httpTrackerHttpPort = "";
/**
* 连接池的连接对象最大个数
*/
private String trackerServers = "";
/**
* 连接池的最大空闲对象个数
*/
private String connectionPoolMaxTotal = "18";
/**
* 连接池的最小空闲对象个数
*/
private String connectionPoolMaxIdle = "18";
/**
* Nginx服务器IP,多个使用分号分割
*/
private String connectionPoolMinIdle = "2";
/**
* 获取连接对象时可忍受的等待时长(毫秒)
*/
private String nginxServers = "";
}
创建连接对象工厂类,在factory包下创建StorageClientFactory
package com.syiti.dev.component.fastdfs.factory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.csource.fastdfs.StorageClient;
import org.csource.fastdfs.TrackerClient;
import org.csource.fastdfs.TrackerServer;
/**
* 用于创建连接对象的工厂类
* @author LinYoufeng
* @date 2020-01-03 下午4:02
*/
public class StorageClientFactory implements PooledObjectFactory {
@Override
public PooledObject makeObject() throws Exception {
TrackerClient client = new TrackerClient();
TrackerServer server = client.getConnection();
return new DefaultPooledObject<>(new StorageClient(server, null));
}
@Override
public void destroyObject(PooledObject p) throws Exception {
p.getObject().getTrackerServer().close();
}
@Override
public boolean validateObject(PooledObject p) {
return false;
}
@Override
public void activateObject(PooledObject p) throws Exception {
}
@Override
public void passivateObject(PooledObject p) throws Exception {
}
}
创建配置类
package com.syiti.dev.component.fastdfs.autoconfiguration;
import com.syiti.dev.component.fastdfs.properties.FastdfsProperties;
import com.syiti.dev.component.fastdfs.service.FastdfsClientService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author LinYoufeng
* @date 2020-01-03 下午4:02
*/
@Configuration
@EnableConfigurationProperties(FastdfsProperties.class)
public class FastdfsAutoConfiguration {
@Autowired
private FastdfsProperties fastdfsProperties;
@Bean
@ConditionalOnMissingBean(FastdfsClientService.class)
public FastdfsClientService fastdfsClientService() throws Exception {
return new FastdfsClientService(fastdfsProperties);
}
}
然后在service包下创建FastdfsClientService类,包括具体的上传操作
package com.syiti.dev.component.fastdfs.service;
import com.syiti.dev.component.fastdfs.factory.StorageClientFactory;
import com.syiti.dev.component.fastdfs.properties.FastdfsProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.ClientGlobal;
import org.csource.fastdfs.FileInfo;
import org.csource.fastdfs.ProtoCommon;
import org.csource.fastdfs.StorageClient;
import java.util.Arrays;
import java.util.Properties;
import static java.lang.Math.abs;
/**
* @author LinYoufeng
* @date 2020-01-03 下午4:14
*/
@Slf4j
public class FastdfsClientService {
/**
* SpringBoot加载的配置文件
*/
private FastdfsProperties fdfsProp;
/**
* 连接池配置项
*/
private GenericObjectPoolConfig config;
/**
* 转换后的配置条目
*/
private Properties prop;
/**
* 连接池
*/
private GenericObjectPool pool;
/**
* Nginx服务器地址
*/
private String[] nginxServers;
public FastdfsClientService(FastdfsProperties fdfsProp) throws Exception {
this.fdfsProp = fdfsProp;
init();
create();
info();
}
/**
* 初始化全局客户端
*/
private void init() throws Exception {
this.prop = new Properties();
log.info("FastDFS: reading config file...");
log.info("FastDFS: fastdfs.connect_timeout_in_seconds=" + this.fdfsProp.getConnectTimeout());
log.info("FastDFS: fastdfs.network_timeout_in_seconds=" + this.fdfsProp.getNetworkTimeout());
log.info("FastDFS: fastdfs.charset=" + this.fdfsProp.getCharset());
log.info("FastDFS: fastdfs.http_anti_steal_token=" + this.fdfsProp.getHttpAntiStealToken());
log.info("FastDFS: fastdfs.http_secret_key=" + this.fdfsProp.getHttpSecretKey());
log.info("FastDFS: fastdfs.http_tracker_http_port=" + this.fdfsProp.getHttpTrackerHttpPort());
log.info("FastDFS: fastdfs.tracker_servers=" + this.fdfsProp.getTrackerServers());
log.info("FastDFS: fastdfs.connection_pool_max_total=" + this.fdfsProp.getConnectionPoolMaxTotal());
log.info("FastDFS: fastdfs.connection_pool_max_idle=" + this.fdfsProp.getConnectionPoolMaxIdle());
log.info("FastDFS: fastdfs.connection_pool_min_idle=" + this.fdfsProp.getConnectionPoolMinIdle());
log.info("FastDFS: fastdfs.nginx_servers=" + this.fdfsProp.getNginxServers());
this.prop.put("fastdfs.connect_timeout_in_seconds", this.fdfsProp.getConnectTimeout());
this.prop.put("fastdfs.network_timeout_in_seconds", this.fdfsProp.getNetworkTimeout());
this.prop.put("fastdfs.charset", this.fdfsProp.getCharset());
this.prop.put("fastdfs.http_anti_steal_token", this.fdfsProp.getHttpAntiStealToken());
this.prop.put("fastdfs.http_secret_key", this.fdfsProp.getHttpSecretKey());
this.prop.put("fastdfs.http_tracker_http_port", this.fdfsProp.getHttpTrackerHttpPort());
this.prop.put("fastdfs.tracker_servers", this.fdfsProp.getTrackerServers());
ClientGlobal.initByProperties(this.prop);
}
/**
* 显示初始化信息
*/
private void info() {
log.info("FastDFS parameter: ConnectionPoolMaxTotal ==> " + this.pool.getMaxTotal());
log.info("FastDFS parameter: ConnectionPoolMaxIdle ==> " + this.pool.getMaxIdle());
log.info("FastDFS parameter: ConnectionPoolMinIdle ==> " + this.pool.getMinIdle());
log.info("FastDFS parameter: NginxServer ==> " + Arrays.toString(this.nginxServers));
log.info(ClientGlobal.configInfo());
}
/**
* 创建连接池
*/
private void create() {
this.config = new GenericObjectPoolConfig();
log.info("FastDFS Client: Creating connection pool...");
this.config.setMaxTotal(Integer.parseInt(this.fdfsProp.getConnectionPoolMaxTotal()));
this.config.setMaxIdle(Integer.parseInt(this.fdfsProp.getConnectionPoolMaxIdle()));
this.config.setMinIdle(Integer.parseInt(this.fdfsProp.getConnectionPoolMinIdle()));
StorageClientFactory factory = new StorageClientFactory();
this.pool = new GenericObjectPool(factory, this.config);
this.nginxServers = this.fdfsProp.getNginxServers().split(",");
}
/**
* Nginx服务器负载均衡算法
*
* @param servers 服务器地址
* @param address 客户端IP地址
* @return 可用的服务器地址
*/
private String getNginxServer(String[] servers, String address) {
int size = servers.length;
int i = address.hashCode();
int index = abs(i % size);
return servers[index];
}
public String getNginxServer(){
if (this.nginxServers.length > 0){
return "http://"+this.nginxServers[0];
}
return null;
}
/**
* 上传文件,适合上传图片
*
* @param buffer 字节数组
* @param ext 扩展名
* @return 文件组名和ID
*/
public String[] autoUpload(byte[] buffer, String ext) throws Exception {
String[] upload = this.upload(buffer, ext, null);
return upload;
}
/**
* 带有防盗链的下载
*
* @param fileGroup 文件组名
* @param remoteFileName 远程文件名称
* @param clientIpAddress 客户端IP地址
* @return 完整的URL地址
*/
public String autoDownloadWithToken(String fileGroup, String remoteFileName, String clientIpAddress) throws Exception {
int ts = (int) (System.currentTimeMillis() / 1000);
String token = ProtoCommon.getToken(remoteFileName, ts, ClientGlobal.getG_secret_key());
String nginx = this.getNginxServer(this.nginxServers, clientIpAddress);
return "http://" + nginx + "/" + fileGroup + "/" + remoteFileName + "?token=" + token + "&ts=" + ts;
}
/**
* 不带防盗链的下载,如果开启防盗链会导致该方法抛出异常
*
* @param fileGroup 文件组名
* @param remoteFileName 远程文件ID
* @param clientIpAddress 客户端IP地址,根据客户端IP来分配Nginx服务器
* @return 完整的URL地址
*/
public String autoDownloadWithoutToken(String fileGroup, String remoteFileName, String clientIpAddress) throws Exception {
if (ClientGlobal.getG_anti_steal_token()) {
log.error("FastDFS Client: You've turned on Token authentication.");
throw new Exception("You've turned on Token authentication.");
}
String nginx = this.getNginxServer(this.nginxServers, clientIpAddress);
return "http://" + nginx + fileGroup + "/" + remoteFileName;
}
/**
* 通过本地文件上传
*
* @param localFileName 本地文件名称
* @param fileExtName 文件扩展名
* @param metadata 文件的元数据
* @return 文件组名和ID
*/
public String[] upload(String localFileName, String fileExtName, NameValuePair[] metadata) throws Exception {
StorageClient client = this.pool.borrowObject();
final String[] strings = client.upload_file(localFileName, fileExtName, metadata);
this.pool.returnObject(client);
return strings;
}
/**
* 通过字节数组上传
*
* @param fileBuff 文件的字节数组
* @param fileExtName 扩展名
* @param metadata 元数据
* @return 文件组名和ID
*/
public String[] upload(byte[] fileBuff, String fileExtName, NameValuePair[] metadata) throws Exception {
StorageClient client = this.pool.borrowObject();
String[] strings = client.upload_file(fileBuff, fileExtName, metadata);
this.pool.returnObject(client);
return strings;
}
/**
* 通过字节数组上传附加文件
*
* @param buff 字节数组
* @param offset 偏移量
* @param len 长度
* @param fileExtName 扩展名
* @param metadata 元数据
* @return 文件组名和ID
*/
public String[] uploadAppenderFile(byte[] buff, int offset, int len, String fileExtName, NameValuePair[] metadata) throws Exception {
StorageClient client = this.pool.borrowObject();
String[] strings = client.upload_appender_file(buff, offset, len, fileExtName, metadata);
this.pool.returnObject(client);
return strings;
}
/**
* 通过字节数组上传附加文件
*
* @param buff 字节数组
* @param fileExtName 扩展名
* @param metadata 元数据
* @return 文件组名和ID
*/
public String[] uploadAppenderFile(byte[] buff, String fileExtName, NameValuePair[] metadata) throws Exception {
StorageClient client = this.pool.borrowObject();
String[] strings = client.upload_appender_file(buff, fileExtName, metadata);
this.pool.returnObject(client);
return strings;
}
/**
* 追加文件
*
* @param groupName 组名
* @param appenderFileName 追加文件名称
* @param buff 文件字节数组
* @return 返回0表示成功
*/
public int appendFile(String groupName, String appenderFileName, byte[] buff) throws Exception {
StorageClient client = this.pool.borrowObject();
int i = client.append_file(groupName, appenderFileName, buff);
this.pool.returnObject(client);
return i;
}
/**
* 修改文件
*
* @param groupName 组名
* @param appenderFileName 追加文件名称
* @param fileOffset 偏移量
* @param buff 字节数组
* @return 返回0表示成功
*/
public int modify(String groupName, String appenderFileName, long fileOffset, byte[] buff) throws Exception {
StorageClient client = this.pool.borrowObject();
int i = client.modify_file(groupName, appenderFileName, fileOffset, buff);
this.pool.returnObject(client);
return i;
}
/**
* 修改文件
*
* @param groupName 组名
* @param appenderFileName 追加文件名称
* @param fileOffset 偏移量
* @param buff 字节数组
* @param waitTimeMillis 等待时间
* @return 返回0表示成功
*/
public int modify(String groupName, String appenderFileName, long fileOffset, byte[] buff, long waitTimeMillis) throws Exception {
StorageClient client = this.pool.borrowObject(waitTimeMillis);
int i = client.modify_file(groupName, appenderFileName, fileOffset, buff);
this.pool.returnObject(client);
return i;
}
/**
* 删除文件
*
* @param groupName 组名
* @param remoteFileName 远程文件名称
* @return 返回0表示成功
*/
public int delete(String groupName, String remoteFileName) throws Exception {
StorageClient client = this.pool.borrowObject();
int i = client.delete_file(groupName, remoteFileName);
this.pool.returnObject(client);
return i;
}
/**
* 文件分片
*
* @param groupName 组名
* @param appenderName 输出源文件名称
* @return 返回0表示成功
*/
public int truncate(String groupName, String appenderName) throws Exception {
StorageClient client = this.pool.borrowObject();
int i = client.truncate_file(groupName, appenderName);
this.pool.returnObject(client);
return i;
}
/**
* 文件分片
*
* @param truncatedFileSize 分片大小
* @param groupName 组名
* @param appenderName 输出源文件名称
* @return 返回0表示成功
*/
public int truncate(long truncatedFileSize, String groupName, String appenderName) throws Exception {
StorageClient client = this.pool.borrowObject();
int i = client.truncate_file(groupName, appenderName, truncatedFileSize);
this.pool.returnObject(client);
return i;
}
/**
* 下载文件
*
* @param groupName 组名
* @param remoteFileName 远程文件名称
* @return 返回0表示成功
*/
public byte[] download(String groupName, String remoteFileName) throws Exception {
StorageClient client = this.pool.borrowObject();
byte[] bytes = client.download_file(groupName, remoteFileName);
this.pool.returnObject(client);
return bytes;
}
/**
* 下载文件
*
* @param groupName 组名
* @param remoteFileName 远程文件名称
* @param waitTimeMillis 等待时间
* @return 返回0表示成功
*/
public byte[] download(String groupName, String remoteFileName, long waitTimeMillis) throws Exception {
StorageClient client = this.pool.borrowObject(waitTimeMillis);
byte[] bytes = client.download_file(groupName, remoteFileName);
this.pool.returnObject(client);
return bytes;
}
/**
* 下载文件
*
* @param groupName 组名
* @param remoteFileName 远程文件名称
* @param localFileName 本地文件名称
* @return 返回0表示成功
*/
public int download(String groupName, String remoteFileName, String localFileName) throws Exception {
StorageClient client = this.pool.borrowObject();
int i = client.download_file(groupName, remoteFileName, localFileName);
this.pool.returnObject(client);
return i;
}
/**
* 下载文件
*
* @param groupName 组名
* @param remoteFileName 远程文件名称
* @param localFileName 本地文件名称
* @param waitTimeMillis 等待时间
* @return 返回0表示成功
*/
public int download(String groupName, String remoteFileName, String localFileName, long waitTimeMillis) throws Exception {
StorageClient client = this.pool.borrowObject(waitTimeMillis);
int i = client.download_file(groupName, remoteFileName, localFileName);
this.pool.returnObject(client);
return i;
}
/**
* 获取元数据
*
* @param groupName 组名
* @param remoteFileName 远程文件名称
* @return 键值对数组
*/
public NameValuePair[] getMetadata(String groupName, String remoteFileName) throws Exception {
StorageClient client = this.pool.borrowObject();
NameValuePair[] metadata = client.get_metadata(groupName, remoteFileName);
this.pool.returnObject(client);
return metadata;
}
/**
* 获取元数据
*
* @param groupName 组名
* @param remoteFileName 远程文件名称
* @param waitTimeMillis 等待时间
* @return 键值对数组
*/
public NameValuePair[] getMetadata(String groupName, String remoteFileName, long waitTimeMillis) throws Exception {
StorageClient client = this.pool.borrowObject(waitTimeMillis);
NameValuePair[] metadata = client.get_metadata(groupName, remoteFileName);
this.pool.returnObject(client);
return metadata;
}
/**
* 设置元数据
*
* @param groupName 组名
* @param remoteFileName 远程文件名称
* @param metadata 元数据
* @param flag 更新设置:ProtoCommon.STORAGE_SET_METADATA_FLAG_OVERWRITE 表示重写所有; ProtoCommon.STORAGE_SET_METADATA_FLAG_MERGE 表示没有就插入,有就更新
* @return 返回0表示成功
*/
public int setMetadata(String groupName, String remoteFileName, NameValuePair[] metadata, byte flag) throws Exception {
StorageClient client = this.pool.borrowObject();
int i = client.set_metadata(groupName, remoteFileName, metadata, flag);
this.pool.returnObject(client);
return i;
}
/**
* 获取文件信息
*
* @param groupName 组名
* @param remoteFileName 远程文件名称
* @return 返回文件信息
*/
public FileInfo getFileInfo(String groupName, String remoteFileName) throws Exception {
StorageClient client = this.pool.borrowObject();
FileInfo file_info = client.get_file_info(groupName, remoteFileName);
this.pool.returnObject(client);
return file_info;
}
/**
* 获取文件名称
*
* @param groupName 组名
* @param remoteFileName 远程文件名称
* @param waitTimeMillis 等待时长
* @return 返回文件信息
*/
public FileInfo getFileInfo(String groupName, String remoteFileName, long waitTimeMillis) throws Exception {
StorageClient client = this.pool.borrowObject(waitTimeMillis);
FileInfo file_info = client.get_file_info(groupName, remoteFileName);
this.pool.returnObject(client);
return file_info;
}
/**
* 查询文件信息
*
* @param groupName 组名
* @param remoteFileName 远程文件信息
* @return 返回文件信息
*/
public FileInfo queryFileInfo(String groupName, String remoteFileName) throws Exception {
StorageClient client = this.pool.borrowObject();
FileInfo fileInfo = client.query_file_info(groupName, remoteFileName);
this.pool.returnObject(client);
return fileInfo;
}
/**
* 查询文件信息
*
* @param groupName 组名
* @param remoteFileName 远程文件信息
* @param waitTimeMillis 等待时间
* @return 返回文件信息
*/
public FileInfo queryFileInfo(String groupName, String remoteFileName, long waitTimeMillis) throws Exception {
StorageClient client = this.pool.borrowObject(waitTimeMillis);
FileInfo fileInfo = client.query_file_info(groupName, remoteFileName);
this.pool.returnObject(client);
return fileInfo;
}
}
最后通过创建注解类
package com.syiti.dev.component.fastdfs.annotation;
import com.syiti.dev.component.fastdfs.autoconfiguration.FastdfsAutoConfiguration;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
/**
* @author LinYoufeng
* @date 2020-01-03 下午4:01
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(FastdfsAutoConfiguration.class)
@Documented
public @interface EnableFastdfsClient {
}
在源码上包装后,使用这个组件说明:
FastDFS 组件使用说明:
使用FastDFS 组件分以下四步:
1.加依赖:
com.syiti.dev
component-fastdfs
2.0.0
2.在启动类上加入注解
@EnableDiscoveryClient
@EnableFastdfsClient
3.写配置文件 application.yml
fastdfs:
charset: UTF-8
connect-timeout: 5
network-timeout: 30
http-tracker-http-port: 1080
connection-pool-max-idle: 20
connection-pool-max-total: 20
connection-pool-min-idle: 2
nginx-servers: 192.168.0.192:1080
tracker-servers: 192.168.0.192:22122
4.在需要使用FastDFS 的具体业务实现类上加入 Service
@Autowired
private FastdfsClientService fastdfsClientService;
完成以上四步即可直接使用相关的上传、删除等功能。
fastdfsClientService.autoUpload(xx,xx)
…