利用Nginx第三方模塊,實現附件打包下載

原文:利用Nginx第三方模塊,實現附件打包下載php

前一陣子被一個需求困擾:附件的打包下載,須要將一批邏輯上一塊兒的文件,讓用戶經過一個下載按鈕打包下載。首先想到的方案是服務端調用什麼zip之類的類庫,將文件打包好後返回客戶端。可是這樣作有一個很明顯的問題:文件不少很大的狀況下,打包可能會佔用大量的內存和cpu,就算在磁盤上構建臨時的打包文件,也會增長服務器的磁盤IO負擔,並且這些臨時的文件無端佔用大量的磁盤空間,刪除仍是個問題。用戶體驗也是問題,由於必須打包完成後,才能開始返回,沒法邊打包邊下載。原本都準備放棄了,不過發現百度網盤好像實現了這個功能,因而再次考慮如何實現。想到咱們實際上使用了Nginx做爲文件服務器,會不會有第三方模塊可以支持這種功能呢?尋覓以後果真有結果,就是本文要探討的mod_ziphtml

mod_zip介紹

mod_zip可以動態的構建zip包,這種動態體如今當Nginx做爲反向代理服務器的時候,該模塊可以根據上游服務器返回的文件列表來打包文件。mod_zip其實是利用Nginx的subrequest功能,將zip流發送到客戶端的,並且它實際上只打包不壓縮,因此藉助Nginx自己做爲文件服務器的能力,該模塊的內存佔用十分少,對於上G的大文件也沒有問題。zip文件自己是結構化的,能夠自定義目錄結構,因此對於mod_zip而言,要作的只是添加zip的頭部尾部和zip內部的目錄結構元數據而已,文件數據自己依靠Nginx自身的機制發送。nginx

除此以外,還有以下兩點:git

  • 因爲使用subrequest機制,文件甚至能夠不在Nginx的服務器自己,能夠是上游服務器,甚至是互聯網的遠程服務器上
  • 在添加crc校驗後,mod_zip還可以支持HTTP的Range,支持斷點續傳

基本使用

安裝

下載源碼:github

$ git clone https://github.com/evanmiller/mod_zip.git

從新編譯Nginx,不要make install:服務器

$ ./configure --add-module=/src/mod_zip
$ make

將生成的二進制文件覆蓋現有的二進制文件。一般編譯出來的二進制文件位於源碼目錄的objs/nginx。更多關於如何添加第三方模塊看如何安裝nginx第三方模塊app

使用方法

該模塊不須要在nginx.conf中配置任何東西,一切的行爲取決於上游服務器的響應內容。mod_zip規定當響應頭中包含X-Archive-Files的時候,將啓用mod_zip的功能:dom

X-Archive-Files: zip

同時,響應的body中須要包含一個欲打包的文件的列表,如:ide

1034ab38 428    /foo.txt   My Document1.txt
83e8110b 100339 /bar.txt   My Other Document1.txt

每一行表示一個文件描述,行與行之間有一個換行符(最後也有個換行)。每行從左向右以空格分隔,依次是文件的crc-32校驗,文件大小(Byte),文件的uri,文件名。其中crc-32能夠忽略,並用-代替,文件名能夠包含目錄,會體如今最後的壓縮包中的目錄結構中。測試

重點是文件的uri怎麼理解。這裏的/foo.txt/bar.txt並不是指向文件系統的路徑,而是一個子請求的地址。好比上面的/foo.txt實際上會產生一個Nginx自身的請求:http://host/foo.txt,至於這個請求獲得什麼又要根據nginx.conf中的配置決定了。這樣的設計十分靈活,例以下面的配置:

location ~ "^/(?<srv>server[12])/(?<file>.*txt)" {
    proxy_pass http://$srv.domain.com/$file
}

因而,能夠這樣使用文件uri:

1034ab38 428    /server1/foo.txt   My Document1.txt
83e8110b 100339 /server2/bar.txt   My Other Document1.txt

這樣兩個文件分別會向遠程服務器請求文件:

http://server1.domain.com/foo.txt
http://server2.domain.com/bar.txt

上游服務器能夠經過在頭部注入Content-Disposition來控制zip文件的輸出文件名

Content-Disposition: attachment; filename=foobar.zip

上游服務器示例

下面是個測試用的上游服務器例子

<?php

    header('X-Accel-Chareset: utf-8');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename=test.zip');
    header('X-Archive-Files: zip');
    $crc32 = "-";
    printf("%s %d %s %s\n", $crc32, 66382593, '/video/raw/1dc3b670-f864-4050-9772-8ccff341d091.mp4', '1.mp4');
    printf("%s %d %s %s\n", $crc32, 26160723, '/video/raw/4d6bf6ba-5b8a-49be-bede-481fe49093dc.mp4', '2.mp4');
?>
相關文章
相關標籤/搜索