在Web服务器中即时创建的流式文件存档

时间:2012-06-08 10:41:00

标签: web-services haskell compression yesod

我有一个Yesod应用程序(但问题比这更通用)允许文件上传。我也允许文件下载。我想允许用户使用单个链接下载多个文件。根据这个问题:How to download multiple files with one HTTP request?唯一的解决方案似乎是创建包含所有文件的文件存档。

我希望使用Hackage中的库在Haskell的常量内存中执行此操作,而无需写入磁盘或执行外部程序。

特别是以下是非解决方案:

  • 调用外部程序来创建存档:文件可能位于磁盘上或某些数据库中,可通过某些远程URL访问。文件系统可以是“只读”。出于安全原因,可能无法执行外部程序。外部程序使部署复杂化。

  • 从源文件在磁盘上创建临时存档:请参阅上面的“只读”文件系统。效率也很低:实际上写入磁盘非常慢。

  • 在内存中创建完整的存档并在之后提供:文件可能非常大(想想CD图像)和多个。所需的记忆力太大了。

1 个答案:

答案 0 :(得分: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函数生成条目。值得一提的是eRelativePath中的Entry字段是.zip存档内文件的相对路径,而不是文件系统中的实际相对路径。