1.查找Docker Hub上的redis镜像
docker search fastdfs
2.拉取镜像
docker pull delron/fastdfs #拉取最新版本
4.使用docker镜像构建tracker容器(跟踪服务器,起到调度的作用):
docker run -dti --network=host --name tracker -v /var/fdfs/tracker:/var/fdfs -v /etc/localtime:/etc/localtime delron/fastdfs tracker
5.使用docker镜像构建storage容器(存储服务器,提供容量和备份服务):
docker run -dti --network=host --name storage -e TRACKER_SERVER=192.168.56.1:22122 -v /var/fdfs/storage:/var/fdfs -v /etc/localtime:/etc/localtime delron/fastdfs storage
注意:TRACKER_SERVER=外网的ip地址:22122 本机ip地址不要使用127.0.0.1
注意:
一. 应用系统在上传文件到FastDFS成功时将原始文件名和“文件索引(FID)”保存下来(例如:保存到数据库)。
二. 用户点击下载的时用Nginx的域名和FID拼出url,然后在url后面增加一个参数,指定原始文件名。例如:
http://192.168.6.124:8888/group1/M00/00/00/wKgGe15Ut3CAZcnmAA98T7dRZ0c.tar.gz?attname=shooter.tar.gz
三. 在Nginx上进行如下配置,这样Nginx就会截获url中的参数attname,在Http响应头里面加上字段 Content-Disposition "attachment;filename=$arg_attname"
server {
listen 8888;
server_name localhost;
location ~/group([0-9])/M00 {
alias /data-fdfs/storage/data;
add_header Content-Disposition "attachment;filename=$arg_attname";
ngx_fastdfs_module;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
access_log /usr/local/nginx/logs/access.log main;
}
进入storage容器,到storage的配置文件中配置http访问的端口,配置文件在/etc/fdfs目录下的storage.conf。
6.测试
/usr/bin/fdfs_upload_file /etc/fdfs/client.conf test.png
<dependency>
<groupId>com.roncoo</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.29</version>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<dependency>
<groupId>org.csource</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.27-RELEASE</version>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
</exclusions>
</dependency>
import lombok.Data;
@Data
public class FastDFSFile {
/**
* 文件名字
*/
private String name;
/**
* 文件内容
*/
private byte[] content;
/**
* 文件扩展名
*/
private String ext;
/**
* 文件MD5摘要值
*/
private String md5;
/**
* 文件创建作者
*/
private String author;
/**
* 文件长度
*/
private Long size;
public FastDFSFile(String name, byte[] content, String ext, String height, String width, String author) {
super();
this.name = name;
this.content = content;
this.ext = ext;
this.author = author;
}
public FastDFSFile(String name, byte[] content, String ext) {
super();
this.name = name;
this.content = content;
this.ext = ext;
}
public FastDFSFile(byte[] content, String name, Long size){
this.content = content;
this.name = name;
this.size = size;
}
}
fastdfs客户端
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* 上传对象存储数据 访问8888
*/
@Order(100)
@Slf4j
@Configuration
public class FastDfsClient implements ApplicationListener<ApplicationReadyEvent> {
private static int dfsTrackerPoint;
private static String dfsTrackerServer;
private static int dfsConnectTimeout;
private static int dfsNetworkTimeout;
/**
* 上传文件
* @param file 文件对象
* @param fileName 文件名
* @return
*/
public String uploadFile(File file, String fileName) {
try {
FileInputStream fis = new FileInputStream(file);
byte[] fileBuff = null;
if (fis != null) {
int len = fis.available();
fileBuff = new byte[len];
fis.read(fileBuff);
}
FastDFSFile fastDFSFile = new FastDFSFile(fileName, fileBuff, FilenameUtils.getExtension(file.getName()));
fis.close();
String uploadFile = uploadFile(fastDFSFile);
if(uploadFile != null){
return uploadFile.concat("?").concat("attname=").concat(fileName);
}
}catch (Exception e){
log.error("上传文件失败:{}",e);
}
return null;
}
/**
* 下载文件
* @param fileName
* @return
*/
public byte[] downloadFile(String fileName) {
try {
return getStorageClient1().download_file1(fileName);
} catch (Exception e) {
log.error("下载文件失败:{}", e);
}
return null;
}
/**
* 下载文件
* @param groupName
* @param remoteFileName
* @return
*/
public static InputStream downFile(String groupName, String remoteFileName) {
try {
StorageClient storageClient = getStorageClient();
byte[] fileByte = storageClient.download_file(groupName, remoteFileName);
return new ByteArrayInputStream(fileByte);
} catch (Exception e) {
log.error("下载文件失败:{}",e);
}
return null;
}
/**
* 获取文件元数据
* @param fileId 文件ID
* @return
*/
public Map<String,String> getFileMetadata(String fileId) {
try {
NameValuePair[] metaList = getStorageClient1().get_metadata1(fileId);
if (metaList != null) {
HashMap<String,String> map = new HashMap<>();
for (NameValuePair metaItem : metaList) {
map.put(metaItem.getName(), metaItem.getValue());
}
return map;
}
} catch (Exception e) {
log.error("获取文件元数据失败:{}", e);
}
return null;
}
/**
* 删除文件
* @param fileId 文件ID
* @return 删除失败返回-1,否则返回0
*/
public int deleteFile(String fileId) {
try {
return getStorageClient1().delete_file1(fileId);
} catch (Exception e) {
log.error("删除文件失败:{}", e);
}
return -1;
}
/**
* 下载文件
* @param fileId 文件ID(上传文件成功后返回的ID)
* @param outFile 文件下载保存位置
* @return
*/
public int downloadFile(String fileId, File outFile) {
FileOutputStream fileOutputStream = null;
ByteArrayInputStream inputStream = null;
try {
byte[] content = getStorageClient1().download_file1(fileId);
inputStream = new ByteArrayInputStream(content, 1, content.length);
fileOutputStream = new FileOutputStream(outFile);
IOUtils.copy(inputStream, fileOutputStream);
return 0;
} catch (Exception e) {
log.error("下载文件失败:{}", e);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
log.error("下载文件完毕后, 关闭输入流失败:{}", e);
}
}
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
log.error("下载文件完毕后, 关闭输出流失败:{}", e);
}
}
}
return -1;
}
/**
* 上传文件方法
* Title: uploadFile
* Description:
* @param fileName 文件全路径
* @param extName 文件扩展名,不包含(.)
* @param metas 文件扩展信息
* @return
* @throws Exception
*/
public String uploadFile(String fileName, String extName, NameValuePair[] metas) throws Exception {
return getStorageClient1().upload_file1(fileName, extName, metas);
}
public String uploadFile(String fileName) throws Exception {
return uploadFile(fileName, null, null);
}
public String uploadFile(String fileName, String extName) throws Exception {
return uploadFile(fileName, extName, null);
}
/**
* 上传文件方法
* @param fileContent 文件的内容,字节数组
* @param extName 文件扩展名
* @param metas 文件扩展信息
* @return
* @throws Exception
*/
public String uploadFile(byte[] fileContent, String extName, NameValuePair[] metas){
try {
return getTrackerUrl().concat(getStorageClient1().upload_file1(fileContent, extName, metas));
} catch (Exception e) {
log.error("上传文件失败:{}", e);
}
return null;
}
public String uploadFile(byte[] fileContent, String fileName){
String uploadFile = uploadFile(fileContent, null, null);
if(uploadFile != null){
return uploadFile.concat("?").concat("attname=").concat(fileName);
}
return null;
}
public String uploadExtName(byte[] fileContent, String extName){
return uploadFile(fileContent, extName, null);
}
/**
* 生成可以访问的url
* @param byteFile
* @param extName
* @param fileName
* @return
*/
public String uploadFileByte(byte[] byteFile, String extName, String fileName){
NameValuePair[] naps = new NameValuePair[1];
naps[0] = new NameValuePair(fileName, extName);
try {
return getTrackerUrl().concat(getStorageClient1().upload_file1(byteFile, extName, naps));
} catch (Exception e) {
log.error("上传文件失败:{}", e);
}
return null;
}
/**
* 不生成可以访问的url
* @param byteFile
* @param extName
* @param fileName
* @return
*/
public String uploadFileByteKey(byte[] byteFile, String extName, String fileName){
NameValuePair[] naps = new NameValuePair[1];
naps[0] = new NameValuePair(fileName, extName);
try {
return getStorageClient1().upload_file1(byteFile, extName, naps);
} catch (Exception e) {
log.error("上传文件失败:{}", e);
}
return null;
}
/**
* 下载文件
* @param groupName
* @param fileName
* @return 二进制流
*/
public byte[] downloadBytes(String groupName, String fileName) {
try {
return getStorageClient1().download_file(groupName, fileName);
} catch (Exception e) {
log.error("下载文件失败:{}", e);
}
return null;
}
/**
* 文件下载
* @param filePath 文件地址
* @param savePath 本地保存地址
*/
public void download(String filePath, String savePath){
try {
byte[] bytes = getStorageClient1().download_file1(filePath);
IOUtils.write(bytes,new FileOutputStream(savePath));
} catch (Exception e) {
log.error("文件下载失败:{}",e);
}
}
/**
* 获取文件信息
* 文件IP地址
* String sourceIpAddr = fileInfo.getSourceIpAddr();
* 文件大小
* long fileSize = fileInfo.getFileSize();
* 文件创建时间
* Date createTimestamp = fileInfo.getCreateTimestamp();
* 错误码
* long crc32 = fileInfo.getCrc32();
* @param filePath 文件的地址
* @return
*/
public FileInfo getFileInfo(String filePath){
try {
return getStorageClient1().get_file_info1(filePath);
} catch (Exception e) {
log.info("获取文件信息失败:{}",e);
}
return null;
}
/**
* 上传文件
* @param file
* @return
*/
public String uploadFile(FastDFSFile file){
NameValuePair[] nameValuePairs = new NameValuePair[1];
nameValuePairs[0] = new NameValuePair(file.getAuthor());
try {
return getTrackerUrl().concat(getStorageClient1().upload_file1(file.getContent(), file.getExt(),nameValuePairs));
} catch (Exception e) {
log.error("upload file is fail: {} ",e.getMessage());
}
return null;
}
/**
* 下载文件
* @param path
* @return 文件对象
*/
public InputStream downFile(String path){
try {
byte[] bytes = getStorageClient1().download_file1(path);
return new ByteArrayInputStream(bytes);
} catch (Exception e) {
log.error("download file is fail : {}", e.getMessage());
}
return null;
}
/**
* 删除文件
* @param path
*/
public void deleteFilePath(String path){
try {
getStorageClient1().delete_file1(path);
} catch (Exception e) {
log.error("delete file is fail : {}",e.getMessage());
}
}
/**
* fastDFS文件上传 生成可以访问的url
*
* @param file 上传的文件 FastDFSFile
* @return String 返回文件的绝对路径
*/
public String uploadFastDFSFile(FastDFSFile file) {
try {
//文件扩展名
String ext = FilenameUtils.getExtension(file.getName());
//mata list是表文件的描述
NameValuePair[] mata_list = new NameValuePair[3];
mata_list[0] = new NameValuePair("fileName", file.getName());
mata_list[1] = new NameValuePair("fileExt", ext);
mata_list[2] = new NameValuePair("fileSize", String.valueOf(file.getSize()));
String url = getStorageClient1().upload_file1(file.getContent(), ext, mata_list);
return getTrackerUrl().concat(url);
} catch (Exception e) {
log.error("上传文件失败:{}",e);
}
return null;
}
/**
* 不生成 可以访问的url
* @param file
* @return
*/
public String uploadFastDFSFileKey(FastDFSFile file) {
try {
//文件扩展名
String ext = FilenameUtils.getExtension(file.getName());
//mata list是表文件的描述
NameValuePair[] mata_list = new NameValuePair[3];
mata_list[0] = new NameValuePair("fileName", file.getName());
mata_list[1] = new NameValuePair("fileExt", ext);
mata_list[2] = new NameValuePair("fileSize", String.valueOf(file.getSize()));
String url = getStorageClient1().upload_file1(file.getContent(), ext, mata_list);
return getTrackerUrl().concat(url);
} catch (Exception e) {
log.error("上传文件失败:{}",e);
}
return null;
}
@Value("${xxx.dfsTrackerPoint}")
public void setDfsTrackerPoint(int dfsTrackerPoint) {
FastDfsClient.dfsTrackerPoint = dfsTrackerPoint;
}
@Value("${xxx.dfsTrackerServer}")
public void setDfsTrackerServer(String dfsTrackerServer) {
FastDfsClient.dfsTrackerServer = dfsTrackerServer;
}
@Value("${xxx.dfsConnectTimeout}")
public void setDfsConnectTimeout(int dfsConnectTimeout) {
FastDfsClient.dfsConnectTimeout = dfsConnectTimeout;
}
@Value("${xxx.dfsNetworkTimeout}")
public void setDfsNetworkTimeout(int dfsNetworkTimeout) {
FastDfsClient.dfsNetworkTimeout = dfsNetworkTimeout;
}
public static StorageServer[] getStoreStorages(String groupName) throws IOException {
TrackerClient trackerClient = new TrackerClient();
TrackerServer trackerServer = trackerClient.getConnection();
return trackerClient.getStoreStorages(trackerServer, groupName);
}
public static ServerInfo[] getFetchStorages(String groupName, String remoteFileName) throws IOException {
TrackerClient trackerClient = new TrackerClient();
TrackerServer trackerServer = trackerClient.getConnection();
return trackerClient.getFetchStorages(trackerServer, groupName, remoteFileName);
}
public static String getTrackerUrl() throws IOException {
return "http://" + getTrackerServer().getInetSocketAddress().getHostString().concat(":").concat("8888").concat("/");
}
private static StorageClient getStorageClient() throws IOException {
TrackerServer trackerServer = getTrackerServer();
StorageClient storageClient = new StorageClient(trackerServer, null);
// 连接FastDFS集群,避免出现 java.io.IOException: recv package size -1 != 10 防止30秒进行重连
//ProtoCommon.activeTest(trackerServer.getSocket());
return storageClient;
}
private static TrackerClient getTrackerClient() {
TrackerClient trackerClient = new TrackerClient();
return trackerClient;
}
private static TrackerServer getTrackerServer() throws IOException {
TrackerClient trackerClient = getTrackerClient();
TrackerServer trackerServer = trackerClient.getConnection();
return trackerServer;
}
private static StorageClient1 getStorageClient1() throws IOException {
TrackerClient trackerClient = getTrackerClient();
TrackerServer trackerServer = trackerClient.getConnection();
StorageServer storageServer = trackerClient.getStoreStorage(trackerServer);
StorageClient1 storageClient1 = new StorageClient1(trackerServer, storageServer);
// 连接FastDFS集群,避免出现 java.io.IOException: recv package size -1 != 10 防止30秒进行重连
//ProtoCommon.activeTest(trackerServer.getSocket());
return storageClient1;
}
@Override
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
try {
Properties props = new Properties();
props.put(ClientGlobal.PROP_KEY_TRACKER_SERVERS, dfsTrackerServer == null ? "192.168.21.1:22122" : dfsTrackerServer);
props.put(ClientGlobal.PROP_KEY_HTTP_TRACKER_HTTP_PORT, dfsTrackerPoint == 0 ? 22122 : dfsTrackerPoint);
props.put(ClientGlobal.PROP_KEY_CHARSET, "UTF-8");
props.put(ClientGlobal.PROP_KEY_HTTP_SECRET_KEY, "FastDFS1234567890");
props.put(ClientGlobal.PROP_KEY_HTTP_ANTI_STEAL_TOKEN, false);
//加载连接信息
ClientGlobal.initByProperties(props);
}catch (Exception e){
log.error("初始化配置失败:{}", e);
}
}
}