FastDFS安装必须使用make、cmake和gcc编译器
yum install -y make cmake gcc gcc-c++
unzip libfastcommon-1.0.43.zip -d /usr/local/fastdfs
解压目录下找到make.sh文件
编译
./make.sh
安装
./make.sh install
有固定的默认安装位置。在/usr/lib64 和/usr/include/fastcommon 两个目录中。
因为FastDFS主程序设置的lib目录是/usr/local/lib,所以需要创建软链接
ln -s /usr/lib64/libfastcommon.so /usr/local/lib/libfastcommon.so
ln -s /usr/local/lib64/libfdfsclient.so /usr/local/lib/libfdfsclient.so
ln -s /usr/local/lib64/libfdfsclient.so /usr/lib/libfdfsclient.so
tar -zxf fastdfs-6.06.tar.gz -C /usr/local/fastdfs
在解压目录找到 make.sh文件 ,修改安装路径(可选择,自定义):
vi make.sh
TARGET_PREFIX=$DESTDIR/usr -> TARGET_PREFIX=$DESTDIR/usr/local
./make.sh
./make.sh install
安装后,FastDFS主程序所在的位置是:
/usr/local/bin 可执行文件所在位置。默认安装在/usr/bin 中。
/etc/fdfs 配置文件所在位置。就是默认位置。
/usr/local/lib64 主程序代码所在位置。默认在/usr/bin 中。
/usr/local/include/fastdfs 包含的一些插件组所在位置。默认在/usr/include/fastdfs 中。
服务脚本
/etc/init.d/目录中,脚本文件是 fdfs-storaged 和 fdfs-trackerd
配置文件模板
/etc/fdfs/目录中,配置文件是 client.conf.sample 、 storage.conf.sample 和 tracker.conf.sample
tracker.conf.sample 跟踪器服务配置文件模板
storage.conf.sample 存储服务器配置文件模板
client.conf.sample FastDFS 提供的命令行客户端配置文件模板。可以通过命令行测试FastDFS有效性。
内置命令
2.3.2修改的目录中。命令有若干。可通过命令在控制台访问 FastDFS。
FastDFS 提供了配置文件模板,可以根据模板创建需要使用的配置文件。
cd /etc/fdfs
cp tracker.conf.sample tracker.conf
tracker.conf 配置文件用于描述跟踪服务的行为,需要进行下述修改:
vi tracker.conf
port=22122 # 默认服务端口
base_path=/home/yuqing/fastdfs --> base_path=/xxx/xxx/tracker (自定义目录,目录要存在,如果不存在,自己手动创建一个mkdir -p age/xxx/xxx/tracker)
base_path是FastDFSTracker启动后使用的根目录。就是data和logs所在的位置
修改:fdfs_trackerd
vi /etc/init.d/fdfs_trackerd
#将 PRG=/usr/bin/fdfs_trackerd 修改为 PRG=/usr/local/bin/fdfs_trackerd
启动服务:启动成功后,配置文件中base_path指向的目录中出现 FastDFS服务相关数据目录( data目录、logs 目录)
./fdfs_trackerd start # 启动服务
ps aux | frep fdfs # 查看服务状态
./fdfs_trackerd stop # 停止服务
./fdfs_trackerd restart # 重启服务
vi /etc/rc.d/rc.local 新增内容 - /etc/init.d/fdfs_trackerd start # 设置开机自启
放开22122端口 ,或者关闭防火墙
创建配置文件
FastDFS提供了配置文件模板,可以根据模板创建需要使用的配置文件。
cd /etc/fdfs
cp storage.conf.sample storage.conf
修改配置文件
storage.conf 配置文件用于描述存储服务的行为,需要进行下述修改:
cd /etc/fdfs/
vi storage.conf
base_path=/home/yuqing/fastdfs -> base_path=/xxxx/storage/base(自定义目录,必须存在)
store_path0=/home/yuqing/fastdfs -> store_path0=/xxxx/storage/store (自定义目录,必须存在)
tracker_server=192.168.2.109:22122 -> tracker_server=tracker 服务 IP:22122
要求tracker服务必须已启动
cd /etx/init.d
vi fdfs_storaged
将 PRG=/usr/bin/fdfs_storaged 修改为 PRG=/usr/local/bin/fdfs_storaged
./fdfs_storaged start # 启动服务
ps aux | frep fdfs # 查看服务状态
./fdfs_storaged stop # 停止服务
./fdfs_storaged restart # 重启服务
vi /etc/rc.d/rc.local 新增内容 - /etc/init.d/fdfs_storaged start # 设置开机自启 不推荐,因为stirage启动前提为tracker服务必须已启动
启动成功后,配置文件中 base_path 指向的目录中出现 FastDFS 服务相关数据目录(data目录、logs 目录),
配置文件中的 store_path0 指向的目录中同样出现 FastDFS 存储相关数据目录(data 目录)。其中
$store_path0/data/ 目录中默认创建若干子孙目录(两级目录层级总计256256 个目录),是用于存储具体文件
数据的。
Storage 服务器启动比较慢,因为第一次启动的时候,需要创建 256256 个目录。
ln -s /自定义安装目录/lib64/libfdfsclient.so /usr/local/lib/libfdfsclient.so
ln -s /自定义安装目录/lib64/libfdfsclient.so /usr/lib/libfdfsclient.so
不是必须的。就是用于使用命令行测试 FastDFS 才需要配置的。
在tracker服务结点所在服务器中配置客户端。同样通过配置文件模板创建对应配置文件
cd /ect/fdfs/
cp client.conf.sample client.conf
client.conf 配置文件中主要描述客户端的行为,需要进行下述修改:
cd /ect/fdfs/
vi client.conf
base_path=/home/yuqing/fastdfs -> base_path=/xxx/client (自定义目录。必须存在)
tracker_server=192.168.2.109:22122 -> tracker_server=tracker 服务 IP:22122
base_path 就是客户端命令行执行过程时临时数据存储位置。
命令所在: /usr/local/bin 目录。(如果在安装FastDFS过程中,没有修改make.sh文件中的 TARGET_PREFIX
属性值,命令所在为 /usr/bin 目录)
/自定义安装路径/bin/fdfs_upload_file /etc/fdfs/client.conf /要上传的文件
上传结束后,返回 group1/M00/00/00/xxxxxxxxxx.xxx ,检查 storage 服务结点中的
$store_path0/data/00/00/ 目录中是否有上传的文件(一般情况上传的文件按顺序保存在
s t o r e p a t h 0 / d a t a / 00 / 00 / 目录中,不能完全保证)。测试的上传文件结果: g r o u p 1 / M 00 / 00 / 00 / w K h V g 2 Q d P z m A c L F S A A S N d 34 d U Y 0799. j p g 卷名: g r o u p 1 文件名: M 00 / 00 / 00 / w K h V g 2 Q d P z m A c L F S A A S N d 34 d U Y 0799. j p g 其中 M 00 是一个虚拟目录,相当于 w i n d o w s 中的快捷方式,引用的是 store_path0/data/00/00/ 目录中,不能完全保证 )。 测试的上传文件结果:group1/M00/00/00/wKhVg2QdPzmAcLFSAASNd34dUY0799.jpg 卷名:group1 文件名:M00/00/00/wKhVg2QdPzmAcLFSAASNd34dUY0799.jpg 其中 M00 是一个虚拟目录,相当于 windows 中的快捷方式,引用的是 storepath0/data/00/00/目录中,不能完全保证)。测试的上传文件结果:group1/M00/00/00/wKhVg2QdPzmAcLFSAASNd34dUY0799.jpg卷名:group1文件名:M00/00/00/wKhVg2QdPzmAcLFSAASNd34dUY0799.jpg其中M00是一个虚拟目录,相当于windows中的快捷方式,引用的是store_path0/data目录。
./fdfs_delete_file /etc/fdfs/client.conf group1/M00/00/00/wKhVg2QdPzmAcLFSAASNd34dUY0799.jpg
删除结束后,检查 $store_path0/data/00/00/ 目录中是否还有文件。
如果FastDFS中保存的是图片信息。希望在 WEB 应用中可以直接访问FastDFS中的图片进行显示。
安装 Nginx是为了WEB应用中可以使用 HTTP协议直接访问Storage 服务中存储的文件。在 storage 结点所在服务
器安装 Nginx 组件。
需要安装两部分内容。
tar -zxf fastdfs-nginx-module-1.22.tar.gz -C /usr/local/fastdfs
cd /usr/local/fastdfs-nginx-module-1.22/src
vi /config
yum install -y gcc gcc-c++ make automake autoconf libtool pcre pcre-develzlib zlib-devel
openssl openssl-devel
tar -zxf nginx-1.16.1.tar.gz -C /自定义目录,必须已存在/fastdfs/
./configure --prefix=/usr/local/nginx --pid-path=/var/run/nginx/nginx.pid --lock-path=/var/lock/nginx.lock --error-log-path=/var/log/nainx/error.log --http-log-path=/var/log/nginx/access.log --with-http_gzip_static_module --http-client-body-temp-path=/var/temp/nginx/client --http-proxy-temp-path=/var/temp/nginx/fastcgi --http-fastcgi-temp-path=/var/temp/nginx/fastcgi --http-uwsgi-temp-path=/var/temp/nginx/uwsgi --http-scgi-temp-path=/var/temp/nginx/scgi --add-module=/usr/local/fastdfs/fastdfs-nginx-module-1.22/src/
– add-module 必须定义,此配置信息是用于指定安装 Nginx时需要加载的模块,如果未指定, Nginx安装过程不会
加载 fastdfs-nginx-module 模块,后续功能无法实现。
make
make install
这个时候可能会遇到如下错误:
解决方法如下:
报错fatal error:fdfs_define.h: 没有那个文件或目录
复制配置文件/usr/local/fastdfs/fastdfs-nginx-module-1.22/src/mod_fastdfs.conf到/etc/fdfs目录中
cp /自定义安装目录/fastdfs/fastdfs-nginx-module-1.22/src/mod_fastdfs.conf /etc/fdfs/
cd /etc/fdfs/
#修改配置文件 mod_fastdfs.conf
vim mod_fastdfs.conf
源配置:
connect_timeout=2 #连接超时时间,单位秒
tracker_server=tracker:22122 #tracker 服务结点
url_have_group_name = false #URL中是否包含group名称
store_path0=/home/yuqing/fastdfs # storage 服务结点的存储位置,与配置storage结点一致
参考修改值
connect_timeout=10
tracker_server=ip:22122
url_have_group_name = true
store_path0=/xxx/storage/store 自定义文件保存路径,必须已存在
复制 FastDFS安装包中的两个配置文件( http.conf 和 mime.types )到/etc/fdfs 目录中
cp /安装路径/fastdfs/fastdfs-6.06/conf/http.conf /etc/fdfs/
cp /安装路径/fastdfs/fastdfs-6.06/conf/mime.types /etc/fdfs/
ln -s /安装目录/lib64/libfdfsclient.so /usr/lib64/libfdfsclient.so
nginx启动后,会在默认的/usr/lib64目录中查找需要的so文件。如果在安装FastDFS时,修改了make.sh文件中的
TARGET_PREFIX参数,则必须创建此软连接
ln -s /fastdfs/storage/store/data/ /fastdfs/storage/store/data/M00
在上传文件到 FastDFS 后,FastDFS 会返回 group1/M00/00/00/xxxxxxxxxx.xxx。其中 group1 是卷名,在
mod_fastdfs.conf 配置文件中已配置了 url_have_group_name ,以保证URL解析正确。而其中的 M00 是FastDFS
保存数据时使用的虚拟目录,需要将这个虚拟目录定位到真实数据目录上。
cd /自定义安装目录/nginx/conf vim nginx.conf
参考修改配置:( 部分配置信息,不要完整复制 )
user root; # Nginx 需要访问 linux文件系统,必须有文件系统的权限。User root代表nginx 访问文件系统的权限 如果因为这里运行不了nginx就要注释了
是root用户权限。如果不开启权限,可能有404访问错误。
server{
listen 8888; # storage 配置中,有 http.server_port=8888 的配置信息,必须一致。配置文件
是/etc/fdfs/storaged.conf
server_name localhost;
location ~/group([0-9])/M00{
ngx_fastdfs_module;
}
}
cd /etc/init.d/
./fdfs_storaged restart 重启 storage
cd /自定义安装目录/nginx/sbin/
./nginx
此步骤省略
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--FastDFS依赖-->
<dependency>
<groupId>org.csource</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.29-SNAPSHOT</version>
</dependency>
<!--thymeleaf 依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
github下载zip
github下载
解压
找个目录解压,然后打开解压后文件夹的黑窗口
生产jar包
黑窗口中输入:mvn clean install
如果没有配置maven本地仓库,那么他会默认下载到默认的本地仓库C:${user.home}.m2\repository
将生成的jar包导入idea
然后选择jar包 点击ok,重新加载依赖 就可以了
在resources目录下创建 yc.conf文件
添加如下内容
#连接超时
connect_timeout = 2
#网络超时
network_timeout = 30
#编码格式
charset = UTF-8
#tracker端口号
http.tracker_http_port = 22122
#防盗链功能
http.anti_steal_token = no
#秘钥
http.secret_key = FastDFS1234567890
#tracker ip:端口号
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
package com.yc.fastDFS01.pojo;
import java.util.Arrays;
/**
* 文件上传对象类
*
* @Author 杨超
* @PackageName pojo
* @Package com.yc.fastDFS01.pojo
* @Date 2023/3/17 10:45
*/
public class FastDFSFile {
private String name;
private byte[] content;
private String ext;
private String md5;
private String author;
private String height;
public FastDFSFile() {
}
public FastDFSFile(String name, byte[] content, String ext) {
this.name = name;
this.content = content;
this.ext = ext;
}
public FastDFSFile(String name, byte[] content, String ext, String md5, String author, String height) {
this.name = name;
this.content = content;
this.ext = ext;
this.md5 = md5;
this.author = author;
this.height = height;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public byte[] getContent() {
return content;
}
public void setContent(byte[] content) {
this.content = content;
}
public String getExt() {
return ext;
}
public void setExt(String ext) {
this.ext = ext;
}
public String getMd5() {
return md5;
}
public void setMd5(String md5) {
this.md5 = md5;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getHeight() {
return height;
}
public void setHeight(String height) {
this.height = height;
}
@Override
public String toString() {
return "FastDFSFile{" +
"name='" + name + '\'' +
", content=" + Arrays.toString(content) +
", ext='" + ext + '\'' +
", md5='" + md5 + '\'' +
", author='" + author + '\'' +
", height='" + height + '\'' +
'}';
}
}
package com.yc.fastDFS01.util;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
/**
* 文件上传工具类
*
* @Author 杨超
* @PackageName util
* @Package com.yc.fastDFS01.util
* @Date 2023/3/17 10:47
*/
public class FastDFSClient {
private static Logger logger = LoggerFactory.getLogger(FastDFSClient.class);
//ClientGlobal.init 方法会读取配置文件,并初始化对应的属性
static {
try {
String filePath = new ClassPathResource("fdfs_client.conf").getFile().getAbsolutePath();
ClientGlobal.init(filePath);
} catch (Exception e) {
e.printStackTrace();
logger.error("FastDFS Client Init Fail!", e);
}
}
/**
* 上传文件
*
* @param file
* @return
*/
public static String[] upload(FastDFSFile file) {
logger.info("File Name:" + file.getName() + "File Length:" + file.getContent().length);
//文件属性信息
NameValuePair[] meta_list = new NameValuePair[1];
meta_list[0] = new NameValuePair("author", file.getAuthor());
long startTime = System.currentTimeMillis();
String[] uploadResults = null;
StorageClient storageClient = null;
try {
//获取storage客户端
storageClient = getStorageClient();
//上传
uploadResults = storageClient.upload_file(file.getContent(), file.getExt(), meta_list);
} catch (Exception e) {
e.printStackTrace();
logger.info("Exception when uploadind the file:" + file.getName(), e);
}
//验证上传结果
if (uploadResults == null && storageClient != null) {
logger.info("upload file fail , error code:" + storageClient.getErrorCode());
}
//上传文件成功会返回 groupName.
logger.info("upload file successfully!!! group_name:" + uploadResults[0] + " , remoteFileName: " + uploadResults[1]);
return uploadResults;
}
/**
* 获取文件信息
*
* @param groupName
* @param remoteFileName
* @return
*/
public static FileInfo getFile(String groupName, String remoteFileName) {
try {
StorageClient storageClient = getStorageClient();
return storageClient.get_file_info(groupName, remoteFileName);
} catch (Exception e) {
e.printStackTrace();
logger.info("Exception:Get File from Fast DFS failed", 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);
InputStream ins = new ByteArrayInputStream(fileByte);
return ins;
} catch (Exception e) {
e.printStackTrace();
logger.info("Exception: Get File from FastDFS failed", e);
}
return null;
}
/**
* 删除文件
*
* @param groupName
* @param remoteFileName
* @throws Exception
*/
public static void deleteFile(String groupName, String remoteFileName) throws Exception {
StorageClient storageClient = getStorageClient();
int i = storageClient.delete_file(groupName, remoteFileName);
logger.info("delete file successfully!!! " + i);
}
/**
* 生成storage客户端
*
* @return
* @throws Exception
*/
private static StorageClient getStorageClient() throws Exception {
TrackerServer trackerServer = getTrackerServer();
StorageClient storageClient = new StorageClient(trackerServer, null);
return storageClient;
}
/**
* 生成Tracker服务器端
*
* @return
* @throws Exception
*/
private static TrackerServer getTrackerServer() throws Exception {
TrackerClient trackerClient = new TrackerClient();
TrackerServer trackerServer = trackerClient.getTrackerServer();
return trackerServer;
}
/**
* 获取文件路径
*
* @return
* @throws Exception
*/
public static String getTrackerUrl() throws Exception {
TrackerClient trackerClient = new TrackerClient();
TrackerServer trackerServer = trackerClient.getTrackerServer();
StorageServer storeStorage = trackerClient.getStoreStorage(trackerServer);
return "http://" + storeStorage.getInetSocketAddress().getHostString() + ":8888/";
}
}
package com.yc.fastDFS01.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import java.io.InputStream;
/**
* 文件上传controller
* @Author 杨超
* @PackageName controller
* @Package com.yc.fastDFS01.controller
* @Date 2023/3/17 10:52
*/
@Controller
public class UploadController {
private static Logger logger = LoggerFactory.getLogger(FastDFSClient.class);
/**
* 页面跳转
*
* @return
*/
@GetMapping("/fastDfs")
public String index() {
return "upload";
}
/**
* 上传文件
*
* @param file
* @param redirectAttributes
* @return
*/
@PostMapping("/upload")
public String singleFileUpload(@RequestParam("file") MultipartFile file, RedirectAttributes redirectAttributes) {
if (file.isEmpty()) {
redirectAttributes.addFlashAttribute("message", "Please select a file to upload");
return "uploadStatus";
}
try {
//上传文件拿到返回的文件路径
String path = saveFile(file);
redirectAttributes.addFlashAttribute("message", "You successfully uploaded '" + file.getOriginalFilename() + "'");
redirectAttributes.addFlashAttribute("path", "file path url '" + path + "'");
} catch (Exception e) {
e.printStackTrace();
logger.info("upload file failed", e);
}
return "redirect:/uploadStatus";
}
/**
* 页面跳转
*
* @return
*/
@GetMapping("/uploadStatus")
public String uploadStatus() {
return "uploadStatus";
}
/**
* 上传文件
*
* @param file
* @return
* @throws Exception
*/
private String saveFile(MultipartFile file) throws Exception {
String[] fileAbsolutePath = {};
String fileName = file.getOriginalFilename();
String ext = fileName.substring(fileName.lastIndexOf(".") + 1);
byte[] file_buff = null;
InputStream inputStream = file.getInputStream();
if (inputStream != null) {
int len1 = inputStream.available();
file_buff = new byte[len1];
inputStream.read(file_buff);
}
inputStream.close();
FastDFSFile fdfs = new FastDFSFile(fileName, file_buff, ext);
try {
//上传文件
fileAbsolutePath = FastDFSClient.upload(fdfs);
} catch (Exception e) {
e.printStackTrace();
logger.info("upload file Exception!", e);
}
if (fileAbsolutePath == null) {
logger.info("upload file failed,please upload again!");
}
String path = FastDFSClient.getTrackerUrl() + fileAbsolutePath[0] + "/" + fileAbsolutePath[1];
return path;
}
}
在 resources/templates/ 目录下创建
<!DOCTYPE html>
<html>
<body>
<h1>Spring Boot file upload example</h1>
<form method="POST" action="/upload" enctype="multipart/form-data">
<input type="file" name="file" /><br/><br/>
<input type="submit" value="Submit" />
</form>
</body>
</html>
在 resources/templates/ 目录下创建
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<body>
<h1>Spring Boot - Upload Status</h1>
<div th:if="${message}">
<h2 th:text="${message}"/>
</div>
<div th:if="${path}">
<h2 th:text="${path}"/>
</div>
</body>
</html>