1、FastDFS簡介
FastDFS是一個開源的輕量級分佈式文件系統,它對文件進行管理,功能包括:文件存儲、文件同步、文件訪問(文件上傳、文件下載)等,解決了大容量存儲和負載均衡的問題。特別適合以文件爲載體的在線服務,如相冊網站、視頻網站等等。
FastDFS爲互聯網量身定製,充分考慮了冗餘備份、負載均衡、線性擴容等機制,並注重高可用、高性能等指標,使用FastDFS很容易搭建一套高性能的文件服務器集羣提供文件上傳、下載等服務。
FastDFS 架構包括 Tracker server 和 Storage server。客戶端請求 Tracker server 進行文件上傳、下載,通過Tracker server 調度最終由 Storage server 完成文件上傳和下載。Tracker server 作用是負載均衡和調度,通過 Tracker server 在文件上傳時可以根據一些策略找到Storage server 提供文件上傳服務。可以將 tracker 稱爲追蹤服務器或調度服務器。Storage server 作用是文件存儲,客戶端上傳的文件最終存儲在 Storage 服務器上,Storageserver 沒有實現自己的文件系統而是利用操作系統的文件系統來管理文件。可以將storage稱爲存儲服務器
2、文件上傳流程
客戶端上傳文件後存儲服務器將文件 ID 返回給客戶端,此文件 ID 用於以後訪問該文件的索引信息。文件索引信息包括:組名,虛擬磁盤路徑,數據兩級目錄,文件名
組名:文件上傳後所在的 storage 組名稱,在文件上傳成功後有storage 服務器返回,需要客戶端自行保存。
虛擬磁盤路徑:storage 配置的虛擬路徑,與磁盤選項store_path*對應。如果配置了store_path0 則是 M00,如果配置了 store_path1 則是 M01,以此類推。
數據兩級目錄:storage 服務器在每個虛擬磁盤路徑下創建的兩級目錄,用於存儲數據文件。
文件名:與文件上傳時不同。是由存儲服務器根據特定信息生成,文件名包含:源存儲服務器 IP 地址、文件創建時間戳、文件大小、隨機數和文件拓展名等信息。
3、搭建文件存儲微服務
創建文件管理微服務 service_file,該工程主要用於實現文件上傳以及文件刪除等功能。
3.1 修改pom.xml,引入依賴
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐starter‐web</artifactId>
</dependency>
<dependency>
<groupId>net.oschina.zcx7878</groupId>
<artifactId>fastdfs‐client‐java</artifactId>
<version>1.27.0.0</version>
</dependency>
</dependencies>
3.2 在resources文件夾下創建fasfDFS的配置文件fdfs_client.conf
# 連接超時時間,單位爲秒。
connect_timeout=60
# 通信超時時間,單位爲秒
network_timeout=60
# 字符集
charset=utf-8
# tracker的http端口:瀏覽器訪問
http.tracker_http_port=8080
# Tracker服務器與外界通信的IP和端口:Java程序訪問
tracker_server=192.168.33.133:22122
3.3 在resources文件夾下創建application.yml
server:
port: 18082
spring:
application:
name: file
servlet:
multipart:
max‐file‐size: 10MB
max‐request‐size: 10MB
main:
allow‐bean‐definition‐overriding: true # 當遇到同樣名字的時候,是否允許覆蓋註冊
eureka:
client:
service‐url:
defaultZone: http://127.0.0.1:7001/eureka
instance:
prefer‐ip‐address: true
- max-file-size是單個文件大小
- max-request-size是設置總上傳的數據大小
3.4 創建啓動類
@SpringBootApplication
@EnableEurekaClient
public class FileApplication {
public static void main(String[] args) {
SpringApplication.run(FileApplication.class);
}
}
4、文件上傳示例代碼
4.1 文件信息封裝
文件上傳一般都有文件的名字、文件的內容、文件的擴展名、文件的md5值、文件的作者等相關屬性,我們可以創建一個對象封裝這些屬性。
@Getter
@Setter
public class FastDFSFile {
// 文件名字
private String name;
// 文件內容
private byte[] content;
// 文件擴展名
private String ext;
// 文件MD5摘要值
private String md5;
// 文件創建作者
private String author;
public FastDFSFile(String name, byte[] content, String ext, String
height,String width, String author) {
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;
}
}
4.2、文件操作工具類
@Slf4j
public class FastDFSUtils {
static {
try{
// 獲得配置文件路徑
String file = new ClassPathResource("fsdf_client.conf").getPath();
// 加載tracker配置信息
ClientGlobal.init(file);
} catch(Exception e){
e.printStackTrace();
}
}
/**
* 文件上傳方法
* @param file
* @return
*/
public static String[] upload(FastDFSFile file) {
// 獲取文件的作者
NameValuePair[] meta_list = new NameValuePair[1];
meta_list[0] = new NameValuePair("author", file.getAuthor());
// 接收返回數據
String[] uploadResults = null;
StorageClient storageClient = null;
try {
// 創建StorageClient客戶端對象
storageClient = getStorageClient();
/**
* 1)文件字節數組
* 2)文件擴展名
* 3)文件作者
*/
uploadResults = storageClient.upload_file(file.getContent(),file.getExt(),meta_list);
} catch (Exception e){
log.error("Exception when uploadind the file:" +
file.getName(), e);
}
if (uploadResults == null && storageClient!=null) {
log.error("upload file fail, error code:" +
storageClient.getErrorCode());
}
// 獲取組名
String groupName = uploadResults[0];
// 獲取文件存儲路徑
String remoteFileName = uploadResults[1];
return uploadResults;
}
/**
* 文件下載
* @param groupName 文件所在組名:group1
* @param remoteFileName 文件的存儲路徑名 M00/00/00/wKghhV6FkViAfTuRAAGSbLHFVsM382.png
* @return
*/
public static InputStream downloadFile(String groupName,String remoteFileName) throws Exception {
// 1. 獲得Storage客戶端
StorageClient storageClient = getStorageClient();
// 2. 下載文件
byte[] buf = storageClient.download_file(groupName, remoteFileName);
// 3. 將字節數組轉換爲輸入流
return new ByteArrayInputStream(buf);
}
/** 文件刪除
* @param groupName 文件所在組名:group1
* @param remoteFileName 文件的存儲路徑名 M00/00/00/wKghhV6FkViAfTuRAAGSbLHFVsM382.png
*/
public static void deleteFile(String groupName,String remoteFileName) throws Exception{
getStorageClient().delete_file(groupName, remoteFileName);
}
/**
* 獲取文件信息
* @param groupName 文件所在組名:group1
* @param remoteFileName 文件的存儲路徑名 M00/00/00/wKghhV6FkViAfTuRAAGSbLHFVsM382.png
* @return
*/
public static FileInfo getFileInfo(String groupName, String remoteFileName) throws Exception {
return getStorageClient().get_file_info(groupName, remoteFileName);
}
/**
* 獲取Tracker的信息
* @throws Exception
*/
public static String getTrackerInfo() throws IOException{
// 獲取TrackerServer對象
TrackerServer trackerServer = getTrackerServer();
// 獲得Tracker的IP和端口
String ip = trackerServer.getInetSocketAddress().getHostString();
int port = ClientGlobal.getG_tracker_http_port();
return "http://"+ip+":"+port+"/";
}
/**
* * 獲取Storage客戶端
*/
private static StorageClient getStorageClient() throws IOException {
TrackerServer trackerServer = getTrackerServer();
StorageClient storageClient = new StorageClient(trackerServer,
null);
return storageClient;
}
/*
**
* 獲取Tracker服務器
*/
private static TrackerServer getTrackerServer() throws IOException {
TrackerClient trackerClient = new TrackerClient();
TrackerServer trackerServer = trackerClient.getConnection();
return trackerServer;
}
}
4.3、文件上傳控制器
@RestController
@CrossOrigin
public class FileController {
@PostMapping("/upload")
public Result uploadFile(@RequestParam("file") MultipartFile file) throws IOException {
// 創建FastDFSFile對象:封裝文件數據
FastDFSFile fastDFSFile = new FastDFSFile(
file.getOriginalFilename(),
file.getBytes(),
StringUtils.getFilenameExtension(file.getOriginalFilename()));
// 調用工具類方法執行上傳
String[] uploadResults = FastDFSUtils.upload(fastDFSFile);
// 獲取組名
String groupName = uploadResults[0];
// 獲取文件存儲路徑
String remoteFileName = uploadResults[1];
// 拼接訪問地址
String url = FastDFSUtils.getTrackerInfo() +groupName+"/"+remoteFileName;
return new Result(true, StatusCode.OK, "上傳成功",url);
}
}