FastDFS 實際開發中的應用(上傳、下載、刪除文件)

實際開發時 應用場景 :

(1)用戶的頭像 可以通過我們的 fastdfs 來存放進我們的 磁盤當中。

(2)我們需要看到 頭像 怎麼辦呢: 頭像其實就是img 標籤,然後就可以寫一個地址,然後讓nginx來接收,訪問上傳的頭像。

(3)客戶端jar 就是一個maven項目,我們直接將它clean 然後 install 打包進我們的maven倉庫中,在以後的 編寫代碼的時候字節通過maven引入依賴就行了。

(4)Maven工程找配置文件 是 自動去 resources中去找的。

上傳文件的 五步固定套路:

上面這句代碼會返回一個數組,數組中的兩個返回值爲:如下圖:

我們將meta_list設置爲null,fastDFS就不會爲我們生成屬性文件,也就是以-m結尾的文件。

在實際開發中我們是不需要生成-m屬性文件的,因爲佔有效存儲空間。

設置爲 null 就不生成meta屬性文件(-m)。

上傳 ,下載,刪除 文件 的 操作 :

下面兩個條件 是用來配置 通過fastDFS存放文件的位置:

圓圈1 : 配置 當前的 組中 有幾個存放 文件的位置。

圓圈2 : 配置這幾個存放文件位置的路徑。 如上圖的 path0 路徑,當我們運行fastDFS之後 ,fastDFS會爲我們在 files目錄下 創建一個 data 目錄,這個data目錄下 有256個目錄,這 256 個目錄 每個目錄下又有256個目錄,這256個目錄纔是真正存放文件的地方,這樣子就形成了256*256的文件存放路徑。

上面兩行配置是用來幹什麼的?

上圖 【兩行配置】 只是用來配置在【組】中可以用來【存放文件】的【位置】的。 我們組數還是當前這個組,不會因爲上面兩行配置的配置而增加或者減少,增加或者減少的只是 組中 可以存放文件的位置而已。

什麼是組(group)?

組這個詞出現在 fastDFS給我們返回的訪問路徑中。【組】其實就是許多臺服務器的集合,有一個專門用來存放文件的服務器,還有一個專門用來備份的服務器,所以說 一個組 中 最少有兩臺服務器,一個用來存取文件,一個用來備份文件的。

所以說: 組就是許多臺服務器的集合 。

.conf  就是配置文件 :

.conf 是config的簡寫,也就是配置文件,多用於存取硬件驅動程序的安裝配置信息。
內容一般是一些硬件的版本號呀,支持什麼樣的系統等信息。
本質上來說就是TXT文件,裏面的格式沒有統一標準,各個廠商的格式不同內容也完全不一樣。

我們在配置文件中配置的是:

我們 java程序是通過jar包來連接服務端的 tracker ,然後我們的tracker又連接上了storage。

也就是說: 客戶端的 java程序 是連接 tracker,服務端存放文件的storage也是連接tracker ,所以我們需要在配置文件中 配置 連接tracker的配置信息。

投資人 擁有債權,債權的意思就是投資人擁有催債的權利。一般 債權 的底下就 對應一筆借款。

擴展知識:

爲什麼需要在JDBC中添加 useSSL ?

用的是java-connector-5.1.42-bin.jar

當然結果是對的,但是上面一行說的什麼useSSl沒有設置,百度了一下,是這樣的。

冷靜分析:主要是我的jar包版本過高造成的。用以前的舊版本沒什麼問題,而且新版本的MySQL要求是否進行ssl連接。

  解決方法:這樣寫就可以了  String url = "jdbc:mysql://localhost:3306/school?useSSL=false";   

 

拓展:conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/Test_db?useUnicode=true&characterEncoding=utf-8&useSSL=false","root","password");

useUnicode設置主要是設置字符編碼爲utf-8,也可以在eclipse中設置默認的字符編碼。

只要我們提交的表單中有附件的話,那麼就需要添加一個屬性:enctype。

只有表單中需要提交附件,那麼就需要添加enctype這個屬性。

這是一個名字叫做filename的 附件參數 ,注意是一個附件參數。

服務端控制層 應該怎樣接收這個附件參數呢?

