在前篇 高可用高性能分布式文件系統(tǒng)FastDFS進(jìn)階keepalived+nginx對多tracker進(jìn)行高可用熱備 中已介紹搭建高可用的分布式文件系統(tǒng)架構(gòu)。
廣東ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場景,ssl證書未來市場廣闊!成為創(chuàng)新互聯(lián)的ssl證書銷售渠道,可以享受市場價格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18982081108(備注:SSL證書合作)期待與您的合作!
那怎么在程序中調(diào)用,其實(shí)網(wǎng)上有很多栗子,這里在他們的基礎(chǔ)上作個簡單的介紹。
官網(wǎng)Java客戶端源代碼:https://github.com/happyfish200/fastdfs-client-java
打開源碼后 執(zhí)行maven install 將代碼打成jar到本地maven倉庫(這步可自行 google)
然后創(chuàng)建一個Demo工程,這里采用spring mvc模式創(chuàng)建。
org.csource fastdfs-client-java 1.27-SNAPSHOT
在resources的properties文件夾中創(chuàng)建配置文件
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 = 10.0.11.201:22122,10.0.11.202:22122,10.0.11.203:22122
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); } /** * 上傳文件方法 *Title: uploadFile
*Description:
* * @param fileName 文件全路徑 * @param extName 文件擴(kuò)展名,不包含(.) * @param metas 文件擴(kuò)展信息 * @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 文件的擴(kuò)展名 如 txt jpg等 * @return null為失敗 */ public String uploadFile(String fileName, String extName) { return uploadFile(fileName, extName, null); } /** * 上傳文件方法 *Title: uploadFile
*Description:
* * @param fileContent 文件的內(nèi)容,字節(jié)數(shù)組 * @param extName 文件擴(kuò)展名 * @param metas 文件擴(kuò)展信息 * @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 文件的字節(jié)數(shù)組 * @return null為失敗 * @throws Exception */ public String uploadFile(byte[] fileContent) throws Exception { return uploadFile(fileContent, null, null); } /** * 上傳文件 * * @param fileContent 文件的字節(jié)數(shù)組 * @param extName 文件的擴(kuò)展名 如 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; } /** * 獲取文件數(shù)組 * * @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客戶端文件上傳、下載、刪除和元數(shù)據(jù)獲取測試:
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 { /** * 文件上傳測試 */ @Test public void testUpload() { File file = new File("C:\\Users\\James\\Pic\\share.jpg"); MapmetaList = new HashMap (); 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 } /** * 文件下載測試 */ @Test public 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 ? "下載成功" : "下載失敗"); } /** * 文件刪除測試 */ @Test public void testDelete() { int r = FastDFSClientUtils.delete_file("group1/M00/00/00/wKgAyVgFk9aAB8hwAA-8Q6_7tHw351.jpg"); System.out.println(r == 0 ? "刪除成功" : "刪除失敗"); } }
如果沒有什么問題將會看到打印的日志。
net版本可參考另外一位網(wǎng)友代碼:
https://github.com/huanzui/fastdfs.client.net
現(xiàn)在分布式文件平臺已經(jīng)完成了搭建和代碼測試,但實(shí)踐過程中還是有幾個問題:
1、上傳到平臺的文件名都是無規(guī)律的64base編碼過的字符串,因此如果只作為如圖片等文件存儲是沒有問題的,因為我們不關(guān)心其文件名,但如果作為要下載的內(nèi)容,如附件,或安裝包,下載時如果還是編碼那無法直觀的知道此文件是做什么的,是要轉(zhuǎn)換為正確的文件名。
解決:關(guān)于這個問題,網(wǎng)上有方法是通過nginx,利用域名和FID拼出url,然后在url后面增加一個參數(shù),指定原始文件名。
例如:http://121.14.161.48:9030/group2/M00/00/89/eQ6h4FKJf_PRl8p4AUz4wO8tqaA688.apk?attname=filename.apk
在Nginx上進(jìn)行如下配置,這樣Nginx就會截獲url中的參數(shù)attname,在Http響應(yīng)頭里面加上字段 Content-Disposition “attachment;filename=$arg_attname”。
這里只提供了一個方案,具體內(nèi)容其實(shí)還需要一個篇幅來介紹,有時間再寫吧。
2、實(shí)際用的時候我們其實(shí)是想按業(yè)務(wù)還將不同文件放在不同的文件夾中的,比如聊天文件,文檔文件,還有臨時文件 有時需要定時清理的,但分布式文件平臺是沒法指定文件夾的。
解決:最常用的做法是自己實(shí)現(xiàn)一個文件對應(yīng)庫,將上傳的文件名,時間,對應(yīng)的業(yè)務(wù)等信息與最終的文件路徑對應(yīng)起來,這樣就可以作任何邏輯了,但缺點(diǎn)是非常麻煩。
FastDFS沒有看到保存到指定的2級目錄的API,但可以保存到指定的group,可以指定某個group為哪個業(yè)務(wù)用。但這樣會破壞整個FastDFS的分布式結(jié)構(gòu),造成某個group非常巨大,而且不容易擴(kuò)容。實(shí)際使用時還會有其它業(yè)務(wù)的內(nèi)容進(jìn)入到此group。
3、大文件如何斷點(diǎn)續(xù)傳?
如果文件大于100M,則需要斷點(diǎn)續(xù)傳的功能了,F(xiàn)astDFS對于大文件來說是有點(diǎn)吃力的,但還是可以實(shí)現(xiàn),根據(jù)網(wǎng)友提供的方案來看就是需要客戶進(jìn)行切片上傳,并且切片字節(jié)大小小于等于storage配置的buff_size,默認(rèn)是256k,這一塊需要自己實(shí)現(xiàn)。