高可用高性能分布式文件系统FastDFS实践Java程序
在前篇?高可用高性能分布式文件系統FastDFS進階keepalived+nginx對多tracker進行高可用熱備?中已介紹搭建高可用的分布式文件系統架構。
那怎么在程序中調用,其實網上有很多栗子,這里在他們的基礎上作個簡單的介紹。
下載源碼并加入本地倉庫
官網Java客戶端源代碼:https://github.com/happyfish100/fastdfs-client-java ?
打開源碼后?執行maven install 將代碼打成jar到本地maven倉庫(這步可自行 google)
示例源碼
然后創建一個Demo工程,這里采用spring mvc模式創建。
在pom中加入引用?maven中依賴jar包
<!-- fastdfs上傳下載圖片 路徑和上面的pom中對應 -->
<dependency><groupId>org.csource</groupId><artifactId>fastdfs-client-java</artifactId><version>1.27-SNAPSHOT</version> </dependency>fastdfs-client.properties
在resources的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 = 80fastdfs.tracker_servers = 10.0.11.201:22122,10.0.11.202:22122,10.0.11.203:22122?
?創建FastDFSClient工具類
package com.james.utils;import org.csource.common.MyException; import org.csource.common.NameValuePair; import org.csource.fastdfs.*;import java.io.BufferedOutputStream; import java.io.IOException; import java.net.URLDecoder;/*** Created by James on 2015/11/14.* FastDFS文件上傳*/ public class FastDFSClientUtils {private TrackerClient trackerClient = null;private TrackerServer trackerServer = null;private StorageServer storageServer = null;private StorageClient1 storageClient = null;public FastDFSClientUtils(String conf) throws Exception {if (conf.contains("classpath:")) {String path = this.getClass().getResource("/").getPath();conf = conf.replace("classpath:", URLDecoder.decode(path, "UTF-8"));}ClientGlobal.init(conf);trackerClient = new TrackerClient();trackerServer = trackerClient.getConnection();storageServer = null;storageClient = new StorageClient1(trackerServer, storageServer);}/*** 上傳文件方法* <p>Title: uploadFile</p>* <p>Description: </p>** @param fileName 文件全路徑* @param extName 文件擴展名,不包含(.)* @param metas 文件擴展信息* @return* @throws Exception*/public String uploadFile(String fileName, String extName, NameValuePair[] metas) {String result = null;try {result = storageClient.upload_file1(fileName, extName, metas);} catch (IOException e) {e.printStackTrace();} catch (MyException e) {e.printStackTrace();}return result;}/*** 上傳文件,傳fileName** @param fileName 文件的磁盤路徑名稱 如:D:/image/aaa.jpg* @return null為失敗*/public String uploadFile(String fileName) {return uploadFile(fileName, null, null);}/*** @param fileName 文件的磁盤路徑名稱 如:D:/image/aaa.jpg* @param extName 文件的擴展名 如 txt jpg等* @return null為失敗*/public String uploadFile(String fileName, String extName) {return uploadFile(fileName, extName, null);}/*** 上傳文件方法* <p>Title: uploadFile</p>* <p>Description: </p>** @param fileContent 文件的內容,字節數組* @param extName 文件擴展名* @param metas 文件擴展信息* @return* @throws Exception*/public String uploadFile(byte[] fileContent, String extName, NameValuePair[] metas) {String result = null;try {result = storageClient.upload_file1(fileContent, extName, metas);} catch (IOException e) {e.printStackTrace();} catch (MyException e) {e.printStackTrace();}return result;}/*** 上傳文件** @param fileContent 文件的字節數組* @return null為失敗* @throws Exception*/public String uploadFile(byte[] fileContent) throws Exception {return uploadFile(fileContent, null, null);}/*** 上傳文件** @param fileContent 文件的字節數組* @param extName 文件的擴展名 如 txt jpg png 等* @return null為失敗*/public String uploadFile(byte[] fileContent, String extName) {return uploadFile(fileContent, extName, null);}/*** 文件下載到磁盤** @param path 圖片路徑* @param output 輸出流 中包含要輸出到磁盤的路徑* @return -1失敗,0成功*/public int download_file(String path, BufferedOutputStream output) {//byte[] b = storageClient.download_file(group, path);int result = -1;try {byte[] b = storageClient.download_file1(path);try {if (b != null) {output.write(b);result = 0;}} catch (Exception e) {} //用戶可能取消了下載finally {if (output != null)try {output.close();} catch (IOException e) {e.printStackTrace();}}} catch (Exception e) {e.printStackTrace();}return result;}/*** 獲取文件數組** @param path 文件的路徑 如group1/M00/00/00/wKgRsVjtwpSAXGwkAAAweEAzRjw471.jpg* @return*/public byte[] download_bytes(String path) {byte[] b = null;try {b = storageClient.download_file1(path);} catch (IOException e) {e.printStackTrace();} catch (MyException e) {e.printStackTrace();}return b;}/*** 刪除文件** @param group 組名 如:group1* @param storagePath 不帶組名的路徑名稱 如:M00/00/00/wKgRsVjtwpSAXGwkAAAweEAzRjw471.jpg* @return -1失敗,0成功*/public Integer delete_file(String group, String storagePath) {int result = -1;try {result = storageClient.delete_file(group, storagePath);} catch (IOException e) {e.printStackTrace();} catch (MyException e) {e.printStackTrace();}return result;}/*** @param storagePath 文件的全部路徑 如:group1/M00/00/00/wKgRsVjtwpSAXGwkAAAweEAzRjw471.jpg* @return -1失敗,0成功* @throws IOException* @throws Exception*/public Integer delete_file(String storagePath) {int result = -1;try {result = storageClient.delete_file1(storagePath);} catch (IOException e) {e.printStackTrace();} catch (MyException e) {e.printStackTrace();}return result;} }Java客戶端文件上傳、下載、刪除和元數據獲取測試:
package com.james.fdfs; import org.junit.Test; import java.io.File; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import ?com.james.utils.FastDFSClientUtils;public class FastDFSClientUtilsTest {/*** 文件上傳測試*/@Testpublic void testUpload() {File file = new File("C:\\Users\\James\\Pic\\share.jpg");Map<String,String> metaList = new HashMap<String, String>();metaList.put("width","1024");metaList.put("height","768");String fid = FastDFSClientUtils.uploadFile(file,file.getName(),metaList);System.out.println("upload local file " + file.getPath() + " ok, fileid=" + fid);//上傳成功返回的文件ID: group1/M00/00/00/wKgAyVgFk9aAB8hwAA-8Q6_7tHw351.jpg}/*** 文件下載測試*/@Testpublic void testDownload() {int r = FastDFSClientUtils.download_file("group1/M00/00/00/wKgAyVgFk9aAB8hwAA-8Q6_7tHw351.jpg", new File("DownloadFile_fid.jpg"));System.out.println(r == 0 ? "下載成功" : "下載失敗");}/*** 文件刪除測試*/@Testpublic void testDelete() {int r = FastDFSClientUtils.delete_file("group1/M00/00/00/wKgAyVgFk9aAB8hwAA-8Q6_7tHw351.jpg");System.out.println(r == 0 ? "刪除成功" : "刪除失敗");} }?
如果沒有什么問題將會看到打印的日志。
Net版本
net版本可參考另外一位網友代碼:
https://github.com/huanzui/fastdfs.client.net
問題
現在分布式文件平臺已經完成了搭建和代碼測試,但實踐過程中還是有幾個問題:
1、上傳到平臺的文件名都是無規律的64base編碼過的字符串,因此如果只作為如圖片等文件存儲是沒有問題的,因為我們不關心其文件名,但如果作為要下載的內容,如附件,或安裝包,下載時如果還是編碼那無法直觀的知道此文件是做什么的,是要轉換為正確的文件名。
解決:關于這個問題,網上有方法是通過nginx,利用域名和FID拼出url,然后在url后面增加一個參數,指定原始文件名。
例如:http://121.14.161.48:9030/group2/M00/00/89/eQ6h3FKJf_PRl8p4AUz4wO8tqaA688.apk?attname=filename.apk
在Nginx上進行如下配置,這樣Nginx就會截獲url中的參數attname,在Http響應頭里面加上字段 Content-Disposition “attachment;filename=$arg_attname”。
這里只提供了一個方案,具體內容其實還需要一個篇幅來介紹,有時間再寫吧。
2、實際用的時候我們其實是想按業務還將不同文件放在不同的文件夾中的,比如聊天文件,文檔文件,還有臨時文件 有時需要定時清理的,但分布式文件平臺是沒法指定文件夾的。
解決:最常用的做法是自己實現一個文件對應庫,將上傳的文件名,時間,對應的業務等信息與最終的文件路徑對應起來,這樣就可以作任何邏輯了,但缺點是非常麻煩。
FastDFS沒有看到保存到指定的2級目錄的API,但可以保存到指定的group,可以指定某個group為哪個業務用。但這樣會破壞整個FastDFS的分布式結構,造成某個group非常巨大,而且不容易擴容。實際使用時還會有其它業務的內容進入到此group。
3、大文件如何斷點續傳?
如果文件大于100M,則需要斷點續傳的功能了,FastDFS對于大文件來說是有點吃力的,但還是可以實現,根據網友提供的方案來看就是需要客戶進行切片上傳,并且切片字節大小小于等于storage配置的buff_size,默認是256k,這一塊需要自己實現。
?
總結
以上是生活随笔為你收集整理的高可用高性能分布式文件系统FastDFS实践Java程序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CCNA--OSI、TCP/IP
- 下一篇: windows reload()