使用MultipartFile 這個對象來進行接收。 這個multipartfile類是我們的spring-boot框架爲我們提供的,專門用來接收 【附件參數】的。

————————————————————————————————————————————————

我們的文件是通過什麼樣的形式 在 網絡中進行傳輸的呢?

如果我們的文件需要在網絡中進行傳輸,我們一般的做法是 將 文件 轉換成 字節數組 進行傳輸。

<iframe>  和 <script> 標籤 要以什麼形式來結束標籤 ?

<iframe> 和 <script>這兩個標籤 都要以</iframe>和</script> 這樣來結束,千萬不要以/斜槓這種形式來結束標籤。

Form標籤中的 target屬性:

target 屬性的意思是: form 表單 的 請求 從哪裏進行提交。 可以在當前頁進行提交,或者是新開一個新的頁面進行提交,再或者從 iframe框架中進行提交。

我們有時候需要讓我們的<form>表單 從 <iframe>內嵌頁面中進行提交?

我們這樣做的目的是爲了【模擬ajax異步請求】,從而實現刷新。這種局部刷新的方法 在 很久以前沒有ajax的時候 用來實現 異步請求局部刷新。

<form action=”/xxx/xxx/xxxx”  method=”post”  target=”target_iframe”>

</form>

<iframe name=”target_iframe”>

form表單會從 這個iframe中進行提交。

後端返回的 json 或 text 都會返回到這裏,因爲表單form中的請求 是從這裏發出的。所以最終 後端 推送的 結果也會 推送到這個 iframe內嵌頁面中 。

</iframe>

 

容易理解錯的知識點:

<script></script>  和 js 代碼。

我們的 js 代碼如果沒有寫在方法中,也就是function 方法()  這裏面,那麼在加載頁面的時候,我們的這些不在 方法中的js代碼 就會 在讀取頁面的時候 執行和加載編譯,也就是 頁面只要加載 就會 執行這些不在方法中的js代碼。如果 我們 將這些 js寫在了方法中,也就是寫在了 function 方法()中時,頁面加載的時候 【不會】爲我們加載編譯和執行 寫在方法中的js代碼,如果我們想要執行:

JS代碼執行的位置:

JS代碼寫在了 方法裏面 :

<script>

function fun1(){

  一坨 JS 代碼 : 巴拉巴拉。

}

fun1();  //頁面加載到這裏 會幫我們執行 js代碼,執行寫在方法中的js代碼。

</script>

JS代碼寫在了 方法裏面:

<script>

function fun1(){

  一坨 JS 代碼 : 巴拉巴拉。 //這段js代碼直接不執行了。

}

</script>

JS代碼寫在了 方法外面:

<script>

 一坨 JS 代碼 : 巴拉巴拉。 //頁面加載到這裏就執行js代碼了。

</script>

爲什麼會出現xml 映射文件沒有編譯到classPath下?

因爲我們idea在進行編譯的時候,resources目錄下idea認爲是配置文件,java目錄下idea認爲是java文件,所以在編譯的時候 resources中只會編譯配置文件,當然也會編譯 我們的 thymeleaf 模板頁面,一位內 我們 templates 目錄是 創建 spring-boot項目的時候自動爲我們創建的,所以會被編譯。 但是我們mapper目錄下的 .xml文件 不是java文件,所以我們的idea不會幫我們 繼續編譯,所以會出現 xml映射文件沒有被變異的情況,所以說,我們需要配置resources,讓我們的 xml映射文件被編譯到我們的 classPath目錄下。

如果xml映射文件沒有編譯到classpath目錄下,我們瀏覽器會報出什麼錯誤:

 【上傳文件】 和 【下載文件】 中 涉及的 文件傳輸 和 文件參數的接收,文件在前端、傳輸中、後端的存在形式。

   (1)我們使用 form表單 將 我們的 選中的文件 提交到後臺:

   代碼: 

有上面 【兩個黃線】  才能正常的用 表單提交我們的附件。

上面這段代碼中,form表單就是從iframe框架中進行提交的,然後最後服務端將處理結果向前端進行推送的時候也是推送到 iframe 框架中,因爲是 iframe框架 發出的 form表單的請求,所以最後也是iframe進行請求的接收。

