How can I work with ZIP- within ZIP-File and Memorystream

Sep 3, 2009 at 5:00 AM

Hello, good morning or good evening :-)

I am beginner with use the DotNetZip-Library and I am not a professional programmer with Visual Basic, sorry for that and the following question when it is easy to solve (but not for me).

To open a ZIP-File I use the DotNetZip-Library and I can unzip oder zip needed files and more, it is very easy to work.

Now I have a problem, I must open/read a ZIP-File, but within this file I have a further ZIP-File. Within this ZIP-File I must find two XML-Files, the first one is a manifest.xml, the second file is an other XML-File. To open the second xml file, I must read the manifest.xml, search for a TAG within this file to read the name of the second xml file.

In my first question (one or two week ago) I have wrote that it is not allowed for me to extract this file, and so I must use a memory-stream for my operation with this files.

Has anyone a idea how can I read the ZIP-File with a memory-stream, then read the ZIP-File within this memory-stream to open the manifest.xml?

The next problem is, when I have opend the second xml file within the second ZIP-File and change a entry, how can I save back the changes without save the ZIP- or XML-ile on my harddisk?

I an single ZIP-File I can read a XML-File with XDocument.parse(...), this is not the problem. But how can I read an XML-File within a Zip- in Zip-File and save back to the Zip- in Zip-File?

This is a big problem for me on this time and I am very happy when anyone can help me with an idea to do this.

Sorry, I use a translator and I hope that is not to confused for you :-)

Regards,
Maximilian

Coordinator
Sep 3, 2009 at 9:05 AM
Public Sub ModifyToplevelZip
    Using zip1 as ZipFile = ZipFile.Read("Embedded.zip")
        Using ms1 As New MemoryStream
            '' ms1 will hold the original inner zip
            zip1("Payload.zip").Extract(ms1)
            ms1.Seek(0, SeekOrigin.Begin)
            Using ms2 As MemoryStream = GetModifiedZip(ms1)
                zip1.UpdateEntry("Payload.zip", "", ms2)
                '' save to the toplevel zip, in the filesystem
                zip1.Save
            End Using
        End Using
    End Using        
End Sub

Public Function GetModifiedZip (ByVal ms As MemoryStream) As MemoryStream
    Dim msOut As New MemoryStream
    Using zip2 as ZipFile = ZipFile.Read(ms)
        Using manifest As New MemoryStream
            zip2("manifest.xml").Extract(manifest)

            Console.WriteLine("manifest: {0}", System.Text.Encoding.ASCII.GetString(manifest.ToArray()))
            manifest.Seek(0,SeekOrigin.Begin)
            ' at this point, manifest contains the contents of the manifest.xml

            Dim entryName As String = Nothing
            Using reader As XmlReader = XmlReader.Create(manifest)
                ' Move the reader to the root element.
                reader.MoveToContent()
                entryName = reader.ReadElementString()
                ' Read the title and price elements.
                Console.WriteLine("entry name: {0}", entryName)
            End Using

            ' entryName now contains the name of the entry that must be modified

            Dim entry As ZipEntry = zip2(entryName)
            Dim intValue As Int32
            Using reader As XmlReader = XmlReader.Create(entry.OpenReader)
                reader.MoveToContent()
                Dim nodeValue As String = reader.ReadElementString()
                Console.WriteLine("nodeValue: {0}", nodeValue)
                intValue = Int32.Parse(nodeValue)
            End Using

            intValue = intValue + 1
            Dim newXmlContent As String = String.Format("<count>{0}</count>", intValue)
            zip2.UpdateEntry(entryName, "", newXmlContent)
            zip2.Save(msOut)
        End Using
    End Using
    msOut.Seek(0, SeekOrigin.Begin)
    Return msOut
End Function


Sep 3, 2009 at 9:51 AM

Hello Cheeso,

you are great, I test your code and come back later with the result.

I had seen that I can read a entry within the ZIP-File with a "CrcCalculatorStream", I can test it if the entry readable or not?
In my testings I had one ZIP-File who was bad and so I need a check-routine to test the ZIP-File it is readable, empty or bad. This is my next step when I can use your code.

Thanks a lot of this time :-)

Regards,
Maximilian

Coordinator
Sep 3, 2009 at 4:26 PM

There is a static (Shared) CheckZip method on the ZipFile class.  There's also a method called IsZipFile.  Either of those can read from a stream.

You can use one of those to check the zip in the way you want. 

Coordinator
Sep 3, 2009 at 7:40 PM

one more thing - to use that code I posted, if you use v1.9,  you need at least v1.9.0.2 - the latest version on the site right now.   There was a bug in v1.9.0.1 that would prevent it from working.

if you are using v1.8, then you can use 1.8.4.22. 

Sep 4, 2009 at 11:58 AM

Hello Cheeso,

can I check a ZIP-File for an existing entry like "If Zip.ZipFile(filename).IsExist(entryname) then ... End If" ?

I have used your code, but I had a problem to read a specific TAG. The manifest.xml looks like that and I need the value from <machine><location>:
<config id="" instance="">
  <machine>
    <location>business</location
  </machine>
  <system>
    <location>bla</location>
  </system>
</config>

I have tested with XmlReader, but I can't find a way to read exactly the needed value and it is possible that we have more that one entry named <location> in other TAGs under the root <config...>

To use a select-command (Linq) I must work with XDocument, but with XDocument.load I cannot read a memorystream, I'm very confused and cannot find the correct way.

For this time I read the lines step by step and search for the word <location>, but this is a very bad way.

I would like to locate a entry within the same zipfile as the manifest.xml, for this I need the function from my first question (IfExist...).

Kind regards,
Maximilian

Coordinator
Sep 4, 2009 at 2:11 PM

you can pass an XmlRader to XDocument.Load(), and you can create an XmlReader using a MemoryStream.

Sep 4, 2009 at 3:00 PM

Hello Cheeso,

thanks, I use now a XmlReader and it works.

But how can I check for an existing file within the zipfile, here the "\manifest.xml"?

I would like to test it before I read the specific file and get an error when the file is not existing.

Best regards,

Maximilian

PS:

For donating your project can I make this without paypal? I don't have a paypal-account.

Coordinator
Sep 4, 2009 at 3:08 PM

To test for existence of an entry, use the string indexer, zip("Manifest.xml") will be Nothing if the named entry does  not exist in the zip file.

For donations, Paypal takes credit cards.  you don't need a paypal account.

 

Sep 4, 2009 at 3:36 PM

and how can I check whether the file exists in the root?

if ZipFile("manifest.xml") is nothing then .... end if ==> works
if ZipFile("/manifest.xml") is nothing then .... end if ==> works not

It is possible that the manifest.xml exists in the root and als under a folder.

Coordinator
Sep 4, 2009 at 4:11 PM

ZipFile("manifest.xml") is in the root

ZipFile("/manifest.xml") is also in the root.

Some zip libraries or tools prepend a leading slash, and some do not.   DotNetZip treats "/manifest.xml" and "manifest.xml" as the same.

ZipFile("/some/other/directory/manifest.xml") is in a different directory.

You can also use backslashes.  They are equivalent, for the purposes of the string indexer, to forward slashes.