7

I'm using .net core to upload and retrieve an image from a private Amazon S3 bucket.

I'm able to upload it successfully, and even view it after I download it from S3, however when I'm a bit unsure about how to return the stream/response back to the client for the actual API call (for example right now I'm just trying to use Postman/Fiddler proxy tools to get back the image from my API)

My code for S3 to retrieve the stream:

///Retrieve my image from my bucket

public async Task<string> ReadObjectData(MediaFolder key, String fileName)
    {
        string responseBody = "";
        IAmazonS3 client;

        using (client = new AmazonS3Client(accessKey, accessSecret, endpoint))
        {
            Amazon.S3.Model.GetObjectRequest request = new Amazon.S3.Model.GetObjectRequest
            {
                BucketName = bucket,
                Key = key + "/" + fileName,
            };
            using (GetObjectResponse response = await client.GetObjectAsync(request))
            using (Stream responseStream = response.ResponseStream)
            using (StreamReader reader = new StreamReader(responseStream))
            {
                string title = response.Metadata["x-amz-meta-title"];
                responseBody = reader.ReadToEnd();
            }
        }
         return responseBody;
    }

So now in my controller, I have the following action:

   [HttpGet("ProfilePic")]
    public async Task<IActionResult> GetProfilePicture()
    {
        var user = await GetUserFromBearerToken();

        //Retrieve
        var utf8ImageResponse = await _fileService.ReadObjectData(MediaFolder.Profiles, user.ProfileImageFileName);

        //To return a file as a stream
        var imageBytes = System.Text.Encoding.UTF8.GetBytes(utf8ImageResponse);

        //Return the image, which I'll hardcode as jpeg for a test
        return File(imageBytes, "image/jpeg");
    }

When I make the call using Postman, it returns a little blank box (the box you'd see if you tried to return an image, but it wasn't a valid image or null in some way).

Right now I'm using Postman but ideally I'd want an app to present this image.

Any ideas what I'm doing wrong? I tried messing around with base64 encoding and other things but nothing seems to work.

Thanks!

NullHypothesis
  • 4,286
  • 6
  • 37
  • 79

1 Answers1

16

This way you can retrieve the file as stream from S3 storage

public async Task<Stream> ReadObjectData(MediaFolder key, String fileName)
{
    try
    {
        using (var client = new AmazonS3Client(accessKey, accessSecret, endpoint))
        {
            var request = new GetObjectRequest
            {
                BucketName = bucket,
                Key = key + "/" + fileName
            };

            using (var getObjectResponse = await client.GetObjectAsync(request))
            {
                using (var responseStream = getObjectResponse.ResponseStream)
                {
                    var stream = new MemoryStream();
                    await responseStream.CopyToAsync(stream);
                    stream.Position = 0;
                    return stream;
                }
            }
        }
    }
    catch (Exception exception)
    {
        throw new Exception("Read object operation failed.", exception);
    }
}

And then - return this stream as FileStreamResult:

[HttpGet("ProfilePic")]
public async Task<IActionResult> GetProfilePicture()
{
    var user = await GetUserFromBearerToken();

    Stream imageStream = await _fileService.ReadObjectData(MediaFolder.Profiles, user.ProfileImageFileName);

    Response.Headers.Add("Content-Disposition", new ContentDisposition
    {
        FileName = "Image.jpg",
        Inline = true // false = prompt the user for downloading; true = browser to try to show the file inline
    }.ToString());

    return File(imageStream, "image/jpeg");
}
Dmitry Pavlov
  • 30,789
  • 8
  • 97
  • 121
  • 1
    THANK YOU! I really appreciate you not only helping me, but even typing the code out in my own function to get it to work. I didn't need to change anything. TYSM :) – NullHypothesis Sep 19 '18 at 20:47
  • Additional improvements for your code: I would suggest to 1) add key format checking (check for wrong slashes and replace them with correct ones if needed) 2) in catch block - `exception` most likely will be `AggregateException` you should take care of flattening ithttps://stackoverflow.com/questions/22872995/flattening-of-aggregateexceptions-for-processing 3) Add pre-check that storage file exists – Dmitry Pavlov Sep 20 '18 at 15:21
  • Why copy to MemoryStream when you can just give it to `return File(streamFromS3)` ? – Alex from Jitbit Mar 11 '20 at 18:13
  • 1
    Yep, you can without copying it to memory as well @Alex – Dmitry Pavlov Mar 11 '20 at 19:08