一個簡單的FastDFS + SpringBoot + RestFul 上傳文件的案例
前言:FastDFS是一個新接觸的技術,SpringBoot和RestFul在本文中就不做介紹了,閱讀本文需要有SpringBoot和RestFul的基礎
FastDFS
什麼是 FastDFS
FastDFS 是⼀個開源的輕量級分佈式⽂件系統,它解決了⼤數據量存儲和負載均衡等問題,特別適合以中⼩ ⽂件(建議範圍:4 KB < file_size < 500 MB)爲載體的在線服務,如相冊⽹站、視頻⽹站等。在 UC 基於 FastDFS 開發向⽤戶提供了⽹盤、社區、⼴告和應⽤下載等業務的存儲服務。
FastDFS 由 C 語⾔開發,⽀持 Linux、FreeBSD 等 UNIX 系統類 Google FS,不是通⽤的⽂件系統,只能通 過專有 API 訪問,⽬前提供了 C、Java 和 PHP API,爲互聯⽹應⽤量身定做,解決了⼤容量⽂件存儲問 題,追求⾼性能和⾼擴展性,FastDFS 可以看做是基於⽂件的 Key Value Pair 存儲系統,稱作分佈式⽂件存 儲服務會更合適。
FastDFS 特性
⽂件不分塊存儲,上傳的⽂件和 OS ⽂件系統中的⽂件⼀⼀對應 ⽀持相同內容的⽂件只保存⼀份,節約磁盤空間 下載⽂件⽀持 HTTP 協議,可以使⽤內置 Web Server,也可以和其他 Web Server 配合使⽤ ⽀持在線擴容 ⽀持主從⽂件 存儲服務器上可以保存⽂件屬性(meta-data)V2.0 ⽹絡通信採⽤ libevent,⽀持⼤併發訪問,整體性 能更好
FastDFS 相關概念
FastDFS 服務端有三個⻆角⾊:跟蹤服務器(Tracker Server)、存儲服務器(Storage Server)和客戶端 (Client)。
Tracker Server:跟蹤服務器,主要做調度⼯作,起負載均衡的作⽤。在內存記錄集羣中所有存儲組和 存儲服務器的狀態信息,是客戶端和數據服務器交互的樞紐。相⽐ GFS 中的 Master 更爲精簡,不記錄 ⽂件索引信息,佔⽤的內存量很少。 Storage Server:存儲服務器(⼜稱存儲節點或數據服務器),⽂件和⽂件屬性(Meta Data)都保存 到存儲服務器上。Storage Server 直接利⽤ OS 的⽂件系統調⽤管理⽂件。 Client:客戶端,作爲業務請求的發起⽅,通過專有接⼝,使⽤ TCP/IP 協議與跟蹤器服務器或存儲節 點進⾏數據交互。FastDFS 向使⽤者提供基本⽂件訪問接⼝,如 upload、download、append、delete 等,以客戶端庫的⽅式提供給⽤戶使⽤。
上傳機制
首先客戶端請求 Tracker 服務獲取到存儲服務器的 IP 地址和端口,然後客戶端根據返回的 IP 地址和端口號請求上傳文件,存儲服務器接收到請求後生產文件,並且將文件內容寫入磁盤並返回給客戶端 file_id、路徑信息、文件名等信息,客戶端保存相關信息上傳完畢。
圖示
項目結構
fastdfs包
FastDFSFile:改類是上傳的工具類,封裝FastDFSFile。主要包括文件的基礎信息 [文件名,內容,文件的類型,作者]
代碼
package com.jimmy.fastdfs;
/**
* @Author Jimmy
* @Date 2019/8/22 9:25 PM
* @Desc
*
* 上傳工具類:
* 封裝 FastDFSFile,文件基礎信息包括文件名、內容、文件類型、作者等
*/
public class FastDFSFile {
//文件名
private String name;
//文件內容
private byte[] content;
//文件類型
private String ext;
//作者 (上傳者)
private String author;
public FastDFSFile() {
}
public FastDFSFile(String name, byte[] content, String ext, String author) {
this.name = name;
this.content = content;
this.ext = ext;
this.author = author;
}
public FastDFSFile(String name, byte[] content, String ext) {
this.name = name;
this.content = content;
this.ext = ext;
}
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 getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
}
FastDFSClient:該類客戶端 提供上傳文件
代碼
package com.jimmy.fastdfs;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.ClientGlobal;
import org.csource.fastdfs.StorageClient;
import org.csource.fastdfs.TrackerClient;
import org.csource.fastdfs.TrackerServer;
import org.springframework.core.io.ClassPathResource;
import java.io.IOException;
/**
* @Author Jimmy
* @Date 2019/8/22 9:29 PM
* @Desc
*
* FastDFSClient:客戶端 提供上傳文件
*/
public class FastDFSClient {
//TODO 1.類加載時讀取配置信息,並進行初始化
static {
try {
String path = new ClassPathResource("FastDFS.conf").getFile().getAbsolutePath();
//FastDFS客戶端初始化方法
ClientGlobal.init(path);
} catch (Exception e) {
e.printStackTrace();
}
}
//TODO 2.上傳文件
public static String[] upload(FastDFSFile file){
//打印查看文件的信息 文件名和文件的字節長度
System.out.println("FileName:" + file.getName() + " FileContent:" + file.getContent().length);
//文件屬性
//NameValuePair,主要存儲文件的一些基礎屬性,如作者信息、創建時間等
NameValuePair[] nameValuePair = new NameValuePair[1];
nameValuePair[0] = new NameValuePair("author",file.getAuthor());
//上傳結果
String[] uploadResults = null;
//存儲客戶端
StorageClient storageClient = null;
try {
//獲取客戶端實例
storageClient = getStorageClient();
//封裝最終上傳結果
uploadResults = storageClient.upload_file(file.getContent(),file.getExt(),nameValuePair);
} catch (Exception e) {
e.printStackTrace();
}
//驗證上傳結果
if (uploadResults == null && storageClient != null) {
//這裏直接打印上傳文件失敗 不做其他操作
System.out.println("上傳文件失敗");
}
System.out.println("上傳文件成功");
return uploadResults;
}
/**
* 首先獲取 TrackerServer [追蹤服務器] 信息,
* 使用 TrackerServer 構建出每次操作的客戶端實例 StorageClient
* @return
*/
private static StorageClient getStorageClient() throws IOException {
//獲取TrackerServer
TrackerServer trackerServer = getTrackerServer();
StorageClient client = new StorageClient(trackerServer, null);
return client;
}
/**
* 獲取TrackerServer的方法
* @return
*/
private static TrackerServer getTrackerServer() throws IOException {
TrackerClient trackerClient = new TrackerClient();
//客戶端獲得追蹤服務器的連接
TrackerServer server = trackerClient.getConnection();
return server;
}
/**
* TODO 上傳文件返回的URL
* @return
* @throws IOException
*/
public static String getTrackerUrl() throws IOException {
return "http://"+getTrackerServer().getInetSocketAddress().getHostString()+":"+ClientGlobal.getG_tracker_http_port()+"/";
}
}
controller包
UploadFileController:處理文件上傳的controller
代碼
package com.jimmy.controller;
import com.jimmy.fastdfs.FastDFSClient;
import com.jimmy.fastdfs.FastDFSFile;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
/**
* @Author Jimmy
* @Date 2019/8/22 10:15 PM
* @Desc 處理文件上傳的controller
*/
@RestController
@RequestMapping("/fastdfs")
public class UploadFileController {
/**
* TODO 上傳文件的接口
* @param file
* @return
*/
@PostMapping("/upload")
public ResponseEntity<String> upload(MultipartFile file) throws IOException {
if (file.isEmpty()) {
return new ResponseEntity<>("文件爲不能爲空",HttpStatus.INTERNAL_SERVER_ERROR);
}
String path = saveFile(file);
return new ResponseEntity<>(path,HttpStatus.CREATED);
}
public String saveFile(MultipartFile file) throws IOException {
//文件信息
String[] fileInformation = {};
//文件的名稱
String fileName = file.getOriginalFilename();
//通過文件的名稱獲取文件的類型
String fileType = fileName.substring(fileName.lastIndexOf(".") + 1);
//儲存文件內容的字節數組
byte[] fileContent = null;
InputStream inputStream = file.getInputStream();
//一下操作是將文件讀取到字節數組
if (inputStream != null) {
int length = inputStream.available();
fileContent = new byte[length];
inputStream.read(fileContent);
}
//關閉流
inputStream.close();
FastDFSFile fastDFSFile = new FastDFSFile(fileName, fileContent, fileType);
fileInformation = FastDFSClient.upload(fastDFSFile);
String path = FastDFSClient.getTrackerUrl() + fileInformation[0] + "/" + fileInformation[1];
return path;
}
}
UploadFile.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上傳</title>
<script type="text/javascript" src="js/jquery.min.js"></script>
</head>
<body>
<script>
$(function () {
$("#button").click(function () {
var data = new FormData;
var file = $("#file").prop("files");
data.append("file",file[0]);
$.ajax({
type: "POST",
url: "/fastdfs/upload",
data: data,
cache: false,
processData: false,
contentType: false,
statusCode: {
201: function (data) {
alert("上傳成功!文件的路徑爲:"+data)
window.location.href = data;
},
500: function (data) {
console.log(data.toString())
alert("文件上傳失敗")
}
}
});
})
})
</script>
<div>
<form>
<input type="file" id="file"/>
<input type="button" id="button" value="上傳"/>
</form>
</div>
</body>
</html>
application.properties
### 最大上傳文件大小
spring.servlet.multipart.max-file-size=10MB
### 總上傳的文件大小
spring.servlet.multipart.max-request-size=10MB
配置文件 FastDFS.conf
connect_timeout = 60
network_timeout = 60
charset = UTF-8
http.tracker_http_port = 追蹤服務器端口號
http.anti_steal_token = no
http.secret_key = 123456
tracker_server = 服務器IP:22122
pom.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.jimmy</groupId>
<artifactId>springboot_fastdfs</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot_fastdfs</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<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>
<dependency>
<groupId>org.csource</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.27-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>