0

I have a directory with a couple of files which I want to send to a client upon request. In order do this I have to somehow combine the files in a single file but I don't want to store this file (even temporarily) in a file system or entirely in memory. In other words I need to create an archive and stream it directly to the client.

A simple candidate for this seems to be a tarball. If I understand correctly a tarball doesn't contain a global file header but just a header for the individual files in the archive:
HeaderFile1-File1-glue-HeaderFile2-File2-finalizer

Assuming this is correct, it would be possible to generate a tarball without having to store it completely in memory or otherwise but I've looked into the libraries SharpCompress and SharpZipLib but neither seem to support something like this.

Before I try and implement it entirely myself two questions:

  1. Is there an easier way to stream directory content to a client.

  2. Does any commonly used library support the streaming of a tarball like I described?

FYI, I'm using ASP.net core 6


As an example using SharpCompress that first loads the tarball entirely in memory:

DirectoryInfo directoryOfFilesToBeTarred = new DirectoryInfo(dir);
FileInfo[] filesInDirectory = directoryOfFilesToBeTarred.GetFiles();
MemoryStream memStream = new MemoryStream();
var options = new TarWriterOptions(SharpCompress.Common.CompressionType.None, finalizeArchiveOnClose: true)
{
    LeaveStreamOpen = true
};
using (TarWriter writer = new TarWriter(memStream, options: options))
{
    foreach (FileInfo fileToBeTarred in filesInDirectory)
    {
        writer.Write(fileToBeTarred.Name, fileToBeTarred);
    }
}
memStream.Seek(0, SeekOrigin.Begin);
return memStream;
Jurgy
  • 2,128
  • 1
  • 20
  • 33
  • 1
    This post may help you: [Writing to ZipArchive using the HttpContext OutputStream](https://stackoverflow.com/questions/16585488/writing-to-ziparchive-using-the-httpcontext-outputstream). – Chen Oct 06 '22 at 07:05

1 Answers1

0

Thanks to the comment of Chen I found a working solution with zip archives.

It seems obvious now, but the trick was to directly write to the response body's output stream.

using (ZipArchive archive = new ZipArchive(Response.BodyWriter.AsStream(), // <--
                                           ZipArchiveMode.Create,
                                           leaveOpen: true))
{
    foreach (string path in paths)
    {
        string fname = Path.GetFileName(path);
        archive.CreateEntryFromFile(path, fname);
    }
}

And then return new EmptyResult(); in the controller.


After some more tinkering the nicest way I found was to create a class derived from ActionResult and paste the above code in the overridden ExecuteResult method. The method has the ActionContext as argument from which you can get the response's BodyWriter.

In this case you can also easily set the response headers and works better in the asp.net pipeline.

Jurgy
  • 2,128
  • 1
  • 20
  • 33