Search code examples
c#asp.netazure-blob-storagegzipstream

How do you compress a stream and upload it to Azure Blob Storage without writing to a file?


I have written a Post method in ASP.NET Core to compress the requests body and upload it to Azure Blob Storage. The method takes parameters as follows:

public async Task<IActionResult> Post([FromHeader] string AssignmentId)

Various strings are then set, including fetching the connection string for the storage:

string fileName = $"{AssignmentId}.gz";
string compressedFilePath = Path.Combine(hostEnvironment.ContentRootPath, $"Test JSONs/{fileName}");
string connectionString = Environment.GetEnvironmentVariable("AZURE_STORAGE_CONNECTION_STRING");

I initialize the BlobClient:

BlobClient blobClient = new BlobClient(connectionString, "assignments", fileName);

Then I create a file, and compress the body stream of the request using GZipStream to the file:

using (FileStream compressedFileStream = System.IO.File.Create(compressedFilePath))
{
    using GZipStream compressionStream = new GZipStream(compressedFileStream, CompressionMode.Compress);
    using Stream bodyStream = HttpContext.Request.Body;
    await bodyStream.CopyToAsync(compressionStream);
}

Finally I read the file I just wrote and upload using the FileStream:

using (FileStream fileStream = System.IO.File.OpenRead(compressedFilePath))
{
    await blobClient.UploadAsync(fileStream);
}

This solution works, but I am concerned about the constant reading and writing of the file, in terms of speed. I attempted to use a MemoryStream passed into the GZipStream, however it ended up only uploading 10B files when the files should be 1KB+.

I appreciate any suggestions.

Here is the complete method:

public async Task<IActionResult> Post([FromHeader] string AssignmentId)
{
    string fileName = $"{AssignmentId}.gz";
    string compressedFilePath = Path.Combine(hostEnvironment.ContentRootPath, $"Test JSONs/{fileName}");
    string connectionString = Environment.GetEnvironmentVariable("AZURE_STORAGE_CONNECTION_STRING");

    BlobClient blobClient = new BlobClient(connectionString, "assignments", fileName);
    
    using (FileStream compressedFileStream = System.IO.File.Create(compressedFilePath))
    {
        using GZipStream compressionStream = new GZipStream(compressedFileStream, CompressionMode.Compress);
        using Stream bodyStream = HttpContext.Request.Body;
        await bodyStream.CopyToAsync(compressionStream);
    }

    using (FileStream fileStream = System.IO.File.OpenRead(compressedFilePath))
    {
        await blobClient.UploadAsync(fileStream);
    }
    
    return Ok();
}

Solution

  • I eventually solved this by both leaving the compression stream open, and by resetting the position of the memory stream the compression stream is writing to (thanks to @MitchWheat !).

    using MemoryStream memoryStream = new MemoryStream() ;
    using (Stream bodyStream = HttpContext.Request.Body)
    {
        using (GZipStream compressionStream = new GZipStream(memoryStream, 
        CompressionMode.Compress, true))
        {
            await bodyStream.CopyToAsync(compressionStream);
        }
    }
    memoryStream.Position = 0;
    
    await blobClient.UploadAsync(memoryStream, overwrite: true);