一个简单的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>