Search code examples
vb.nethttpwebresponsedotnetzip

VB.NET - download zip in Memory and extract file from memory to disk


I'm having some trouble with this, despite finding examples. I think it may be an encoding problem, but I'm just not sure. I am trying to programitally download a file from a https server, that uses cookies (and hence I'm using httpwebrequest). I'm debug printing the capacity of the streams to check, but the output [raw] files look different. Have tried other encoding to no avail.

Code:

    Sub downloadzip(strURL As String, strDestDir As String)

    Dim request As HttpWebRequest
    Dim response As HttpWebResponse

    request = Net.HttpWebRequest.Create(strURL)
    request.UserAgent = strUserAgent
    request.Method = "GET"
    request.CookieContainer = cookieJar
    response = request.GetResponse()

    If response.ContentType = "application/zip" Then
        Debug.WriteLine("Is Zip")
    Else
        Debug.WriteLine("Is NOT Zip: is " + response.ContentType.ToString)
        Exit Sub
    End If

    Dim intLen As Int64 = response.ContentLength
    Debug.WriteLine("response length: " + intLen.ToString)

    Using srStreamRemote As StreamReader = New StreamReader(response.GetResponseStream(), Encoding.Default)
        'Using ms As New MemoryStream(intLen)
        Dim fullfile As String = srStreamRemote.ReadToEnd

        Dim memstream As MemoryStream = New MemoryStream(New UnicodeEncoding().GetBytes(fullfile))

        'test write out to flie
        Dim data As Byte() = memstream.ToArray()
        Using filestrm As FileStream = New FileStream("c:\temp\debug.zip", FileMode.Create)
            filestrm.Write(data, 0, data.Length)
        End Using

        Debug.WriteLine("Memstream capacity " + memstream.Capacity.ToString)
        'Dim strData As String = srStreamRemote.ReadToEnd
        memstream.Seek(0, 0)
        Dim buffer As Byte() = New Byte(2048) {}
        Using zip As New ZipInputStream(memstream)
            Debug.WriteLine("zip stream cap " + zip.Length.ToString)
            zip.Seek(0, 0)
            Dim e As ZipEntry

            Dim flag As Boolean = True
            Do While flag ' daft, but won't assign e=zip... tries to evaluate
                e = zip.GetNextEntry
                If IsNothing(e) Then
                    flag = False
                    Exit Do
                Else
                    e.UseUnicodeAsNecessary = True
                End If

                If Not e.IsDirectory Then
                    Debug.WriteLine("Writing out " + e.FileName)
                    '    e.Extract(strDestDir)

                    Using output As FileStream = File.Open(Path.Combine(strDestDir, e.FileName), _
                                                          FileMode.Create, FileAccess.ReadWrite)
                        Dim n As Integer
                        Do While (n = zip.Read(buffer, 0, buffer.Length) > 0)
                            output.Write(buffer, 0, n)
                        Loop
                    End Using

                End If
            Loop
        End Using
        'End Using
    End Using 'srStreamRemote.Close()
    response.Close()
End Sub

So I get the right size file downloaded, but dotnetzip does not recognise it, and the files that get copied out are incomplete/invalid zips. I've spent most of today on this, and am ready to give up.


Solution

  • I thought I'd post my full working solution to my own question, it combines the two excellent replies I've had, thank you guys.

    Sub downloadzip(strURL As String, strDestDir As String)
        Try
    
            Dim request As HttpWebRequest
            Dim response As HttpWebResponse
    
            request = Net.HttpWebRequest.Create(strURL)
            request.UserAgent = strUserAgent
            request.Method = "GET"
            request.CookieContainer = cookieJar
            response = request.GetResponse()
    
            If response.ContentType = "application/zip" Then
                Debug.WriteLine("Is Zip")
            Else
                Debug.WriteLine("Is NOT Zip: is " + response.ContentType.ToString)
                Exit Sub
            End If
    
            Dim intLen As Int32 = response.ContentLength
            Debug.WriteLine("response length: " + intLen.ToString)
    
            Dim memStream As MemoryStream
            Using stmResponse As IO.Stream = response.GetResponseStream()
                'Using ms As New MemoryStream(intLen)
    
                Dim buffer = New Byte(intLen) {}
                'Dim memstream As MemoryStream = New MemoryStream(buffer)
    
                Dim bytesRead As Integer
                Do
                    bytesRead += stmResponse.Read(buffer, bytesRead, intLen - bytesRead)
                Loop Until bytesRead = intLen
    
                memStream = New MemoryStream(buffer)
    
                Dim res As Boolean = False
                res = ZipExtracttoFile(memStream, strDestDir)
    
            End Using 'srStreamRemote.Close()
            response.Close()
    
    
    
        Catch ex As Exception
            'to do :)
        End Try
    End Sub
    
    
    Function ZipExtracttoFile(strm As MemoryStream, strDestDir As String) As Boolean
    
        Try
            Using zip As ZipFile = ZipFile.Read(strm)
                For Each e As ZipEntry In zip
    
                    e.Extract(strDestDir)
    
                Next
            End Using
        Catch ex As Exception
            Return False
        End Try
    
        Return True
    
    End Function