溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

高可用高性能分布式文件系統(tǒng)FastDFS實(shí)踐Java程序

發(fā)布時(shí)間:2020-06-10 23:38:22 來(lái)源:網(wǎng)絡(luò) 閱讀:8116 作者:歡醉 欄目:大數(shù)據(jù)

  在前篇 高可用高性能分布式文件系統(tǒng)FastDFS進(jìn)階keepalived+nginx對(duì)多tracker進(jìn)行高可用熱備 中已介紹搭建高可用的分布式文件系統(tǒng)架構(gòu)。

  那怎么在程序中調(diào)用,其實(shí)網(wǎng)上有很多栗子,這里在他們的基礎(chǔ)上作個(gè)簡(jiǎn)單的介紹。

下載源碼并加入本地倉(cāng)庫(kù)

官網(wǎng)Java客戶端源代碼:https://github.com/happyfish200/fastdfs-client-java  

打開源碼后 執(zhí)行maven install 將代碼打成jar到本地maven倉(cāng)庫(kù)(這步可自行 google)

示例源碼

然后創(chuàng)建一個(gè)Demo工程,這里采用spring mvc模式創(chuàng)建。

在pom中加入引用 maven中依賴jar包

<!-- fastdfs上傳下載圖片 路徑和上面的pom中對(duì)應(yīng) -->

<dependency>
    <groupId>org.csource</groupId>
    <artifactId>fastdfs-client-java</artifactId>
    <version>1.27-SNAPSHOT</version>
</dependency>

fastdfs-client.properties

在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

 

 創(chuàng)建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  文件擴(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);
    }    
    /**
     * 上傳文件方法
     * <p>Title: uploadFile</p>
     * <p>Description: </p>
     *
     * @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ù)獲取測(cè)試:

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 {
    /**
     * 文件上傳測(cè)試
     */
    @Test 
       public 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
        }
     /**
     * 文件下載測(cè)試
     */
    @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 ? "下載成功" : "下載失敗");
    }
     /**
     * 文件刪除測(cè)試
     */
    @Test
        public void testDelete() {
            int r = FastDFSClientUtils.delete_file("group1/M00/00/00/wKgAyVgFk9aAB8hwAA-8Q6_7tHw351.jpg");
        System.out.println(r == 0 ? "刪除成功" : "刪除失敗");
    }
}

 

如果沒有什么問題將會(huì)看到打印的日志。

Net版本

net版本可參考另外一位網(wǎng)友代碼:

https://github.com/huanzui/fastdfs.client.net

問題

現(xiàn)在分布式文件平臺(tái)已經(jīng)完成了搭建和代碼測(cè)試,但實(shí)踐過程中還是有幾個(gè)問題:

1、上傳到平臺(tái)的文件名都是無(wú)規(guī)律的64base編碼過的字符串,因此如果只作為如圖片等文件存儲(chǔ)是沒有問題的,因?yàn)槲覀儾魂P(guān)心其文件名,但如果作為要下載的內(nèi)容,如附件,或安裝包,下載時(shí)如果還是編碼那無(wú)法直觀的知道此文件是做什么的,是要轉(zhuǎn)換為正確的文件名。

解決:關(guān)于這個(gè)問題,網(wǎng)上有方法是通過nginx,利用域名和FID拼出url,然后在url后面增加一個(gè)參數(shù),指定原始文件名。

例如:http://121.14.161.48:9030/group2/M00/00/89/eQ6h4FKJf_PRl8p4AUz4wO8tqaA688.apk?attname=filename.apk

在Nginx上進(jìn)行如下配置,這樣Nginx就會(huì)截獲url中的參數(shù)attname,在Http響應(yīng)頭里面加上字段 Content-Disposition “attachment;filename=$arg_attname”。

這里只提供了一個(gè)方案,具體內(nèi)容其實(shí)還需要一個(gè)篇幅來(lái)介紹,有時(shí)間再寫吧。

2、實(shí)際用的時(shí)候我們其實(shí)是想按業(yè)務(wù)還將不同文件放在不同的文件夾中的,比如聊天文件,文檔文件,還有臨時(shí)文件 有時(shí)需要定時(shí)清理的,但分布式文件平臺(tái)是沒法指定文件夾的。

解決:最常用的做法是自己實(shí)現(xiàn)一個(gè)文件對(duì)應(yīng)庫(kù),將上傳的文件名,時(shí)間,對(duì)應(yīng)的業(yè)務(wù)等信息與最終的文件路徑對(duì)應(yīng)起來(lái),這樣就可以作任何邏輯了,但缺點(diǎn)是非常麻煩。

FastDFS沒有看到保存到指定的2級(jí)目錄的API,但可以保存到指定的group,可以指定某個(gè)group為哪個(gè)業(yè)務(wù)用。但這樣會(huì)破壞整個(gè)FastDFS的分布式結(jié)構(gòu),造成某個(gè)group非常巨大,而且不容易擴(kuò)容。實(shí)際使用時(shí)還會(huì)有其它業(yè)務(wù)的內(nèi)容進(jìn)入到此group。

3、大文件如何斷點(diǎn)續(xù)傳?

如果文件大于100M,則需要斷點(diǎn)續(xù)傳的功能了,F(xiàn)astDFS對(duì)于大文件來(lái)說是有點(diǎn)吃力的,但還是可以實(shí)現(xiàn),根據(jù)網(wǎng)友提供的方案來(lái)看就是需要客戶進(jìn)行切片上傳,并且切片字節(jié)大小小于等于storage配置的buff_size,默認(rèn)是256k,這一塊需要自己實(shí)現(xiàn)。

 


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI