Search code examples
asp.net-core-webapi.net-6.0minio

ASP.NET Core 6 Web API stream file directly from MinIO


My task is to get a .pdf file from MinIO storage and show it in application.

If file has a big size I have to wait a lot time before it downloads from MinIO and I can start to send it to UI.

This is my code sample:

public async Task<byte[]> DownloadAsync(Guid fileId, string? fileTag = default, Guid? versionId = default)
{
    var bucketName = string.IsNullOrEmpty(fileTag) ? _defaultBucketName : fileTag;

    var memoryStream = new MemoryStream();
    var minioClient = CreateMinioClient();

    var args = new GetObjectArgs();
    args.WithBucket(bucketName);
    args.WithObject(fileId.ToString());
    args.WithCallbackStream((stream) => stream.CopyTo(memoryStream));
    // args.WithRequestBody()

    if (versionId != null)
    {
        args.WithVersionId(versionId.ToString());
    }

    await minioClient.GetObjectAsync(args);

    return memoryStream.ToArray();
}

As we can see we get stream in callback method and have to copy it to memory before we can send it to frontend.

[HttpGet]
[Route("gettestpdf")]
[AllowAnonymous]
public async Task<FileResult> GetTestPdf()
{
    var fileData = await _fileLoader.DownloadAsync(Guid.Parse("e691e105-5240-4df5-a02f-e5d8e7a73b36"), "pdf");

    var memStream = new MemoryStream();
    memStream.Write(fileData, 0, fileData.Length);
    memStream.Seek(0, SeekOrigin.Begin);

    return File(memStream, "application/pdf", "test.pdf");
}

But if the .pdf document is linearized, we can start to view it before it fully loads.

Is here a way to start sending the .pdf file stream from storage to the UI before we copy the full stream? Some way to send stream bytes from MinIO before object will load and copied fully?


Solution

  • I have found a solution!

    We can get link instead on object

            public async Task<string> GetDownloadLinkAsync(Guid fileId, string? fileTag = null, Guid? versionId = null)
        {
            var bucketName = string.IsNullOrEmpty(fileTag) ? _defaultBucketName : fileTag;
    
            var minioClient = CreateMinioClient();
    
            var args = new PresignedGetObjectArgs()
                .WithBucket(bucketName)
                .WithObject(fileId.ToString())
                .WithExpiry(1200);
    
            return await minioClient.PresignedGetObjectAsync(args);
        }
    

    And then send it as a PhisycalFileResult.

            [HttpGet]
        [Route("gettestpdf")]
        [AllowAnonymous]
        public async Task<PhysicalFileResult> GetTestPdf()
        {
            var downloadUrl = await _fileLoader.GetDownloadLinkAsync(Guid.Parse("e691e105-5240-4df5-a02f-e5d8e7a73b36"), "pdf");
            return PhysicalFile(downloadUrl, "application/pdf", "test.pdf");
        }
    

    As result, we download it only once (from MinIO to UI) and we can view it before it will be downloaded fully (because pdf file is linearized).