Just want single file extracted, not including relative path.

Jun 7, 2009 at 8:51 PM

Hello all.

First off, great library, I love it.
My problem is this:

Say I have an archive with a file in it like this: /foldera/folderb/file.txt

I would like to extract "file.txt" to C:\folderc\file.txt - but when I use ZipFile.Extract("/foldera/folderb/file.txt","C:\folderc") I obviously end up wit: C:\folderc\foldera\folderb\file.txt

How does one "leave off" the relative path and just extract the single file to a directory?

Thank you,
Backslash

Coordinator
Jun 7, 2009 at 9:23 PM
Edited Jun 7, 2009 at 9:23 PM

What's your language?  In vb.net, you can do what you want with code like this:

Dim Overwrite as ExtractExistingFileAction = ExtractExistingFileAction.OverwriteSilently
Using zip1 As ZipFile = ZipFile.Read(ZipToUnpack)
  Dim e As ZipEntry 
  For Each e In zip1
    ' change the name of the ZipEntry in memory, to remove the path
    e.FileName = System.IO.Path.GetFileName(e.FileName)
    ' extract that entry, with the modified name, into the specified directory
    e.Extract("C:\folderc", Overwrite)
  Next 
End Using 

There was a request to add a new method to make this simpler.  (http://dotnetzip.codeplex.com/WorkItem/View.aspx?WorkItemId=7803)  Because the way you do this today is so simple, and because it is a rare enough case, I am not sure if a new method in the DotNetZip interface is necessary.

 

Jun 7, 2009 at 11:27 PM

I'm working in C# but I can see what you're doing. It even makes sense in it's own right. Thanks for that, and thanks for the quick answer. I'll give it a try when I get home.

:)

Coordinator
Jun 8, 2009 at 12:31 AM

No problem. In C# it looks like this:

var overwrite = ExtractExistingFileAction.OverwriteSilently;
using (var zip1 = ZipFile.Read(ZipToUnpack))
{
  foreach (var e in zip1)
  {
    // change the name of the ZipEntry in memory, to remove the path
    e.FileName = System.IO.Path.GetFileName(e.FileName);
    // extract that entry, with the modified name, into the specified directory
    e.Extract("C:\\folderc", overwrite);
  }
}

Or, if you want to extract just one, or a few particular entries, maybe something like this:

using (var zip1 = ZipFile.Read(ZipToUnpack))
{
  var e = zip1["\\folderA\\folderB\\entryName.txt"];
  // change the name of the ZipEntry in memory, to remove the path
  e.FileName = System.IO.Path.GetFileName(e.FileName);
  // extract that entry, with the modified name, into the specified directory
  e.Extract("C:\\folderc", overwrite);
}

When you call ZipFile.Read(), you create an in-memory representation of the zip file. That representation includes a list of all the entries from the zip file, and each entry includes a path. What you are doing by setting the FileName property of one of the particular entries is just modifying the in-memory representation of the zip file.  Then you extract the entry, using that modified pathname.  Easy.

 

Aug 2, 2009 at 7:26 PM
Edited Aug 2, 2009 at 7:27 PM

I'm having some trouble with the code from the second post in VB2008 (under Windows 7 (don't know if that's important)).

The line "e.FileName = System.IO.Path.GetFileName(e.FileName)" generates the following error: "ZipException was unhandled", "The filename must be non-empty and non-null."

When I put "Debug.Print(System.IO.Path.GetFileName(e.FileName))" in the for-loop I indeed get blank lines in between the filenames.

All help welcomed.


 

Coordinator
Aug 2, 2009 at 7:40 PM

The Windows 7 part is not important.

what are you trying to do?  are you trying to strip paths from the entries in a zip file?

You could be having this exception if you set e.FileName to Nothing. Could this be possible? How?

What is the value of Path.GetFileName() if there is no slash in the argument? I'm not sure. It could be Nothing. But you can test this out pretty easily.

I don't know exactly what you mean by "get blank lines". Maybe you need to insert a condition on the assignment.

  If e.FileName.Contains("/") Then
    e.FileName = Path.GetFileName(e.FileName)
  End If