控制層controller如何接收 請求中的附件參數?

  1. 下載文件:

將我們 通過 fastDFS軟件 獲取到的 文件 推送給我們瀏覽器,然後讓我們的瀏覽器知道 推送過來的是一個附件,而且這個附件存在的形式是 字節流的形式,因爲我們將文件轉換成了  字節數組了,然後也告訴我們瀏覽器一個 我們自定義的這個文件的名稱。 我們 向前端瀏覽器推送 附件的時候,然後讓瀏覽器開始下載該文件,我們需要告訴我們的瀏覽器什麼: 需要告訴我們的瀏覽器:這個數據是 【一個附件】,這個附件存在的形式爲【字節流的形式】,因爲我們之前將我們文件轉換成了【字節數組】,而且我們還要告訴我們的瀏覽器 一個下載文件是 所要使用的文件名稱。 這樣子就可以讓我們的瀏覽器在知道是一個文件之後,自動下載我們的文件。

@Controller
public class UserController {

    @Autowired
    private UserService userService ;

    @GetMapping(value="/fastdfs/downLoad")
    public ResponseEntity<byte[]> downLoad(
            @RequestParam(value="id") Integer id,
            Model model
    ){
        TrackerClient trackerClient = null ;
        TrackerServer trackerServer = null ;
        StorageServer storageServer = null ;
        StorageClient storageClient = null ;

        ResponseEntity<byte[]> responseEntity = null ;
        try{
            ClientGlobal.init("配置文件名");
            trackerClient = new TrackerClient();
            trackerServer = trackerClient.getConnection();
            storageServer = trackerClient.getStoreStorage(trackerServer);
            storageClient = new StorageClient(trackerServer,storageServer);

            //從數據庫中獲取到 當前 id 對應的數據庫的 借款人信息 。
            Creditor creditor = userService.queryByPrimaryKey(id);
            String groupname = creditor.getGroupname();
            String remotefilepath = creditor.getRemotefilepath();
            byte[] fileBytes = storageClient.download_file(groupname, remotefilepath); //文件的字節數組存在形式。
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            String filePath = creditor.getRemotefilepath();
            String suffix = filePath.substring(filePath.indexOf("."));
            headers.setContentDispositionFormData("attachment",System.currentTimeMillis()+suffix);
    responseEntity = new ResponseEntity<byte[]>(fileBytes,headers,HttpStatus.OK);
        }catch(Exception e) {
            e.printStackTrace();
        }finally {
            if(null != trackerServer){
                try {
                    trackerServer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(null != storageServer){
                try {
                    storageServer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return responseEntity;
    }

 

我們 的 contoller控制層嗎,需要給我們的前臺的瀏覽器傳遞一個 【文件類型數據】,這個文件類型的數據,和平時向前段推送的 json ,string ,view 等等類型數據都不一樣,因爲這是向 前臺瀏覽器推送 一個 文件,當我們瀏覽器接收到這個 文件類型的數據的時候就會 彈出下載框,進行下載操作。 所以我們需要告訴我們的瀏覽器 我們向他 推送的是一個文件,而且告訴我們的 瀏覽器 我們這個文件的 【存在的形式】 和 【文件的名字】。

這樣我們的瀏覽器就會自動彈出下載框了。

需要告訴我們的 瀏覽器 那些信息:【這個數據是一個文件】,【文件存在的形式:流形式】,【文件的名字】。

 HttpHeaders headers = new HttpHeaders(); 徵稅創建一個請求頭,我們從請求頭中告訴我的瀏覽器  我們 文件的內容 是什麼形式的,因爲我們的文件需要在網絡中進行傳遞所以我們的文件需要轉換成 【字節數組】,所以文件中內容的形式是 流形式,設置如下:
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);

然後 告訴我們的 瀏覽器 這個數據是 一個 文件,和這個文件的名字:

headers.setContentDispositionFormData("attachment",System.currentTimeMillis()+suffix);

這個對象是專門用來 向前臺瀏覽器推送我們的 文件 數據的 :

 responseEntity = new ResponseEntity<byte[]>(fileBytes,headers,HttpStatus.OK);
向前臺推送數據的時候 方法名前面不需要添加 @responsebody,因爲這是推送文件,有點特殊。

 

綜上所述:

上傳文件和下載文件所涉及的 對象 和 方法 :

 

使用form表單,通過參數的形式 將 我們的文件,提交到後臺的控制層:

Form表單標籤中 需要添加 encTyle 屬性:

 enctype="multipart/form-data"

 

控制層 的 接收方法中使用 MultipartFile 對象形參來接收請求中的 文件參數。

 

因爲 文件的 傳輸是 跨網絡的 ,所以 我們傳輸文件的時候 需要先將文件轉換成 【字符數組】的形式進行傳輸。 byte[] 。

下載文件是:我們需要用responseEntity<byte[]> 對象 將我們的 字節數組形式的文件傳遞到 前臺的瀏覽器。

 HttpHeaders 這個對象是用來這是響應頭的,我們需要在響應頭中 設置 文件 內容的 樣式,因爲我們之前爲了傳輸文件,將文件轉換成了字節數組的樣式,所以如果 我們需要告訴我們的瀏覽器 後端給他推送 的   這個文件 的內容 是 什麼 格式 什麼樣式的。 我們還需要告訴瀏覽器 這個數據是一個 附件,還要告訴它這個文件的名字。 需要告訴瀏覽器的這幾個 數據 我們都存放在了 header響應頭中了。

responseEntity<byte[]>對象的構造方法需要我們傳遞三個參數,分別爲:文件的 byte[]格式,header響應頭,通信狀態碼爲OK 。這個responseEntity<byte[]>對象到達被推送到 瀏覽器之後,我們的瀏覽器就會自動的  彈出下載框了。

關於文件的 上傳 和  下載  的 對象 :

EncType : 告訴form表單提交的參數中有附件。

MultiPartFile : 是spring-boot專門給我們提供接收請求中的 附件參數的 。

HttpHeaders : 響應頭,我們可以設置: 文件內容的格式,推送數據類型,文件的名字 。

ResponseEntity<byte[]>: 這個是專門用來 將我們的 附件字節數組 推送到 前臺的瀏覽器,然後瀏覽器 接收到 responseEntity中的 byte,header還有 狀態碼,就會直接彈出 下載框。

 

Enctype是 表單標籤form的屬性: encode + type ,編碼樣式。提交附件的時候需要指定一下。

接收頁面請求中的參數:

文件在網絡中進行傳輸,必須將文件轉換成字節碼數組。

我的js代碼,沒有寫到方法裏面,所以自動執行了,寫到方法裏面就需要手動調用了一下方法,然後執行代碼。

當有一段js代碼推送到我們的 iframe中是怎麼 執行的?

因爲我們的form表單設置的從 iframe框架中進行提交的,所以最後推送數據的時候也是向iframe框架進行推送數據:

當上面的這段string字符串推送到了我們的iframe框架中,就相當於是:

<iframe name="target_iframe" style="display: none;">
    <script>xxxxxx</script>
</iframe>

因爲script中的代碼沒有寫在方法當中,所以直接就被執行了,如果寫在了方法當中,那麼還需要調用一下方法來執行。

 

我們在響應頭中設置 【文件內容格式】的時候通常有兩種選擇,這個格式是到了瀏覽器中後給我們【展示】的文件內容的格式:

創建一個響應頭 : HttpHeaders headers = new HttpHeaders();
我們可以使用下面這段代碼來設置 文件內容格式:headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);

最常用的兩種格式:
(1)MediaType.APPLICATION_OCTET_STREAM   
文件內容是流形式的,因爲我們的文件需要在網絡中進行傳輸,所以文件內容需要轉換成流形式進行傳遞。 


(2)MediaType.APPLICATION_JSON_UTF8
文件內容 是 json格式的,就是將我們的文件內容轉換成了 json格式的字符串。 

返回的是一個文件,應該怎麼操作:

我們不用寫 responseBody了,因爲我們已經告訴我們的瀏覽器我們返回的是一個什麼類型的數據了,從響應頭中告訴的。

我們設置一下 返回的內容的類型 和 返回的是什麼一個什麼類型的文件和數據。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章