2012-06-08 93 views
0

我有一個允許文件上傳的Yesod應用程序(但問題比這個更普遍)。我也允許文件dowloads。我想讓用戶用單個鏈接下載多個文件。根據這個問題:How to download multiple files with one HTTP request?唯一的解決方案似乎是創建文件存檔與所有的文件裏面。在Web服務器上動態創建流式文件存檔

我想在Haskell的常量內存中使用Hackage的庫來完成它,而不寫入磁盤或執行外部程序。

尤其是以下的非解決方案:

  • 調用外部程序創建存檔:該文件可能是在磁盤或通過一些偏遠的網址上訪問某些數據庫。該文件系統可能是「只讀」的。由於安全原因,執行外部程序可能不可能。外部程序使部署複雜化。

  • 從源文件在磁盤上創建臨時存檔:請參閱上面的「只讀」文件系統。實際上寫入磁盤的效率也非常低。

  • 在內存中創建完整的存檔並在之後進行提供:文件可能非常大(可能是CD映像)和多個文件。需要的內存太大了。

+0

如果您在內存中執行此操作,並且有10個用戶每個都下載5x 100MB文件,則您需要5GB的RAM才能進行歸檔。似乎沒有特別的可擴展性。 – Polynomial

+2

@Polynomial,如果你閱讀這個問題,@Tener明確不想將整個檔案保存在內存中。有很多'gzip'和'zip'的實現可以實時壓縮內容並對其進行流式處理。 – dflemstr

+0

@dflemstr哎呀,錯過了問題的最後部分。儘管如此,這似乎是在即使是中等負載的情況下也會摧毀服務器的CPU。 – Polynomial

回答

1

這很大程度上取決於哪些文件要支持格式文件(.zip,.tar.gz以及TAR.BZ2是最常見的),但你可以使用zip-archive庫創建的.zip檔案。這些檔案以懶惰字節字符串的形式生成,這意味着它們將被即時生成。唯一棘手的部分是產生一個類型爲Archive的值與正確的內容。它可能例如是這樣的:

import Codec.Archive.Zip 

-- ... and in your code: 
let archiveTemplate = 
    Archive 
    { zComment = ByteString.pack "Downloaded from mysite.com" 
    , zSignature = Nothing 
    , zEntries = [] 
    } 

let filesIWantToInclude = ["foo.png", "bar.iso"] 
entries <- forM filesIWantToInclude $ readEntry [] 
let archive = foldr addEntryToArchive archiveTemplate entries 

let byteString = fromArchive archive 
-- Now you can send the byteString over the network, or something. 

如果你沒有在數據庫或東西,你要壓縮的文件系統上的文件,而是文件,你可以手動建立Entry類型的值填寫正確的字段。您只需要表示要壓縮的數據的懶惰ByteString,僅此而已;那麼您可以使用toEntry函數生成一個條目。值得一提的是,Entry中的eRelativePath字段是.zip存檔內文件的相對路徑,而不是文件系統中的實際相對路徑。

+0

我確實看過這個圖書館。乍看起來,它似乎是一個非解決方案。我沒有測試它,但從源文件中查找readEntry使用toEntry,它試圖變得聰明,只在需要時才進行壓縮。測試是通過嘗試壓縮整個文件並查看是否有幫助。我認爲這會使整個文件存儲在內存中,因此整個庫會消耗太多內存。雖然可以手動創建條目。我需要用於crc32計算的代碼。 – Tener

+0

您可以複製內部壓縮方法並省略大小比較... – dflemstr

相關問題