環境引入:
在一個大型的教育官網,會擁有大量優質的視頻教程,並且免費提供給用戶去下載,文件太多如果高效存儲?用戶訪問量大如何保證下載速度?分布式文件系統是解決這些問題的有效方法之一
一、什么是文件系統
分布式文件系統(Distributed File System)是指文件系統管理的物理存儲資源不一定直接連接在本地節點上,而是通過計算機網絡與節點相連。分布式文件系統的設計基於客戶機/服務器模式。一個典型的網絡可能包括多個供多用戶訪問的服務器。另外,對等特性允許一些系統扮演客戶機和服務器的雙重角色。例如,用戶可以“發表”一個允許其他客戶機訪問的目錄,一旦被訪問,這個目錄對客戶機來說就像使用本地驅動器一樣。
-----------------百度百科
為什么需要:以前,我們將上傳的視屏文件等放在一台宿主機內,如果一個盤符內存不夠,就增加硬盤個數,但是單純的增加硬盤個數已經無法滿足當代的需求,畢竟硬盤訪問速度有限
解決方式:增加計算機個數,將視屏分別放在不同計算機內,通過網絡將一個一個計算機的文件系統連接起來組成一個網絡文件系統,形成一個分布式網絡
優點:用於擴容、高並發場景
1、一台計算機的文件系統處理能力擴充到多台計算機內同時處理
2、一台計算機掛了還有另外副本計算提供數據
3、每台計算機可以放在不同的地域,這樣用戶就可以就近訪問,提高訪問速度
二、分布式文件服務提供商
1、阿里的OSS
2、七牛雲存儲
3、百度雲儲存
三、什么是fastDFS
-----百度百科
圖片來源於百度百科
四、fastdfs安裝:
1、安裝編譯環境
[root@bogon ~]# docker exec -it centos /bin/bash [root@0b5933e7fd96 /]# yum -y groupinstall 'Development Tools' [root@0b5933e7fd96 /]# yum -y install wget
2、下載安裝libfastcommon
[root@0b5933e7fd96 /]#git clone https://github.com/happyfish100/libfastcommon.git [root@0b5933e7fd96 /]#cd libfastcommon/ [root@0b5933e7fd96 /]#./make.sh [root@0b5933e7fd96 /]#./make.sh install
3、下載安裝fastdfs
[root@0b5933e7fd96 /]#wget https://github.com/happyfish100/fastdfs/archive/V5.05.tar.gz [root@0b5933e7fd96 /]#tar -zxvf V5.05.tar.gz [root@0b5933e7fd96 /]#cd fastdfs-5.05/ [root@0b5933e7fd96 /]#./make.sh [root@0b5933e7fd96 /]#./make.sh install
4、修改tracker與storage配置文件命名
備份tracker與storage配置文件,為了防止數據文件的破壞
[root@0b5933e7fd96 /]# find / -name fdfs /etc/fdfs [root@0b5933e7fd96 fdfs]# ll total 40 -rw-r--r--. 1 root root 1461 Jan 13 02:08 client.conf.sample -rw-r--r--. 1 root root 7829 Jan 13 02:08 storage.conf.sample -rw-r--r--. 1 root root 7102 Jan 13 02:08 tracker.conf.sample [root@0b5933e7fd96 fdfs]# cp client.conf.sample client.conf [root@0b5933e7fd96 fdfs]#cp storage.conf.sample storage.conf [root@0b5933e7fd96 fdfs]#cp tracker.conf.sample tracker.conf [root@0b5933e7fd96 fdfs]# ll total 40 -rw-r--r--. 1 root root 1461 Jan 13 02:14 client.conf -rw-r--r--. 1 root root 1461 Jan 13 02:08 client.conf.sample -rw-r--r--. 1 root root 7836 Jan 13 03:30 storage.conf -rw-r--r--. 1 root root 7829 Jan 13 02:08 storage.conf.sample -rw-r--r--. 1 root root 7103 Jan 13 03:28 tracker.conf -rw-r--r--. 1 root root 7102 Jan 13 02:08 tracker.conf.sample
5、配置tracker
[root@0b5933e7fd96 fdfs]# vi tracker.conf
disabled=false port=22122 base_path=/home/fastdfs/tracker http.server_port=8080
7、配置storage
disabled=false group_name=group1 port=23000 base_path=/home/fastdfs/storage
store_path0=/home/fastdfs/storage-data tracker_server=192.168.174.128:22122 http.server_port=8888
8、配置路徑
[root@0b5933e7fd96 home]# ll total 0 drwxr-xr-x. 5 root root 53 Jan 13 03:26 fastdfs [root@0b5933e7fd96 home]# cd fastdfs/ [root@0b5933e7fd96 fastdfs]# ll total 0 drwxr-xr-x. 3 root root 17 Jan 13 03:31 storage drwxr-xr-x. 2 root root 6 Jan 13 03:26 storage-data drwxr-xr-x. 4 root root 28 Jan 13 03:31 tracker
8、啟動tracker和storage
[root@0b5933e7fd96 fdfs]# /usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf restart [root@0b5933e7fd96 fdfs]# /usr/bin/fdfs_storaged /etc/fdfs/storage.conf restart [root@0b5933e7fd96 storage-data]# ps -ef | grep fdfs
五、構建JAVA Client API
github訪問地址:https://github.com/happyfish100/fastdfs-client-java
搭建環境
1、創建一個maven工程
2、添加依賴
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>
<groupId>cn.ibo</groupId>
<artifactId>fastdfs</artifactId>
<version>1.0-SNAPSHOT</version>
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>
3、新增配置文件fastdfs-client.properties
## fastdfs-client.properties fastdfs.connect_timeout_in_seconds = 5 fastdfs.network_timeout_in_seconds = 30 fastdfs.charset = UTF-8 fastdfs.http_anti_steal_token = false fastdfs.http_secret_key = FastDFS1234567890 fastdfs.http_tracker_http_port = 80 fastdfs.tracker_servers = 192.168.174.128:22122
4、編寫測試類
4.1) 上傳
需求:將window 盤符下的指定文件通過fastFDS上傳到linux指定目錄下
/** * Created by 佳先森 on 2019/1/13. * 在此文件中通過fastDSF的client代碼訪問tracker和storage * 通過client的api代碼訪問tracker和storage,他們中間走的socket協議 */ public class TestFastDFS { // 測試文件上傳 @Test public void testUpload(){ //通過fastDSF的client代碼訪問tracker和storage String local_filename = "77.jpg";//上傳文件名 try { //加載fastDFS客戶端的配置文件 ClientGlobal.initByProperties("config/fastdfs-client.properties"); System.out.println("network_timeout=" + ClientGlobal.g_network_timeout + "ms"); System.out.println("charset=" + ClientGlobal.g_charset); //創建tracker的客戶端 TrackerClient tracker = new TrackerClient(); TrackerServer trackerServer = tracker.getConnection(); StorageServer storageServer = null; //定義storage的客戶端 StorageClient1 client = new StorageClient1(trackerServer, storageServer); //文件元數據(如文件名稱大小等) NameValuePair[] metaList = new NameValuePair[1]; metaList[0] = new NameValuePair("fileName",local_filename);//這是個數組,可以繼續添加 //執行上傳 String fileId = client.upload_file1("E:\\壁紙\\77.jpg", "jpg", metaList); System.out.println("upload success. file id is: " + fileId); //關閉trackerServer的連接 trackerServer.close(); } catch (Exception ex) { ex.printStackTrace(); } } }
驗證:此時在storage-data目錄下可以看到上傳的文件
[root@bogon 00]# pwd /home/fastdfs/storage-data/data/00/00 [root@bogon 00]# ll -rw-r--r--. 1 root root 55310 1月 13 21:15 wKiugFw7OeSAMlI9AADYDjkgjlY905.jpg
4.2) 檢索
根據控制台打印的信息,利用文件ID檢索文件信息
//查詢上傳的文件 @Test public void testSearch(){ try { //加載fastDFS客戶端的配置文件 ClientGlobal.initByProperties("config/fastdfs-client.properties"); System.out.println("network_timeout=" + ClientGlobal.g_network_timeout + "ms"); System.out.println("charset=" + ClientGlobal.g_charset); //創建tracker的客戶端 TrackerClient tracker = new TrackerClient(); TrackerServer trackerServer = tracker.getConnection(); StorageServer storageServer = null; //定義storage的客戶端 StorageClient1 client = new StorageClient1(trackerServer, storageServer); FileInfo group1 = client.query_file_info("group1", "M00/00/00/wKiugFw7OeSAMlI9AADYDjkgjlY905.jpg"); FileInfo fileInfo = client.query_file_info1("group1/M00/00/00/wKiugFw7OeSAMlI9AADYDjkgjlY905.jpg"); System.out.println(group1); System.out.println(fileInfo); //查詢文件元信息 NameValuePair[] metadata1 = client.get_metadata1("group1/M00/00/00/wKiugFw7OeSAMlI9AADYDjkgjlY905.jpg"); for(NameValuePair temp:metadata1){ System.out.print(temp.getName()+"\t"+temp.getValue()); } //關閉trackerServer的連接 trackerServer.close(); } catch (Exception ex) { ex.printStackTrace(); } }
4.3) 下載文件
根據文件ID下載文件到指定目錄
@Test public void downLoad(){ try { //加載fastDFS客戶端的配置文件 ClientGlobal.initByProperties("config/fastdfs-client.properties"); System.out.println("network_timeout=" + ClientGlobal.g_network_timeout + "ms"); System.out.println("charset=" + ClientGlobal.g_charset); //創建tracker的客戶端 TrackerClient tracker = new TrackerClient(); TrackerServer trackerServer = tracker.getConnection(); StorageServer storageServer = null; //定義storage的客戶端 StorageClient1 client = new StorageClient1(trackerServer, storageServer); //下載 byte[] bytes = client.download_file1("group1/M00/00/00/wKiugFw7OeSAMlI9AADYDjkgjlY905.jpg"); File file = new File("e:/a.jpg"); FileOutputStream fos = new FileOutputStream(file); fos.write(bytes); fos.close(); //關閉trackerServer的連接 trackerServer.close(); } catch (Exception ex) { ex.printStackTrace(); } }
六、文件服務案列
先將文件存放到臨時目錄,再通過tracker server將臨時文件上傳到fastdfs中的storage中
注意:得先安裝fastdfs-nginx-module,再安裝nginx,在編譯nginx過程中添加fastdfs-nginx-module,順序弄反,會報
[emerg] unknown directive "ngx_fastdfs_module" ngix整合fastdfs啟動后報錯
FastDFS通過Tracker服務器,將文件放在Storage服務器存儲,但是同組存儲服務器之間需要進入文件復制,有同步延遲的問題。假設Tracker服務器將文件上傳到了192.168.4.125,上傳成功后文件ID已經返回給客戶端。此時FastDFS存儲集群機制會將這個文件同步到同組存儲192.168.4.126,在文件還沒有復制完成的情況下,客戶端如果用這個文件ID在192.168.4.126上取文件,就會出現文件無法訪問的錯誤。而fastdfs-nginx-module可以重定向文件連接到源服務器取文件,避免客戶端由於復制延遲導致的文件無法訪問錯誤。
1、安裝fastdfs-nginx-module
參考博客:https://blog.csdn.net/u010098331/article/details/51646921
[root@bogon local]# wget https://sourceforge.net/projects/fastdfs/files/FastDFS%20Nginx%20Module%20Source%20Code/fastdfs-nginx-module_v1.16.tar.gz [root@bogon local]# tar xf fastdfs-nginx-module_v1.16.tar.gz [root@bogon local]# cd fastdfs-nginx-module/src/ [root@bogon local]# vim config #編輯config文件,執行如下命令進行批量替換並保存退出,目的:去掉local前綴,指定正確路徑目錄 :%s+/usr/local/+/usr/+g
[root@bogon src]# pwd /usr/local/fastdfs-nginx-module/src [root@bogon src]# cp mod_fastdfs.conf /etc/fdfs [root@bogon local]# cd /etc/fdfs [root@bogon fdfs]# vim mod_fastdfs.conf
#修改內容如下: connect_timeout=10 base_path=/tmp(默認為/tmp) tracker_server=192.168.174.128:22122 storage_server_port=23000(默認配置為23000) url_have_group_name = true store_path0=/home/fastdfs/storage-data group_name=group1(默認配置為group1)
[root@bogon fastdfs-5.05]# pwd /usr/java/fastdfs-5.05 [root@bogon fastdfs-5.05]# cd conf [root@bogon conf]# cp http.conf /etc/fdfs [root@bogon conf]# cp mime.types /etc/fdfs
安裝nginx
[root@bogon local]# tar -zxvf nginx-1.6.0 [root@bogon local]# cd nginx-1.6.0/ [root@bogon nginx-1.6.0]# ./configure --prefix=/usr/local/nginx --add-module=/usr/local/fastdfs-nginx-module/src [root@bogon nginx-1.6.0]#make && make install
[root@bogon local]# cd nginx
[root@bogon nginx]# cd conf
[root@bogon conf]# vim nginx.conf
location /group1/M00 { root /home/fastdfs/storage-data; ngx_fastdfs_module; }
[root@bogon conf]# cd .. [root@bogon nginx]# cd sbin/ [root@bogon sbin]# ./nginx ngx_http_fastdfs_set pid=11005
配置nginx:
server { listen 80; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location /group1/M00 { root /home/fastdfs/storage-data/data; #storage_path0 ngx_fastdfs_module; #虛擬模塊 } }
此時根據剛剛測試類中上傳的文件利用nginx訪問:如果nginx能夠正常訪問表示配置nginx成功
這是通過http的方式(集成nginx)訪問上傳圖片
七、搭建文件管理服務
文件管理服務提供通過http方式上傳文件、刪除文件、查詢文件的功能,管理員通過文件管理服務隊文件服務器上的文件進行管理
文件管理服務采用Spring Boot開發,文件管理服務通過與fastDFS交互最終將用戶上傳的文件存儲到fastDFS上
1、建立實體類
/** * Created by 佳先森 on 2019/1/13. */ public class FileSystem { private String fileId; private String filePath; private long fileSize; private String fileName; private String fileType; //省去set/get }
2、編寫配置文件application.yml
server: port: 22100 fastdfs: #文件上傳臨時目錄 upload_location: E:\\download\\
3、建立controller
/** * Created by 佳先森 on 2019/1/13. */ @RestController @RequestMapping("/filesystem") public class FileServerController { @Value("${fastdfs.upload_location}") //讀取配置文件 private String upload_location; @PostMapping("/upload") @ResponseBody public FileSystem upload(@RequestParam("file") MultipartFile file) throws IOException { //先將文件存儲在web服務器上(本機),再使用fastDFS的client將文件到fastDFS服務器 FileSystem fileSystem = new FileSystem(); //得到文件的原始名稱 String originalFilename = file.getOriginalFilename(); String extention = originalFilename.substring(originalFilename.lastIndexOf(".")); //重構上傳文件名 String newFileName = UUID.randomUUID()+extention; //定義file,使用file存儲上傳的文件 File file1 =new File(upload_location+newFileName); file.transferTo(file1); //獲取新上傳文件的物理路徑 String newFilePath = file1.getAbsolutePath(); try { //加載fastDFS客戶端的配置文件 ClientGlobal.initByProperties("config/fastdfs-client.properties"); System.out.println("network_timeout=" + ClientGlobal.g_network_timeout + "ms"); System.out.println("charset=" + ClientGlobal.g_charset); //創建tracker的客戶端 TrackerClient tracker = new TrackerClient(); TrackerServer trackerServer = tracker.getConnection(); StorageServer storageServer = null; //定義storage的客戶端 StorageClient1 client = new StorageClient1(trackerServer, storageServer); //文件元數據(如文件名稱大小等) NameValuePair[] metaList = new NameValuePair[1]; metaList[0] = new NameValuePair("fileName",originalFilename);//這是個數組,可以繼續添加 //執行上傳,將上傳成功的存放在web服務器(本機)上的文件上傳到fastDFS String fileId = client.upload_file1(newFilePath, extention, metaList); System.out.println("upload success. file id is: " + fileId); fileSystem.setFileId(fileId); fileSystem.setFilePath(fileId); fileSystem.setFileName(originalFilename); //通過調用service即dao將文件路徑存儲到數據庫中 //....todo //關閉trackerServer的連接 trackerServer.close(); } catch (Exception ex) { ex.printStackTrace(); } return fileSystem; } }
4、編寫啟動類
/** * Created by 佳先森 on 2019/1/13. */ @SpringBootApplication public class FileServiceApplication { public static void main(String[] args) { SpringApplication.run(FileServiceApplication.class); } }
5、編寫前端界面
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> <title>Upload</title> </head> <!-- 引入樣式 --> <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"> <body> <div id="clj"> <el-upload action="" list-type="picture-card" :on-preview="handlePictureCardPreview" :on-remove="handleRemove"> <i class="el-icon-plus"></i> </el-upload> <el-dialog :visible.sync="dialogVisible"> <img width="100%" :src="dialogImageUrl" alt=""> </el-dialog> </div> <script type="text/javascript" src="vue.min.js"></script> <!-- 引入組件庫 --> <script src="https://unpkg.com/element-ui/lib/index.js"></script> <script> new Vue({ el: '#clj', data: { dialogVisible: false, dialogImageUrl: '' },
methods: {
handlePictureCardPreview(file){ #預覽
this.dialogImageUrl ="http://192.168.174.128/"+file.response.filePath #注意:預覽時訪問的圖片地址是通過nginx代理的,需要配置圖片所在宿主機地址
this.dialogVisible = true;
},
handleRemove(){}
}
})
</script>
</body>
</html>
6、nginx中配置頁面訪問路徑
#圖片服務測試(圖片頁面) server { listen 7283; server_name localhost; location / { root /usr/java/javademo/springboot-vue-fastdfs/upload/; index index.html; } }
此時訪問http://192.168.174.128:7283/upload.html,若成功訪問到頁面,則配置成功
但是這樣遠遠不夠的,因為頁面中的action配置的請求后台的action是訪問不到的,存在跨域問題(頁面與后端代碼不在同一台宿主機上),解決跨域問題常見的思路就是在nginx中配置代理
#圖片服務測試(圖片頁面) server { listen 7283; server_name localhost; #這里配置的是頁面所在宿主機ip location / { root /usr/java/javademo/springboot-vue-fastdfs/upload/; index index.html; } location ^~ /filesystem/ { #配置代理 proxy_pass http://192.168.2.174:22100/filesystem/; #注意這里配置的是后端代碼所在宿主機ip } }
此時上傳和預覽功能已經完成
克隆源碼地址:git@gitee.com:MR_JiaXianSen/FastDFS.git