1

Cursory Google search didn't return anything simple enough to understand (i'm pretty new to functional programming).

if I have an array of files, how can i zip each file and then create a zip of all zipped files?

I have something like this so far:

let zip f = 
    f.zip //this is where I need the most direction

let zipAllAttachments f =
    f
    |> Seq.map zip   //do I need to create another function to create a single zip of all zips?

EDIT: this is what I have so far, but I'm getting some strange behavior. More to come once I figure out what the strange behavior IS exactly:

use zipfile = new ZipFile()
for fileObj in files do
    zipfile.AddFile(sprintf "%s%s" path  fileObj.Filename) |> ignore
    zipfile.Save("C:\\temp\\Compliance.zip")

UPDATE: I don't think the "strange behavior" is related to the zip module. I appreciate all the help!

Ramy
  • 20,541
  • 41
  • 103
  • 153
  • Check out http://techmikael.blogspot.com/2010/11/creating-zip-files-with.html for how you can use built-in .Net API (System.IO.Packaging) for creating zip's. (With a few limitations) – Mikael Svenson Dec 28 '10 at 21:22
  • Compressing a file that's already compressed actually makes it bigger. For this reason, I would either put all the files in a single zip file, or if you must put zip files inside a zip file, set the compression level on the outer zip file to NONE. – Joel Mueller Dec 28 '10 at 22:09

3 Answers3

9

Are you trying to create an independent implementation of zip compression?

I'd use DotNetZip from http://dotnetzip.codeplex.com/ -- it's a single, managed code (C#) assembly. Using it from F# should be pretty much as simple as referencing the assembly from your project.

Usage is simple. For C#:

using (ZipFile zip = new ZipFile())
{
  // add this map file into the "images" directory in the zip archive
  zip.AddFile("c:\\images\\personal\\7440-N49th.png", "images");
  // add the report into a different directory in the archive
  zip.AddFile("c:\\Reports\\2008-Regional-Sales-Report.pdf", "files");
  zip.AddFile("ReadMe.txt");
  zip.Save("MyZipFile.zip");
}

If you want to zip a collection of zip files (why?), there's a number of ways to do that with DotNetZip (you can, for instance, save your zip file to a stream, or add a stream to a zip file).

Hope this helps!


Edited To Note: DotNetZip used to live at Codeplex. Codeplex has been shut down. The old archive is still [available at Codeplex][1]. It looks like the code has migrated to Github:


Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135
  • I'd like to create a zip of zips so that I have one single deliverable instead of a series of deliverables (i may do this differently later but for now that's what I'd like to do). How can I translate your code to F# (i come from a Java/Python environment but have to use F# from time to time). – Ramy Dec 28 '10 at 19:31
  • also, the "zip" function was intended to take a single file and return a compressed archive of that file. i was hoping to use this for the map function. maybe i'm going about it wrong? – Ramy Dec 28 '10 at 19:36
  • The zip algorithm archives 1 or more files, then compresses it.Jean-Loup Gailly's zlib/gzip algorithm compresses a single file (or stream of bytes). It's typically used in conjunction with an archive format such as tar(1) to produce *.tar.gz files (e.g., a tar archive that's been gzipped). To use DotNetZip from F#, reference the DotNetZip assembly in your project. The DotNetZip objects exist within the namespace 'Ionic.Zip'. You should be able to reference and use them the same you any standard CLR object is referenced and used. – Nicholas Carey Dec 28 '10 at 20:18
  • how do I install the Ionic package? Is there a location that it needs to be? Do I need to then add it to my project? – Ramy Dec 28 '10 at 22:03
  • There's nothing to install, really. DotNetZip is just a single assembly (read DLL). In Visual Studio, you just need to reference that assembly (e.g. bring it into the project). Once that's done, it should automatically get copied into the bin directory when you build the project. – Nicholas Carey Dec 28 '10 at 22:56
3

I haven't used the zip library that Nicholas mentioned, but this may be the F# version of what you want.

let create_zip_file (files: seq<string>) zipfile_name = 
    use zipfile = new ZipFile()
    files
    |> Seq.iter (fun f -> zip.AddFile(f))

    zipfile.Save(zipfile_name)

You may need to add type information to zipfile_name too, if it is overloaded.

This function could be used to create the zip files of the individual files, and then used to create a big zip file containing all of the smaller zip files. Here is an example, although you wouldn't actually want to duplicate the file names all over the place like it does.

create_zip_file ["first_file"] "first_file.zip"
create_zip_file ["second_file"] "second_file.zip"
create_zip_file ["first_file.zip"; "second_file.zip"] "big_file.zip"
dave jones
  • 356
  • 2
  • 6
  • this is telling me that "ZipFile is not defined" – Ramy Dec 28 '10 at 21:45
  • 1
    @Ramy: Try adding `open Ionic.Zip` to your code. Also note there's an AddFiles method of the zip file, so you don't have to use that lambda expression. – phoog Dec 28 '10 at 21:53
1

As of .NET 4.6.2, there is ZipArchive class available:

namespace FSharpBasics

module ZipThis =

    open System.IO
    open System.IO.Compression
    open System.Reflection
    open System

    let create (zipName: string) (files: seq<FileInfo>) =
        use s = File.Create(zipName)
        use z = new ZipArchive(s, ZipArchiveMode.Create)
        files
            |> Seq.map (fun item -> (item.FullName, item.Name))
            |> Seq.iter (fun (path, name) -> z.CreateEntryFromFile (path, name) |> ignore)

    [<EntryPoint>]
    let main argv =
        let di = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory)
        printfn "Creating test.zip on current directory"
        create "test.zip" (di.GetFiles "*.dll")
        printfn "Created"
        0