I have a Yesod application (but question is more general than this) that allows file uploads. I also allow file dowloads. I would like to allow users download multiple files with single link. As per this question: How to download multiple files with one HTTP request? the only solution seems to be creation of file archive with all the files inside.
I want to do it in constant memory in Haskell using libraries from Hackage, without writing to disk or executing external programs.
In particular the following are non-solutions:
-
calling external programs to create an archive: the files may be on disk or in some database on accessible via some remote url. the filesystem may be “read only”. executing external programs may be impossible for security reasons. external programs complicate deployment.
-
creating temporary archive on disk from source files: see “read only” filesystem above. Also quite inefficient: writing to disk is pretty slow actually.
-
creating complete archive in memory and serving it afterwards: the files may be quite large (think CD images) and multiple. The memory needed would be too great.
It very much depends on which file formats you want to support (.zip, .tar.gz, tar.bz2 are the most common), but you can use the
zip-archivelibrary to create .zip archives. These archives are produced as lazy byte-strings, meaning that they will be generated on-the-fly. The only tricky part is to produce a value of typeArchivewith the correct contents. It might for example look like this:If you don’t have files on your file system that you want to compress, but instead files in a database or something, you can manually build values of type
Entrywith the correct fields filled in. You only need a lazyByteStringrepresenting the data you want to compress, nothing more; then you can use thetoEntryfunction to generate an entry. It might be worth mentioning that theeRelativePathfield inEntryis the relative path of the file inside of the .zip archive, not the actual relative path in the file system.