Download File from S3 Bucket API Using AWS .Net SDK

 


In one of my previous posts Upload File to S3 Bucket API Using AWS .Net SDK I explain how to upload a file to an S3 bucket and get the file path where your file was stored. Assuming that you have persisted this file path somewhere somehow, I will show you in this post how to download the file using the file path.

Using the same set-up in my previous post linked above let's start with adding an interface method that performs the needed service.

File Data

When the S3 SDK gets you the object that exists in the file path and bucket name provided, it's in the form of a stream.

You should have a content type so that you are able to tell the receiver of your API what type of file they are getting. Luckily, we can get this information from the Content-Type header from the response, but we need a model that can hold this piece of information.

So, let's create a class named FileData which can hold the stream and the content type of the file received from S3.

namespace Domain.Models

{

    public record FileData

    {

        public MemoryStream? Stream { get; set; }

        public string ContentType { get; set; } = string.Empty;

    }

}

It's just a simple record class that can represent the response of the objects we receive from S3.

Interface

We need a method that given a file path and a bucket name can get an object from S3.

So, just add the following method signature to the IAWSS3Service interface we previously introduced in the post linked above.

 public Task<FileData>? GetObjectAsync(string filePath, string bucketName);

Now let's add the implementation of this function in the service.

Implementation

Like my previous post I will be using the S3 client I declared in the AWSS3Service class. 

After you receive the response, just copy the received stream to your FileData Stream and as explained above I will read the Content-Type header and store it in FileData ContentType.
 public async Task<FileData>? GetObjectAsync(string filePath, string bucketName)
 {
   using (s3Client)
   {
    var s3Request = new GetObjectRequest
    {
      BucketName = bucketName,
      Key = filePath
      };
      using (var response = await s3Client.GetObjectAsync(s3Request))
      {
      var contentType = response.Headers["Content-Type"];
      using (var responseStream = response.ResponseStream)
      {
        var stream = new MemoryStream();
        await responseStream.CopyToAsync(stream);
        stream.Position = 0;
        return new FileData
        {
          Stream = stream,
          ContentType = contentType
        };
        }
         }
    }
  }
Great! Now this service should give you the FileData response you expect. Let's set up an API that uses this service.

API

Let's create an API of type POST which takes filePath as a parameter and fetches the bucket name from appsettings.json and calls our GetObjectAsync function.
 [HttpPost]
 [Route("download-file-from-s3")]
 public async Task<ActionResult> DownloadFileFromS3(string filePath)
  {
     var bucketName = _configuration["AWS:BucketName"];

     var fileData = await _awsS3Service.GetObjectAsync(bucketName, filePath);

     if (fileData == null || fileData.Stream == null)
         return NotFound("No Attachments found");

     return File(fileData.Stream.ToArray(), fileData.ContentType, filePath[(filePath.LastIndexOf("/") + 1)..]);
  }
If the file was successfully retrieved from the bucket, you can return it as a File including its stream, content type and file name. Otherwise just return a NotFound response.

Perfect! Now your API should be able retrieve the object you need.

Comments

Popular posts

Why I Hate Microservices Part 1: The Russian Dolls Problem 🪆🪆🪆

Why I Hate Microservices Part 3: The Identity Crisis 😵

Why I Hate Microservices Part 2: The Who's Telling the Truth Problem 🤷