Export multiple files (with different file types) from database (Byte Array) to single zipped file

I need to export multiple files with varying file types (pdf, xlsx, .docx) stored in a database (Byte Array) and save them as a single zipped file. How should I handle the multiple files? I’m assuming I would need to first store the files in a list and using MemoryStream? I’m using the ZipArchive class to export the files as a zip file. Assuming this approach will work, I’m unsure of how to pass the list as an argument to the ZipArchive (DownloadMultipleFiles) method.

protected void lnkExport_Click(object sender, EventArgs e)
{
    string applicationID = ((sender as LinkButton).CommandArgument);
    var myList = GetFilesandConvertToList(applicationID);
    DownloadMultipleFiles(myList); //How would I pass myList as an argument here? Obviously, this would not work.
}

Call stored procedure to get the files and place them in a list:

public List<ZipList> GetFilesandConvertToList(string applicationID)
{           
    List<ZipList> fileList = new List<ZipList>();
    SqlCommand cmd = new SqlCommand("dbo.spGetFilesByID", ConnC.con);
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.AddWithValue("@ApplicationID", applicationID); //This will return several files (different file types using the ID)
    ConnC.con.Open();
    using (SqlDataReader sdr = cmd.ExecuteReader())
    {
        if (sdr.HasRows)
        {
            while(sdr.Read())
            {
                ZipList zl = new ZipList();
                sdr.Read();
                zl.bytes = (byte[])sdr["FILE_CONTENT"];
                zl.contentType = sdr["FILE_TYPE"].ToString();
                zl.fileName = sdr["FILE_NAME"].ToString();
                fileList.Add(zl);
            }                    
         }
    }
    return fileList;
}

Using ZipArchive to place the list in a MemoryStream and export as a zip file:

public void DownloadMultipleFiles(List<byte[]> byteArrayList)
{
    using (MemoryStream ms = new MemoryStream())
    {
        using (ZipArchive archive = new ZipArchive(ms, ZipArchiveMode.Create, true))
        {
            foreach (byte[] file in byteArrayList)
            {
                ZipArchiveEntry entry = archive.CreateEntry(file.fileName + ".pdf", CompressionLevel.Fastest);
                using (Stream zipStream = entry.Open())
                {
                    zipStream.Write(file, 0, file.Length);
                }
            }
        }
        return File(ms.ToArray(), "application/zip", "Archive.zip");
    }
}
 
public class ZipList
{
    internal byte[] bytes;
    internal string contentType;
    internal string fileName;
}

UPDATE: I’ve updated this method with a slightly modified answer from @Andy. This works great:

protected void lnkExport_Click(object sender, EventArgs e)
{
    string applicationID = ((sender as LinkButton).CommandArgument);
    var myList = GetFilesandConvertToList(applicationID);
    //Download Zipped File
    byte[] fileBytes = GetZipFileForApplicationId(applicationID);
    Response.Clear();
    Response.Buffer = true;
    Response.Charset = "";
    Response.Cache.SetCacheability(HttpCacheability.NoCache);
    Response.ClearContent();
    Response.AppendHeader("Content-Disposition", "attachment; filename=Application.zip");
    Response.AppendHeader("Content-Type", "application/zip");
    Response.BinaryWrite(fileBytes);
    HttpContext.Current.Response.Flush();
    HttpContext.Current.Response.SuppressContent = true;
    HttpContext.Current.ApplicationInstance.CompleteRequest();
}

Using the method suggested from @Andy to get the files into a memory stream and return a byte array:

public byte[] GetZipFileForApplicationId(string applicationID)
{
    byte[] fileBytes = null;
    SqlCommand cmd = new SqlCommand("dbo.spGetFilesByID", ConnC.con);
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.AddWithValue("@ApplicationID", applicationID);
    ConnC.con.Open();
    using (SqlDataReader sdr = cmd.ExecuteReader())
    {
        if (sdr.HasRows)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                using (ZipArchive archive = new ZipArchive(ms, ZipArchiveMode.Create, true))
                {
                    while (sdr.Read())
                    {
                        byte[] bytes = (byte[])sdr["FILE_CONTENT"];
                        string contentType = sdr["FILE_TYPE"].ToString();
                        string fileName = sdr["FILE_NAME"].ToString();

                        ZipArchiveEntry entry = archive.CreateEntry(fileName);
                        using (Stream zipStream = entry.Open())
                        {
                            zipStream.Write(bytes, 0, bytes.Length);
                        }
                    }
                }
                ms.Position = 0;
                fileBytes = ms.ToArray();

            }
        }
    }
    return fileBytes;
}

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

You could kill a couple birds with one stone and do it all at once without extra models (this hasn’t been tested, but you’ll get the gist):

public byte[] GetZipFileForApplicationId(string applicationID)
{
    SqlCommand cmd = new SqlCommand("dbo.spGetFilesByID", ConnC.con);
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.AddWithValue("@ApplicationID", applicationID);
    ConnC.con.Open();
    using (SqlDataReader sdr = cmd.ExecuteReader())
    {
        if (sdr.HasRows)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                using (var archive = new ZipArchive(ms, ZipArchiveMode.Create, true))
                {
                    while(sdr.Read())
                    {
                        var bytes = (byte[])sdr["FILE_CONTENT"];
                        var contentType = sdr["FILE_TYPE"].ToString();
                        var fileName = sdr["FILE_NAME"].ToString();
                        
                        var entry = archive.CreateEntry(fileName);
                        using (var zipStream = entry.Open())
                        {
                            zipStream.Write(bytes, 0, bytes.Length);
                        }
                    } 
                }
                ms.Position = 0;
                // This is kind of redundant. You should return the
                // MemoryStream object instead of duplicating it's data.
                // I'll let you play with that.
                return ms.ToArray();
            }
        }
    }
    return null;
}

Method 2

Its seems you were mostly there, in fact I think you needed to use List<ZipList> because your byteArrayList doesn’t contain a definition for fileNameorLength`.

public void DownloadMultipleFiles(List<ZipList> zipList)
{
    using (MemoryStream ms = new MemoryStream())
    {
        using (ZipArchive archive = new ZipArchive(ms, ZipArchiveMode.Create, true))
        {
            foreach (var file in zipList)
            {
                ZipArchiveEntry entry = archive.CreateEntry(file.fileName + ".pdf", CompressionLevel.Fastest);
                using (Stream zipStream = entry.Open())
                {
                    zipStream.Write(file.bytes, 0, file.bytes.Length);
                }
            }
        }
        // Currently method returns void?
        // return File(ms.ToArray(), "application/zip", "Archive.zip");
        // Maybe you want
        File.WriteAllBytes("application/zip/Archive.zip", ms.ToArray());
    }
}


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x