Building a ZIP in memory is useful when you need to stream a download, pass bytes to another service, or store an archive in a database without touching disk. Aspose.ZIP for .NET exposes a clean API to create ZIP archives using streams, choose compression settings, and save the result to a MemoryStream or directly to the HTTP response.
This guide provides complete, correct code you can paste into a console app or ASP.NET Core project.
Prerequisites
- .NET 6 or later
- NuGet:
Aspose.Zip
dotnet add package Aspose.Zip
Namespaces used:
using Aspose.Zip; // Archive, ArchiveEntry
using Aspose.Zip.Saving; // DeflateCompressionSettings, CompressionLevel
Quick start: create a ZIP entirely in memory
This example adds entries from a string and a file on disk, saves the archive to a MemoryStream, and exposes the resulting byte array.
// File: Program.cs
using System;
using System.IO;
using System.Text;
using Aspose.Zip;
using Aspose.Zip.Saving;
class Program
{
static void Main()
{
// Prepare output buffer
using var zipBuffer = new MemoryStream();
// Choose compression (Deflate is the standard ZIP method)
var deflate = new DeflateCompressionSettings(CompressionLevel.Normal);
var entrySettings = new ArchiveEntrySettings(deflate);
using (var archive = new Archive())
{
// 1) Add a text file from memory
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("Hello from Aspose.ZIP in memory.")))
{
archive.CreateEntry("docs/readme.txt", ms, entrySettings);
}
// 2) Add a file from disk (streamed; not fully loaded in RAM)
var sourcePath = "report.pdf"; // ensure it exists
if (File.Exists(sourcePath))
{
using var fs = File.OpenRead(sourcePath);
archive.CreateEntry("reports/2025/report.pdf", fs, entrySettings);
}
// 3) Save the ZIP to our in-memory buffer
archive.Save(zipBuffer);
}
// Use the ZIP bytes as needed (send over network, write to DB, etc.)
byte[] zipBytes = zipBuffer.ToArray();
Console.WriteLine($"ZIP size: {zipBytes.Length} bytes");
}
}
Key points
new Archive()creates an empty ZIP.CreateEntry(entryName, stream, entrySettings)adds a file from any readable stream.archive.Save(stream)writes the archive to your chosen stream (memory, network, response body).
Add an entire folder tree without writing temp files
Walk a directory recursively, preserve relative paths, and write the final archive to memory.
using System.IO;
using Aspose.Zip;
using Aspose.Zip.Saving;
static class InMemoryZipper
{
public static byte[] ZipFolderToBytes(string sourceFolder, CompressionLevel level = CompressionLevel.Normal)
{
if (!Directory.Exists(sourceFolder))
throw new DirectoryNotFoundException(sourceFolder);
var deflate = new DeflateCompressionSettings(level);
var entrySettings = new ArchiveEntrySettings(deflate);
using var buffer = new MemoryStream();
using (var archive = new Archive())
{
var root = Path.GetFullPath(sourceFolder);
foreach (var filePath in Directory.GetFiles(root, "*", SearchOption.AllDirectories))
{
var rel = Path.GetRelativePath(root, filePath).Replace(Path.DirectorySeparatorChar, '/');
using var fs = File.OpenRead(filePath);
archive.CreateEntry(rel, fs, entrySettings);
}
archive.Save(buffer);
}
return buffer.ToArray();
}
}
ASP.NET Core: stream a ZIP download without disk I/O
This endpoint builds a ZIP in memory from multiple sources and returns it with the correct content type and a download filename.
// File: Program.cs (minimal API)
using System.Text;
using Aspose.Zip;
using Aspose.Zip.Saving;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/download-zip", () =>
{
using var buffer = new MemoryStream();
var deflate = new DeflateCompressionSettings(CompressionLevel.Normal);
var settings = new ArchiveEntrySettings(deflate);
using (var archive = new Archive())
{
// Add dynamic content (for example, a CSV generated on the fly)
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("id,name\n1,Alice\n2,Bob\n")))
archive.CreateEntry("data/users.csv", ms, settings);
// Add a static file from disk if available
var logo = "wwwroot/logo.png";
if (File.Exists(logo))
{
using var fs = File.OpenRead(logo);
archive.CreateEntry("assets/logo.png", fs, settings);
}
archive.Save(buffer);
}
buffer.Position = 0; // rewind for reading
return Results.File(
fileContents: buffer.ToArray(),
contentType: "application/zip",
fileDownloadName: $"bundle-{DateTime.UtcNow:yyyyMMdd-HHmmss}.zip");
});
app.Run();
Why ToArray() here?
Minimal APIs require a concrete payload. For very large archives, prefer streaming directly to HttpResponse.Body:
app.MapGet("/stream-zip", async (HttpContext ctx) =>
{
ctx.Response.ContentType = "application/zip";
ctx.Response.Headers.ContentDisposition = $"attachment; filename=\"bundle.zip\"";
var deflate = new DeflateCompressionSettings(CompressionLevel.Normal);
var settings = new ArchiveEntrySettings(deflate);
using var archive = new Archive();
// Add entries...
using var ms = new MemoryStream(Encoding.UTF8.GetBytes("hello"));
archive.CreateEntry("hello.txt", ms, settings);
// Stream directly to the client without buffering full ZIP in RAM
await archive.SaveAsync(ctx.Response.Body);
});
Choose compression settings
DeflateCompressionSettings controls speed vs size:
var fastest = new DeflateCompressionSettings(CompressionLevel.Low); // fastest, larger files
var balanced = new DeflateCompressionSettings(CompressionLevel.Normal); // default balance
var smallest = new DeflateCompressionSettings(CompressionLevel.High); // slowest, smallest files
Pass the settings via new ArchiveEntrySettings(deflate) when creating entries. You can mix settings per entry if needed.
Add entries from streams safely
- Use
File.OpenRead(path)to stream large files without loading them fully into memory. - For generated content, write to a
MemoryStreamor aPipeWriter-backed stream and pass it toCreateEntry. - Dispose streams after each
CreateEntryto free resources promptly.
Example for large generated content:
using System.IO;
using Aspose.Zip;
using Aspose.Zip.Saving;
static void AddLargeGeneratedEntry(Archive archive, string name)
{
// simulate a big stream produced incrementally
using var temp = new FileStream(Path.GetTempFileName(), FileMode.Create, FileAccess.ReadWrite, FileShare.None, 81920, FileOptions.DeleteOnClose);
using var writer = new StreamWriter(temp);
for (int i = 0; i < 200_000; i++) writer.WriteLine($"row-{i},value-{i}");
writer.Flush();
temp.Position = 0;
var settings = new ArchiveEntrySettings(new DeflateCompressionSettings(CompressionLevel.Normal));
archive.CreateEntry(name, temp, settings);
}
Validation and error handling
- Check inputs exist before adding to the archive.
- Wrap creation in
try/catchand return a clear HTTP error for web APIs. - Normalize entry paths with forward slashes (
/) for consistent behavior across tools.
Performance checklist
- Choose
CompressionLevel.Lowfor real-time downloads when speed matters more than size. - Avoid loading massive entries fully into RAM; stream from files or network streams.
- For very large multi-GB archives, stream directly to
HttpResponse.Bodyor another target stream instead of buffering. - Dispose
Archiveand all input streams deterministically.
FAQ
Can I password-protect the in-memory ZIP?
Aspose.ZIP supports encrypted ZIP archives. Use TraditionalEncryptionSettings or AesEncryptionSettings via ArchiveEntrySettings. Apply per entry when calling CreateEntry.
Can I update an existing ZIP that I loaded into memory?
Yes. Load it into an Archive, add or remove entries, then Save back to a stream.
Does this work in Azure App Service or containers? Yes. In-memory and streamed zipping work well in sandboxed environments where disk access is limited.
Summary
You created a ZIP archive entirely in memory with Aspose.ZIP for .NET, added entries from streams, adjusted compression, and returned the archive from an ASP.NET Core endpoint without temporary files. Use these patterns to generate downloads, bundles, and exports efficiently in your C# applications.