Search code examples
f#zipcompressionunzip

Cannot create ZipArchive in F#


I am trying to instantiate a ZipArchive class in System.IO.Compression in an F# project:

open System.IO
open System.IO.Compression
open System.IO.Compression.FileSystem //this line errors, as expected

// Define your library scripting code here

let directoryPath = @"C:\Users\max\Downloads"

let dirInfo = new DirectoryInfo(directoryPath)

let zippedFiles = dirInfo.GetFiles() 
|> Array.filter (fun x -> x.Extension.Equals(".zip"))

let fileStream = new FileStream(((Array.head zippedFiles).FullName), System.IO.FileMode.Open)

//this line does not compile, because ZipArchive is not defined
let archive = new System.IO.Compression.ZipArchive(fileStream)

I can create the same thing in C# in the correct namespace:

var unzipper = new System.IO.Compression.ZipArchive(null);

(This gives a bunch of errors because I'm passing null, but at least I can try to access it).

I do have the System.IO.Compression.FileSystem reference in my F# project (as well as the parent namespace, System.IO.Compression. However, when loading the .FileSystem namespace, I get an error saying "the namespace 'FileSystem' is not defined".

EDIT

Added the full script file I am trying to execute that reproduces the problem.

As shown via the open statements, my project references both of these libraries:

System.IO.Compression System.IO.Compression.FileSystem

I am running on:

  • F# 4.4
  • .NET 4.6.1
  • Visual Studio 2015
  • Windows 10 64 bit

EDIT 2: The Fix!

I was doing all of this in an F# script file, .fsx, which requires telling the interactive environment to load the DLLs like so:

#if INTERACTIVE
#r "System.IO.Compression.dll"
#r "System.IO.Compression.FileSystem.dll"
#endif

Solution

  • You can use System.IO.Compression to manipulate zip files from .NET 4.5. For some useage examples please see the relevant docs.

    You can just wrap the FileStream into ZipArchive, then manipulate it further. The ExtractToDirectory extension method is quite handy. You can create a FileStream, instantiate ZipArchive, then manipulate it further, for example by using the CreateEntryFromFile extension method, Ideally you should try using use on disposable as in the writeZipFile example.

    Here's an example of reading and writing a zipfile:

    #if INTERACTIVE
    #r "System.IO.Compression.dll"
    #r "System.IO.Compression.FileSystem.dll"
    #endif
    
    open System.IO
    open System.IO.Compression
    
    let zipfile = @"c:\tmp\test.zip"
    File.Exists zipfile //true
    
    let readZipFile (x:string) = 
        let x = new FileStream(x,FileMode.Open,FileAccess.Read)
        new ZipArchive(x)
    
    let z = readZipFile zipfile
    z.ExtractToDirectory(@"c:\tmp\test")
    File.Exists @"c:\tmp\test\test.txt" // true
    
    let writeZipFile (x:string) =
        use newFile = new FileStream(@"c:\tmp\newzip.zip",FileMode.Create)
        use newZip =  new ZipArchive(newFile,ZipArchiveMode.Create)
        newZip.CreateEntryFromFile(@"c:\tmp\test.txt","test.txt")
    
    writeZipFile @"c:\tmp\test.txt"
    File.Exists @"c:\tmp\newzip.zip"  // true