19

Basically the user should be able to click on one link and download multiple pdf files. But the Catch is I cannot create files on server or anywhere. Everything has to be in memory.

I was able to create memory stream and Response.Flush() it as pdf but how do I zip multiple memory streams without creating files.

Here is my code:

Response.ContentType = "application/zip";

// If the browser is receiving a mangled zipfile, IIS Compression may cause this problem. Some members have found that
// Response.ContentType = "application/octet-stream" has solved this. May be specific to Internet Explorer.
Response.AppendHeader("content-disposition", "attachment; filename=\"Download.zip\"");
Response.CacheControl = "Private";
Response.Cache.SetExpires(DateTime.Now.AddMinutes(3)); // or put a timestamp in the filename in the content-disposition                

byte[] abyBuffer = new byte[4096];

ZipOutputStream outStream = new ZipOutputStream(Response.OutputStream);
outStream.SetLevel(3);

#region Repeat for each Memory Stream
MemoryStream fStream = CreateClassroomRoster();// This returns a memory stream with pdf document

ZipEntry objZipEntry = new ZipEntry(ZipEntry.CleanName("ClassroomRoster.pdf"));
objZipEntry.DateTime = DateTime.Now;
objZipEntry.Size = fStream.Length;
outStream.PutNextEntry(objZipEntry);

int count = fStream.Read(abyBuffer, 0, abyBuffer.Length);
while (count > 0)
{
    outStream.Write(abyBuffer, 0, count);
    count = fStream.Read(abyBuffer, 0, abyBuffer.Length);
    if (!Response.IsClientConnected)
        break;

    Response.Flush();
}

fStream.Close();

#endregion

outStream.Finish();
outStream.Close();

Response.Flush();
Response.End();

This creates a zip file but there's no file inside it

I am using using iTextSharp.text - for creating pdf using ICSharpCode.SharpZipLib.Zip - for Zipping

Thanks, Kavita

4
  • Did you ever find a solution? I need to do this exact same thing. Commented Dec 19, 2012 at 16:02
  • no I didn't.. I tried every solution online but nothing worked.. hope u find it Commented Dec 19, 2012 at 17:14
  • This link may help: snipplr.com/view/47762 Commented Nov 19, 2013 at 21:49
  • why are t=you pre-defining new byte[4096]; maybe the file size is more or less. Won't it create an issue? Commented May 13, 2022 at 6:39

6 Answers 6

29

This link describes how to create a zip from a MemoryStream using SharpZipLib: https://github.com/icsharpcode/SharpZipLib/wiki/Zip-Samples#wiki-anchorMemory. Using this and iTextSharp, I was able to zip multiple PDF files that were created in memory.

Here is my code:

MemoryStream outputMemStream = new MemoryStream();
ZipOutputStream zipStream = new ZipOutputStream(outputMemStream);

zipStream.SetLevel(3); //0-9, 9 being the highest level of compression
byte[] bytes = null;

// loops through the PDFs I need to create
foreach (var record in records)
{
    var newEntry = new ZipEntry("test" + i + ".pdf");
    newEntry.DateTime = DateTime.Now;

    zipStream.PutNextEntry(newEntry);

    bytes = CreatePDF(++i);

    MemoryStream inStream = new MemoryStream(bytes);
    StreamUtils.Copy(inStream, zipStream, new byte[4096]);
    inStream.Close();
    zipStream.CloseEntry();
}

zipStream.IsStreamOwner = false;    // False stops the Close also Closing the underlying stream.
zipStream.Close();          // Must finish the ZipOutputStream before using outputMemStream.

outputMemStream.Position = 0;

return File(outputMemStream.ToArray(), "application/octet-stream", "reports.zip");

The CreatePDF Method:

private static byte[] CreatePDF(int i)
{
    byte[] bytes = null;
    using (MemoryStream ms = new MemoryStream())
    {
        Document document = new Document(PageSize.A4, 25, 25, 30, 30);
        PdfWriter writer = PdfWriter.GetInstance(document, ms);
        document.Open();
        document.Add(new Paragraph("Hello World " + i));
        document.Close();
        writer.Close();
        bytes = ms.ToArray();
    }

    return bytes;
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the answer as it worked for me as well. Can you please tell me how can I zip text file as well?
3

Below code is to get files from a directory in azure blob storage, merge in a zip and save it in azure blob storage again.

    var outputStream = new MemoryStream();
    var archive = new ZipArchive(outputStream, ZipArchiveMode.Create, true);

    CloudBlobDirectory blobDirectory = appDataContainer.GetDirectoryReference(directory);
    
    var blobs = blobDirectory.ListBlobs();

    foreach (CloudBlockBlob blob in blobs)
    {
        var fileArchive = archive.CreateEntry(Path.GetFileName(blob.Name),CompressionLevel.Optimal);

        MemoryStream blobStream = new MemoryStream();
        if (blob.Exists())
        {
            blob.DownloadToStream(blobStream);
            blobStream.Position = 0;
        }

        var open = fileArchive.Open();
        blobStream.CopyTo(open);
        blobStream.Flush();
        open.Flush();
        open.Close();

        if (deleteBlobAfterUse)
        {
            blob.DeleteIfExists();
        }
    }
    archive.Dispose();

    CloudBlockBlob zipBlob = appDataContainer.GetBlockBlobReference(zipFile);

    zipBlob.UploadFromStream(outputStream);

Need the namespaces:

  • System.IO.Compression;
  • System.IO.Compression.ZipArchive;
  • Microsoft.Azure.Storage;
  • Microsoft.Azure.Storage.Blob;

Comments

1

This code Will help You in Creating Zip by multiple pdf files which you will get Each file from a Download Link.

        using (var outStream = new MemoryStream())
                {
                    using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true))
                    {
                        for (String Url in UrlList)
                        {
                            WebRequest req = WebRequest.Create(Url);
                            req.Method = "GET";
                            var fileInArchive = archive.CreateEntry("FileName"+i+ ".pdf", CompressionLevel.Optimal);
                            using (var entryStream = fileInArchive.Open())
                            using (WebResponse response = req.GetResponse())
                            {
                                using (var fileToCompressStream = response.GetResponseStream())
                                {
                                    entryStream.Flush();
                                    fileToCompressStream.CopyTo(entryStream);
                                    fileToCompressStream.Flush();
                                }
                            }
                           i++;
                        }

                    }
                    using (var fileStream = new FileStream(@"D:\test.zip", FileMode.Create))
                    {
                        outStream.Seek(0, SeekOrigin.Begin);
                        outStream.CopyTo(fileStream);
                    }
                }

