架构图
上传流程
- client询问tracker上传到的 storage,不需要附加参数;
- tracker返回一台可用的storage;
- client直接和storage 通讯完成文件上传
下载流程
- client询问tracker下载文件的storage,参数为文件标识(组名和文件名);
- tracker返回一台可用的storage;
- client直接和storage通讯完成文件下载。
安装教程
其他博客
测试上传:
http://192.168.166.140/group1/M00/00/00/wKimjGJCy2yAGoYkAAEZCxQ4CEc116.jpg
http://192.168.166.140/group1/M00/00/00/wKimjGJD6O6ATd6TAAF5_l7OiZ8751.jpg
http://192.168.166.140/group1/M00/00/00/wKimjGJD6PSAS59yAAHrcOF67mw325.jpg
卷名:group1
文件名:M00/00/00/wKimjGJCy2yAGoYkAAEZCxQ4CEc116.jpg
其中 M00 是一个虚拟目录,相当于 windows 中的快捷方式,引用的是$store_path0/data目录。
java调用
github连接
下载完后自己编译:mvn clean install
成功之后会有个jar包
maven仓库中也有这个jar包
jar包中常用依赖类
CLientGlobal
用于加载配置文件的公共客户端工具。
常用方法:
- init(String conf_filename); 根据配置文件路径及命名,加载配置文件并设置客户端公共参数,配置文件类型为 conf 文件。可以使用绝对路径或相对路径加载。
- initByProperties(Properties props); 根据 Properties 对象设置客户端公共参数。
注意:使用 conf 或 properties 进行客户端参数配置时,参数 key 命名不同。
TrackerClient
跟踪器客户端类型。创建此类型对象时,需传递跟踪器组,就是跟踪器的访问地址信息。无参构造方法默认使用ClientGlobal.g_tracker_group 常量作为跟踪器组来构造对象。
创建对象的方式为:
new TrackerClient(); 或 new TrackerClient(ClientGlobal.g_tracker_group)
TrackerServer
跟踪器服务类型。此类型的对象是通过跟踪器客户端对象构建的。实质上就是一个与FastDFS Tracker Server 的链接对象。是代码中与 Tracker Server 链接的工具。
构建对象的方式为:
trackerClient. getTrackerServer ();
StorageServer
存储服务类型。此类型的对象是通过跟踪器客户端对象构建的。实质上就是一个与FastDFS Storage Server 的链接对象。是代码中与 StroageServer 链接的工具。获取的具体存储服务链接,是由 Tracker Server 分配的,所以构建存储服务对象时,需要依赖跟踪器服务对象。
构建对象的方式为:
trackerClient.getStoreStorage(trackerServer);
StorageClient
存储客户端类型。此类型的对象是通过构造方法创建的。创建时,需传递跟踪服务对象和存储服务对象。此对象实质上就是一个访问 FastDFS Storage Server 的客户端对象,用于实现文件的读写操作。
创建对象的方式为:
new StorageClient(trackerServer, storageServer);
常用方法有:
upload_file(String local_filename, String file_ext_name,NameValuePair[] meta_list); 上传文件的方法,参数 local_filename为要上传的本地文件路径及文件名,可使用绝对路径或相对路径;参数file_ext_name为上传文件的扩展名,如果传递null,则自动解析文件扩展名;参数meta_list 是用于设置上传文件的源数据的,如上传用户、上传描述等。
download_file(String group_name, String remote_file_name); 下载文件的方法,参数group为组名/卷
名,就是 Storage Server中/etc/fdfs/storage.conf 配置文件中配置的group_name 参数值,也是要下载的文件所在组/卷的命名;参数 remote_file_name 为要下载的文件的路径及文件名。
delete_file(String group_name, String remote_file_name); 删除文件的方法,参数含义同
download_file方法参数。
pom.xml引入依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<!--SpringBoot Web启动-->
<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>
<!--模板引擎 thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--lombok插件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.6</version>
<scope>provided</scope>
</dependency>
<!--FastDFS依赖-->
<dependency>
<groupId>org.csource</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.29-SNAPSHOT</version>
</dependency>
</dependencies>
项目结构:
fdfs_client.conf
#连接超时
connect_timeout = 2
#网络超时
network_timeout = 30
#编码格式
charset = UTF-8
#tracker端口号
http.tracker_http_port = 8080
#防盗链功能
http.anti_steal_token = no
#秘钥
http.secret_key = FastDFS1234567890
#tracker ip:端口号
tracker_server = 192.168.166.140: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
UploadController
package com.shengun.controller;
import com.shengun.pojo.FastDFSFile;
import com.shengun.utill.FastDFSClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
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
@Slf4j
public class UploadController {
@Value("${fastdfs.ip}")
private String fastdfsUrl;
/**
* 页面跳转
*
* @return
*/
@GetMapping("/")
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 "redirect:uploadStatus";
}
try {
// 上传文件拿到返回的文件路径
String path=saveFile(file);
redirectAttributes.addFlashAttribute("message", "You successfully uploaded '" + file.getOriginalFilename() + "'");
redirectAttributes.addFlashAttribute("path", "file path url '" + path + "'");
} catch (Exception e) {
log.error("upload file failed",e);
}
return "redirect:/uploadStatus";
}
/**
* 上传文件
* @param multipartFile
* @return
* @throws Exception
*/
public String saveFile(MultipartFile multipartFile) throws Exception{
String[] fileAbsolutePath={};
String fileName=multipartFile.getOriginalFilename();
log.info("fileName:" + fileName);
String ext = fileName.substring(fileName.lastIndexOf(".") + 1);
log.info("ext:" + ext);
byte[] file_buff = null;
InputStream inputStream=multipartFile.getInputStream();
if(inputStream!=null){
int len1 = inputStream.available();
file_buff = new byte[len1];
inputStream.read(file_buff);
}
log.info("file_buff:" + file_buff.toString());
inputStream.close();
FastDFSFile file = new FastDFSFile(fileName, file_buff, ext);
//上传文件
try {
fileAbsolutePath = FastDFSClient.upload(file);
} catch (Exception e) {
log.error("upload file Exception!",e);
}
if (fileAbsolutePath==null) {
log.error("upload file failed,please upload again!");
}
log.info(fastdfsUrl);
String path=FastDFSClient.getTrackerUrl() +fileAbsolutePath[0]+ "/"+fileAbsolutePath[1];
log.info("path:"+ path);
// String path=fastdfsUrl + "/" +fileAbsolutePath[0]+ "/"+fileAbsolutePath[1];
return path;
}
/**
* 页面跳转
* @return
*/
@GetMapping("/uploadStatus")
public String uploadStatus() {
return "uploadStatus";
}
}
utill FastDFSClient
package com.shengun.utill;
import com.shengun.pojo.FastDFSFile;
import lombok.extern.slf4j.Slf4j;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.*;
import org.springframework.core.io.ClassPathResource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@Slf4j
public class FastDFSClient {
static{
try {
String filePath = new ClassPathResource("fdfs_client.conf").getFile().getAbsolutePath();
ClientGlobal.init(filePath);
} catch (Exception e) {
log.error("FastDFS Client Init Fail!",e);
}
}
/**
* @param file
* @return
*/
public static String[] upload(FastDFSFile file) {
log.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) {
log.error("when uploadind the file:"+file.getName(),e.getMessage());
}
log.info("upload_file time used:" + (System.currentTimeMillis() - startTime) + "ms");
//验证上传结果
if (uploadResults == null && storageClient!=null) {
log.error("upload file fail, error code:" + storageClient.getErrorCode());
}
//上传文件成功会返回 groupName。
log.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 (IOException e) {
log.error("IO Exception: Get File from Fast DFS failed", e);
} catch (Exception e) {
log.error("Non IO 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 (IOException e) {
log.error("IO Exception: Get File from Fast DFS failed", e);
} catch (Exception e) {
log.error("Non IO Exception: Get File from Fast DFS failed", e);
}
return null;
}
/**
* 生成Storage客户端
* @return
* @throws IOException
*/
private static StorageClient getStorageClient() throws IOException {
TrackerServer trackerServer = getTrackerServer();
StorageClient storageClient = new StorageClient(trackerServer, null);
return storageClient;
}
/**
* 删除文件
* @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);
log.info("delete file successfully!!!" + i);
}
/**
* 生成Tracker服务器端
* @return
* @throws IOException
*/
private static TrackerServer getTrackerServer() throws IOException {
TrackerClient trackerClient = new TrackerClient();
TrackerServer trackerServer = trackerClient.getTrackerServer();
return trackerServer;
}
/**
* 获取文件路径
* @return
* @throws IOException
*/
public static String getTrackerUrl() throws Exception {
TrackerClient trackerClient = new TrackerClient();
TrackerServer trackerServer = trackerClient.getTrackerServer();
StorageServer storeStorage = trackerClient.getStoreStorage(trackerServer);
return "http://"+storeStorage.getInetSocketAddress().getHostString()+":80/";
}
}
pojo FastDFSFile
package com.shengun.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class FastDFSFile {
private String name;
private byte[] content;
private String ext;
private String md5;
private String author;
private String height;
public FastDFSFile(String name, byte[] content, String ext) {
this.name = name;
this.content = content;
this.ext = ext;
}
}
模板template upload.html uploadStatus.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<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>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<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>
测试