Using a different filename with AddEntry(String, OpenDelegate, CloseDelegate)

Jul 9, 2014 at 10:53 PM
I am zipping up over 30,000 files from a database export. I retrieve the files via a PKID, but I'd like to name them something human-readable and relevant to the file. The problem I have is that when using this AddEntry overload, the first String parameter is also the parameter sent to the OpenDelegate. I would like to choose a filename for the zipped file that is separate from the parameter sent to the OpenDelegate. Is there a way to do this?

Here is my code for reference.
Private Function GetAnnualReportIDs() As List(Of Integer)
    Dim reportList As List(Of Integer) = New List(Of Integer)

    Using connection As New SqlConnection(ConfigurationManager.ConnectionStrings("DB").ConnectionString)
        Dim command As New SqlCommand("GetAllAnnualReportIDs", connection)
        command.CommandType = CommandType.StoredProcedure

        connection.Open()
        Dim reader As SqlDataReader = command.ExecuteReader()
        While reader.Read()
            reportList.Add(reader("frmPKID"))
        End While
    End Using

    Return reportList
End Function

Private Function GetAnnualReportNames() As List(Of String)
    Dim reportNames As List(Of String) = New List(Of String)

    Using connection As New SqlConnection(ConfigurationManager.ConnectionStrings("DB").ConnectionString)
        Dim command As New SqlCommand("GetAllAnnualReportNames", connection)
        command.CommandType = CommandType.StoredProcedure

        connection.Open()
        Dim reader As SqlDataReader = command.ExecuteReader()
        While reader.Read()
            reportNames.Add(reader("Name"))
        End While
    End Using

    Return reportNames
End Function

Private Function GetAnnualReportByID(ByVal documentID As Integer) As Stream
    Using connection As New SqlConnection(ConfigurationManager.ConnectionStrings("DB").ConnectionString)
        Dim command As New SqlCommand("GetAnnualReportByID", connection)
        command.CommandType = CommandType.StoredProcedure
        command.Parameters.Add("@DocumentID", SqlDbType.Int).Value = documentID

        connection.Open()
        Dim annualReport As SqlDataReader = command.ExecuteReader()

        If annualReport.Read() Then
            Dim docFile As Byte() = annualReport.Item("docFile")
            Return New MemoryStream(docFile)
        Else
            Return New MemoryStream(New Byte() {0})
        End If
        connection.Close()
    End Using
End Function

Private Sub CloseStream(ByVal entryName As String, ByVal stream As Stream)
    If stream IsNot Nothing Then
        stream.Dispose()
    End If
End Sub

Private Sub PrepareAnnualReports()
    Dim zip As ZipFile = New ZipFile
    Dim names As List(Of String) = GetAnnualReportNames()

    Dim documentIndex As Integer = 0
    For Each documentID As Integer In GetAnnualReportIDs()
        Dim filenameSuffix As Integer = 0

        While zip.ContainsEntry(documentID) 'names(documentIndex) & "-" & filenameSuffix.ToString("D3") & ".pdf")
            filenameSuffix += 1
        End While
        zip.AddEntry(documentID, DirectCast(AddressOf GetAnnualReportByID, OpenDelegate), DirectCast(AddressOf CloseStream, CloseDelegate))
        documentIndex += 1
    Next

    Dim output As MemoryStream = New MemoryStream()
    zip.Save(output)
    DirectCast(Cache("AnnualReportExport"), List(Of Byte())).Add(output.ToArray())
Jul 14, 2014 at 4:52 PM
I ended up using a token character that I knew couldn't be in the filenames to send both the PKID and the filename to GetAnnualReportByID() in the same string (separated by the token character), and then I used String.IndexOf() with String.Remove() to split the PKID from the filename to use for retrieving the data. But then when I save the zip file, the filenames all have the PKID in them as well. So I just Read the ZipFile it saves back into an object, change the Filename property on every Entry, again using the token character to split the proper filename from the string, and then save that new ZipFile with the correct names and send that one to the client.

It seems extremely convoluted to me, but it works. If anyone is interested in seeing just how much effort this required, let me know and I can post the code.