Namespace Needed: System.IO.Compression; System.IO.Compression.ZipArchive;

Comments

1

Below is the code which is creating a zip file in MemoryStream using ZipOutputStream class which is exists inside ICSharpCode.SharpZipLib dll.

FileStream fileStream = File.OpenRead(@"G:\1.pdf");
MemoryStream MS = new MemoryStream();

byte[] buffer = new byte[fileStream.Length];
int byteRead = 0;

ZipOutputStream zipOutputStream = new ZipOutputStream(MS);
zipOutputStream.SetLevel(9); //Set the compression level(0-9)
ZipEntry entry = new ZipEntry(@"1.pdf");//Create a file that is needs to be compressed
zipOutputStream.PutNextEntry(entry);//put the entry in zip

//Writes the data into file in memory stream for compression 
while ((byteRead = fileStream.Read(buffer, 0, buffer.Length)) > 0)
    zipOutputStream.Write(buffer, 0, byteRead);

zipOutputStream.IsStreamOwner = false;
fileStream.Close();
zipOutputStream.Close();
MS.Position = 0;

Comments

1

I used info from this thread but decided to post my end to end code because it includes all the elements to download a zip file generated by the backend server.

Front end javascript Angular 12

`

export class downloadDocs{
  fileName:string = '';
  docs:string[] = [];
}

let docs = new downloadDocs();
//do some code to put names in docs.docs;

docs.fileName = 'download.zip';
this.http.post('api/docs/download', docs,
{ responseType: 'arraybuffer' }).subscribe(zip => {
  let blob = new Blob([zip], { type: "application/octetstream" });

  let url = window.URL || window.webkitURL;
  let link = url.createObjectURL(blob);
  let a = $("<a />");
  a.attr("download", this.baseFileName() + '.zip');
  a.attr("href", link);
  $("body").append(a);
  a[0].click();
  $("body").remove(a);
},
error => {
  //however you handle errors
}

` web api core 5 C# backend in Azure App Service. The all memory solution works because I did not have to use any file resources at all. Used the SharpLibZip package.

`

\\drives me nuts in code examples nobody includes the libraries
\\spend lot of time hunting down namespaces
using System.IO;
using System.Threading.Tasks;
using System.Collections.Generic;
using ICSharpCode.SharpZipLib.Zip;
using Microsoft.AspNetCore.Http;

public class DownloadDocs{

    public string FileName = "";

    public List<string> Docs = new List<string>(); 
}

[Route("/api/docs/download")]
[HttpPost]
public async Task<ActionResult> ApiDownloadDocs([FromBody] DownloadDocs docs)
{
  
  try
  {
      var stream = await this.ReturnZipFile(docs.Docs);
      return File(stream, "application/octet-stream", docs.FileName);
  }
  catch (Exception e)
  {
      var msg = $"Docs Download error: {e.Message}";
      return Problem(msg);
  }
}

private async Task<MemoryStream> ReturnZipFile(List<string> files)
{
  var stream = new MemoryStream();
  stream.Position = 0;
  var zipOutputStream = new ZipOutputStream(stream);
  zipOutputStream.SetLevel(4); //Set the compression level(0-9)

  foreach (let doc in files)
  {
      var docStream = new MemoryStream();
      docStream = await this.GetPdfMemoryStream(doc);
      byte[] buffer = new byte[docStream.Length];
      int byteRead = 0;

      ZipEntry entry = new ZipEntry(doc + ".pdf");
      zipOutputStream.PutNextEntry(entry);
      while ((byteRead = docStream.Read(buffer, 0, buffer.Length)) > 0)
          zipOutputStream.Write(buffer, 0, byteRead);

      docStream.Close();
  }

  zipOutputStream.Finish();
  //zipOutputStream.Close(); //this also closed the output stream and made it worthless
  
  stream.Position = 0;
  return stream;
}

`

Sql Server code reading blob from table and returning it as a byte array and then memory stream.

`

public async Task<byte[]> GetPdfBytes(string uuid)
{
    byte[] fileBytes = null;
    var conn = new SqlConnection(connectionString);
    await conn.OpenAsync();

    string sql = $"SELECT CONVERT(varbinary(max),BLOB)  FROM DOC_BLOBS WHERE UUID = '{uuid}'";
    using (var cmd = new SqlCommand(sql, conn))
    {
        using (var reader = await cmd.ExecuteReaderAsync())
        {
            if (await reader.ReadAsync())
            {
                fileBytes = (byte[])reader[0];
            }
        }
    }
    return fileBytes;
}

public async Task<MemoryStream> GetPdfMemoryStream(string uuid)
{
    return new MemoryStream(await GetPdfBytes(uuid));
}

`

Comments

0

You could generate your pdf files and store it in IsolatedStorageFileStream then you could zip content from that storage.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.