一.简介:
今天给大家讲解下FastDfs的使用,主要还是使用java语言如何进行操作,同时也会讲解下FasdDfs环境
的安装,能让大家使用的时候更熟练一些;
一般情况下,对于一个新的技术名称,小编一般都是以3W法则的思路进行思考和解决困惑,也就是what
,why,how,下面就按照这个思路大致简单的给大家介绍下FastDfs:
1.what?(FastDfs是什么?是干什么用的?)
FastDFS是一个开源的高性能,轻量级的分布式文件系统,由淘宝开发平台部资深架构师余庆开发(国内
开源软件),它的主要功能包括:文件存储,文件同步和文件访问(文件上传和文件下载),它可以解
决高容量和负载平衡问题。FastDFS应该满足基于照片共享站点和视频共享站点等文件的网站的要求。
上面文件描述摘自github上fastdfs的项目描述,我们大致可以知道它是个文件系统,关于对该系统存储
单文件的大小讨论,我看网上建议适合文件大小500M以下的存储,具体实际情况小编也没有进行过性能
测试,所以根据上面官方的描述以及网上的建议:FasfDfs是是一个适合存储图片,小文件,小音频的文
件系统。
2.why?(为什么用FastDfs?)
当然,为什么用,主要看是看自己的场景,类似文件存储系统的有很多:GFS、HDFS、Lustre 、
Ceph 、GridFS 、mogileFS、TFS、FastDFS等,各自适用于不同的领域。它们都不是系统级的分布式文
件系统,而是应用级的分布式文件存 储服务。
下面是分布式文件存储选型比较:
而且上面已经提到FastDfs是开源,轻量级,高性能,分布式的。
3.how?(怎么使用FasfDfs呢?)
上面已经对FastDfs有了大概的了解,下面两章会讲解FastDfs的安装(目前FastDfs只支持linux系
统,对windows不支持),以及用java如何进行文件存储和下载。
相关地址:FastDfs的github地址
二.FastDfs环境安装:
上面链接是跳转到FastDfs的wiki,里面了安装的教程,小编在安装的过程中有一些疑惑,不知道大家
在安装的时候有没有?下面就一块说下。
1.安装的时候需要安装nginx,那nginx和fastDfs是什么关系呢?
前面说fastDfs是一个分布式系统,上传的文件会进行复制备份:
FastDFS通过Tracker服务器,将文件放在Storage服务器存储,但是同组之间的服务器需要复制文件,
有延迟的问题.假设Tracker服务器将文件上传到了A服务器,文件ID已经返回客户端,这时,后台会将
这个文件复制到B服务器,如果复制没有完成,客户端就用这个ID在B服务器取文件,肯定会出现错误。
这个fastdfs-nginx-module可以重定向连接到源服务器取文件,避免客户端由于复制延迟的问题,
出现错误。
三.java操作fasfDfs:
1.配置文件:
如下是我的配置文件配置:
我的配置文件是以fdfs_client.conf配置,官方文档说以.properties进行配置,详细见 fastdfs-client-java官方文档
connect_timeout = 2
network_timeout = 30
charset = UTF-8
http.tracker_http_port = 8080
#fastDfs的权限控制,服务端在http.conf配置
http.anti_steal_token = no
http.secret_key = FastDFS1234567890
tracker_server = 127.0.0.1:22122
connection_pool.enabled = true
connection_pool.max_count_per_entry = 500
connection_pool.max_idle_time = 3600
connection_pool.max_wait_time_in_ms = 1000
所以我的读取配置文件的路径如下图所示:
2.工具类代码:
如下代码包含了读取配置文件,文件的上传,下载,删除以及查询等操作;
package com.ufgov.ar.modules.file.util;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.*;
import java.io.BufferedOutputStream;
import java.io.IOException;
/**
* @ProjectName: ar-server
* @Package: com.ufgov.ar.modules.file.util
* @ClassName: FastDfsUtil
* @Author: tianmengwei
* @Description: FastDFS工具类【实现文件上传、下载、删除、查询】
* @Date: 2019/12/28 11:04
*/
public class FastDfsClientHelper {
private static TrackerServer trackerServer;
private static StorageServer storageServer;
// 初始化FastDFS Client
static {
try {
ClientGlobal.initByProperties("src/main/resources/application.properties");
System.out.println("ClientGlobal.configInfo(): " + ClientGlobal.configInfo());
TrackerClient trackerClient = new TrackerClient(ClientGlobal.g_tracker_group);
trackerServer = trackerClient.getConnection();
if (trackerServer == null) {
throw new IllegalStateException("getConnection return null");
}
storageServer = trackerClient.getStoreStorage(trackerServer);
if (storageServer == null) {
throw new IllegalStateException("getStoreStorage return null");
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @author tianmengwei
* @description 获取StorageClient1对象:
* 此处不能将该代码放在静态块的原因:
* 参考该博客;https://mp.weixin.qq.com/s/EFsK3yOtb7maDNqgaHjQow?
* 在并发场景下,线程一在使用完StorageClient1置为null时,此时线程二在继续使用线程一初始化的对象的话,会报空指针;
* @date 2020/2/24 11:12
*/
private static StorageClient1 getStorageClient1() {
try {
StorageClient1 storageClient1 = new StorageClient1(trackerServer, storageServer);
return storageClient1;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* @param fileName : 文件全路径
* @param extName : 文件扩展名,不包含(.)
* @param metas : 文件扩展信息
* @author tianmengwei
* @description 本地上传文件
* @date 2019/12/28 11:36
*/
public static String uploadFileByFastDfs(String fileName, String extName, NameValuePair[] metas) {
String result = null;
try {
StorageClient1 storageClient1 = FastDfsClientHelper.getStorageClient1();
if (storageClient1 == null) {
return null;
}
result = storageClient1.upload_file1(fileName, extName, metas);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 上传文件,传fileName
*
* @param fileName 文件的磁盘路径名称 如:D:/image/aaa.jpg
* @return null为失败
*/
public static String uploadFileByFastDfs(String fileName) {
return uploadFileByFastDfs(fileName, null, null);
}
/**
* @param fileName 文件的磁盘路径名称 如:D:/image/aaa.jpg
* @param extName 文件的扩展名 如 txt jpg等
* @return null为失败
*/
public static String uploadFileByFastDfs(String fileName, String extName) {
return uploadFileByFastDfs(fileName, extName, null);
}
/**
* @param fileContent : 文件的内容,字节数组
* @param extName : 文件扩展名
* @param metas : 文件扩展信息
* @author tianmengwei
* @description 上传文件
* @date 2019/12/28 11:33
*/
public static String uploadFileByFastDfs(byte[] fileContent, String extName, NameValuePair[] metas) {
String result = null;
try {
StorageClient1 storageClient1 = FastDfsClientHelper.getStorageClient1();
if (storageClient1 == null) {
return null;
}
result = storageClient1.upload_file1(fileContent, extName, metas);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* @param fileContent : 文件的字节数组
* @author tianmengwei
* @description 上传文件
* @date 2019/12/28 11:32
*/
public static String uploadFileByFastDfs(byte[] fileContent) {
return uploadFileByFastDfs(fileContent, null, null);
}
/**
* @param fileContent : 文件的字节数组
* @param extName : 文件的扩展名 如 txt jpg png 等
* @author tianmengwei
* @description 上传文件
* @date 2019/12/28 11:32
*/
public static String uploadFileByFastDfs(byte[] fileContent, String extName) {
return uploadFileByFastDfs(fileContent, extName, null);
}
/**
* @param path : 图片路径
* @param output : 输出流 中包含要输出到磁盘的路径
* @author tianmengwei
* @description 文件下载到磁盘 返回结果:-1失败,0成功
* @date 2019/12/28 11:30
*/
public static int downloadFileByFastDfs(String path, BufferedOutputStream output) {
int result = -1;
try {
StorageClient1 storageClient1 = FastDfsClientHelper.getStorageClient1();
if (storageClient1 == null) {
return -1;
}
path = path.replaceAll("\\\\", "/");
byte[] b = storageClient1.download_file1(path);
try {
if (b != null) {
output.write(b);
result = 0;
}
//用户可能取消了下载
} catch (Exception e) {
e.printStackTrace();
} finally {
if (output != null) {
try {
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* @param path : 文件的路径 如group1/M00/00/00/wKgRsVjtwpSAXGwkAAAweEAzRjw471.jpg
* @author tianmengwei
* @description path前端会将FastDfs生成的/转换为\,此处FastDfs解析不到信息导致找不到文件路径
* @date 2019/12/28 11:20
*/
public static byte[] downLoadBytesByFastDfs(String path) {
byte[] b = null;
try {
StorageClient1 storageClient1 = FastDfsClientHelper.getStorageClient1();
path = path.replaceAll("\\\\", "/");
b = storageClient1.download_file1(path);
} catch (Exception e) {
e.printStackTrace();
}
return b;
}
/**
* @param serverPath : 文件在服务器的路径
* @param localPath : 下载的本地路径
* @author tianmengwei
* @description 下载文件到本地指定的路径
* @date 2020/2/20 23:48
*/
public static void downLoadLocalByFastDfs(String serverPath, String localPath) {
try {
StorageClient1 storageClient1 = FastDfsClientHelper.getStorageClient1();
if (storageClient1 == null) {
return;
}
serverPath = serverPath.replaceAll("\\\\", "/");
storageClient1.download_file1(serverPath, localPath);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @param group : 组名 如:group1
* @param storagePath : 不带组名的路径名称 如:M00/00/00/wKgRsVjtwpSAXGwkAAAweEAzRjw471.jpg
* @author tianmengwei
* @description 返回结果:-1失败,0成功
* @date 2019/12/28 11:18
*/
public static Integer deleteFileByFastDfs(String group, String storagePath) {
int result = -1;
try {
StorageClient1 storageClient1 = FastDfsClientHelper.getStorageClient1();
if (storageClient1 == null) {
return -1;
}
storagePath = storagePath.replaceAll("\\\\", "/");
result = storageClient1.delete_file(group, storagePath);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* @param storagePath : 文件的全部路径 如:group1/M00/00/00/wKgRsVjtwpSAXGwkAAAweEAzRjw471.jpg
* @author tianmengwei
* @description 返回结果:-1失败,0成功
* @date 2019/12/28 11:12
*/
public static Integer deleteFileByFastDfs(String storagePath) {
int result = -1;
try {
StorageClient1 storageClient1 = FastDfsClientHelper.getStorageClient1();
if (storageClient1 == null) {
return null;
}
storagePath = storagePath.replaceAll("\\\\", "/");
result = storageClient1.delete_file1(storagePath);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* @param fileId : M00/00/00/wKgRsVjtwpSAXGwkAAAweEAzRjw471.jpg
* @author tianmengwei
* @description 获取远程服务器文件资源信息
* @date 2019/12/28 11:08
*/
public static FileInfo getFileInfoByFastDfs(String fileId) {
try {
StorageClient1 storageClient1 = FastDfsClientHelper.getStorageClient1();
if (storageClient1 == null) {
return null;
}
fileId = fileId.replaceAll("\\\\", "/");
return storageClient1.get_file_info1(fileId);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* @param fileId : M00/00/00/wKgRsVjtwpSAXGwkAAAweEAzRjw471.jpg
* @author tianmengwei
* @description 获取远程服务器文件资源信息
* @date 2019/12/28 11:08
*/
public static NameValuePair[] getNameValuePairByFastDfs(String fileId) {
try {
StorageClient1 storageClient1 = FastDfsClientHelper.getStorageClient1();
if (storageClient1 == null) {
return null;
}
fileId = fileId.replaceAll("\\\\", "/");
return storageClient1.get_metadata1(fileId);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
3.web的使用:
如下代码是上传,下载,查询原数据的操作代码:
@ApiOperation(value = "上传单个普通文件", notes = "上传单个普通文件")
@RequestMapping(value = {"/uploadNormalFile"}, method = RequestMethod.POST)
public ArRes uploadNormalFileByFastDfs(@RequestParam(value = "file", required = false) MultipartFile file) throws Exception {
if (file.isEmpty()) {
return ArRes.error("上传附件为空");
}
String fileType = arBillFileHelper.getFileType(file.getOriginalFilename());
NameValuePair nameValuePairs [] = new NameValuePair[]{
new NameValuePair("fileType", fileType),
new NameValuePair("fileContent", "11111111")};
String result = FastDfsClientHelper.uploadFileByFastDfs(file.getBytes(), "doc", nameValuePairs);
return ArRes.ok().putData(result);
}
上传成功后会返回文件的fileId,可以根据该id进行下载或者进行元数据的查询;
@ApiOperation(value = "下载文件", notes = "根据fileId传出文件")
@RequestMapping(value = {"/downloadNormalFile"}, method = RequestMethod.GET)
public void downloadNormalFile(HttpServletRequest request, HttpServletResponse response, @RequestParam(value = "fileId") String fileId ) throws IOException {
try {
FastDfsClientHelper.downloadFileByFastDfs(fileId,new BufferedOutputStream(response.getOutputStream()));
} catch (ArBillException e) {
response.setContentType("text/html; charset=UTF-8");
response.getWriter().write("文件丢失");
} catch (Exception e) {
logger.error("downloadNormalFile failed error:" + e.getMessage(), e);
response.setContentType("text/html; charset=UTF-8");
response.getWriter().write("文件下载异常");
}
}
FastDfs提供了FileInfo实体类,该实体类存储了上传文件的源数据,包含服务器地址,时间等等:
同时也提供了NameValuePair实体类,可以存储我们想要存储的一些业务信息;
四.结尾:
至此,大家应该对FastDfs大致有了一些了解,并且能简单进行使用了,大家在使用过程中遇到什么问题
欢迎能和小编一起探讨。