从内存流创建Zip文件 C#

18

基本上,用户应该能够点击一个链接并下载多个PDF文件。但问题是我不能在服务器或任何地方创建文件。一切都必须在内存中进行。

我已经能够创建内存流并将其作为PDF使用Response.Flush(),但如何压缩多个内存流而不创建文件呢?

以下是我的代码:

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();

这个操作创建了一个Zip文件,但是里面没有任何文件。

我正在使用以下两个库: 使用iTextSharp.text - 用于创建pdf 使用ICSharpCode.SharpZipLib.Zip - 用于压缩

谢谢, 卡维塔


你有没有找到解决方案?我需要做完全相同的事情。 - sbonkosky
我没有这样做。我尝试了网上的所有解决方案,但都不起作用。希望你能找到解决方法。 - Arshya
这个链接可能会有所帮助:http://snipplr.com/view/47762/ - updev
为什么要预定义new byte[4096];,文件大小可能会更多或更少。这样做不会产生问题吗? - Psychonaut007
6个回答

28

这个链接描述了如何使用SharpZipLib从MemoryStream创建一个zip文件:https://github.com/icsharpcode/SharpZipLib/wiki/Zip-Samples#wiki-anchorMemory。通过使用这个库以及iTextSharp,我能够将多个在内存中创建的PDF文件压缩成为一个zip文件。

以下是我的代码:

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");

创建PDF方法:

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;
}

谢谢你的回答,它对我也起作用了。请问你能告诉我如何压缩文本文件吗? - Jitender Kumar

2
以下代码是从Azure Blob存储中的目录获取文件,合并为一个zip文件并再次保存在Azure Blob存储中。
    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);

需要以下命名空间:

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

1
以下是创建一个zip文件并将其存储在MemoryStream中的代码,使用的是ICSharpCode.SharpZipLib dll中存在的ZipOutputStream类。
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;

1

这段代码将帮助您通过多个PDF文件创建ZIP文件,您将从下载链接中获取每个文件。

        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);
                    }
                }

需要命名空间: System.IO.Compression; System.IO.Compression.ZipArchive;


1
我使用了这个线程的信息,但决定发布我的端到端代码,因为它包括下载由后端服务器生成的zip文件的所有元素。
前端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
}

在Azure App Service中使用C#编写的Web API Core 5后端。全内存解决方案的工作原理是因为我完全没有使用任何文件资源。使用了SharpLibZip包。

`

\\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;
}

`

从表中读取blob数据并将其作为字节数组返回,然后转化为内存流的Sql Server代码。

`

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));
}

`


0

您可以生成PDF文件并将其存储在IsolatedStorageFileStream中,然后可以从该存储中压缩内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接