or

  If Not Path.GetFileName(e.FileName) Is Nothing Then
    e.FileName = Path.GetFileName(e.FileName)
  End If

(not sure of the VB syntax, but I think that's right)

Aug 3, 2009 at 9:17 PM

Ok, part of the issue is solved.

I'm now using the following code

Using zip1 As ZipFile = ZipFile.Read(ZipPath)
            Dim Overwrite As ExtractExistingFileAction = ExtractExistingFileAction.OverwriteSilently
            Dim e As ZipEntry
            Dim tester As String

            For Each e In zip1
                If System.IO.Path.GetFileName(e.FileName) <> "" Then
                    e.FileName = System.IO.Path.GetFileName(e.FileName)
                    e.Extract(FullInstallPath, Overwrite)
                End If
            Next
End Using

If the zip-file has the following files it works (files A,B and C all end up in the folder FullInstallPath, as intended):

submapA/FileC.txt
FileA.txt
FileB.txt

 

However, zip-files like the following where some files need to be overwritten, generate an error on the 'Next'-line

submapA/FileA.txt
FileA.txt
FileB.txt

The Error states: Collection was modified; enumeration operation may not execute.

I hope this info is helpful to you. Thanks for DotNetZip and for your help so far.

 

Coordinator
Aug 3, 2009 at 9:23 PM

ok, there are two things going on here.

First, is the strange error you get from DotNetZip "Collection was modified."  That shouldn't happen.  I'll fix that.

Second, you have a conflict - there are two files that you want to extract, into the same place.   In your second example "submapA/FileA.txt" and "FileA.txt" will both get the FileName "FileA.txt".  You will have to decide what you want to do in that case, to resolve the conflict. 

 

Coordinator
Aug 3, 2009 at 9:51 PM
Edited Aug 3, 2009 at 9:52 PM

ok, now when you try to rename an entry to a name that already exists in the archive, instead of the cryptic "Collection was modified" exception, you will get a more clear and helpful error:

extracting the modified zip....
Exception: Ionic.Zip.ZipException: Cannot rename fodder/next/FileA.zzz to FileA.zzz; an entry by that name already exists in the archive.
   at Ionic.Zip.ZipEntry.set_FileName(String value) in c:\dinoch\dev\dotnet\zip\DotNetZip\Zip Partial DLL\ZipEntry.cs:line 831
   at Ionic.Tests.Something.dusteagle.Run() in c:\dinoch\dev\dotnet\zip\test\dusteagle.vb:line 87
   at Ionic.Tests.Something.dusteagle.Main(String[] args) in c:\dinoch\dev\dotnet\zip\test\dusteagle.vb:line 136

This will be in the next binary release of the library.

You still have to decide what you want to do in the case of duplicates.

Aug 4, 2009 at 10:17 AM

Thanks, I understand the problem better now. You can not have two identical filenames in a single zipfile. I thought ExtractExistingFileAction.OverwriteSilently took care, but that is only for files already existing in the target directory.

Is there a way to retain the largest one of both submapA/FileA.txt and FileA.txt and extract only that one?

Coordinator
Aug 4, 2009 at 11:47 AM
Edited Aug 4, 2009 at 12:44 PM

Yes, you cannot have two entries with the same name, in a zip file.

And Yes, the way to retain the largest one is to scan through and get the largest one.  You could do this with a loop, or a LIINQ query over the entries.

Using zip As ZipFile = ZipFile.Read(ZipPath)
    Dim g2 = _
        From e in zip _
        Group e by Name = System.IO.Path.GetFileName(e.FileName) Into Group

    For Each elt in g2
        Dim x = elt
        Dim largest = (From e In x.Group _
                       Where e.UncompressedSize = x.Group.Max(Function(entry) entry.UncompressedSize) _
                       Select e) _
            .First
        Console.WriteLine("    {1,8}  {0}", largest.FileName, largest.UncompressedSize)

         If (largest.UncompressedSize > 0) Then
             largest.FileName = elt.Name
             largest.Extract("queryzip")
         End If
     Next
End Using

Aug 9, 2009 at 6:41 PM

Thanks for your help. I've worked around it by loading all info into an array and first renaming the files I don't want to unzip and then stripping the relative paths.