2

I'm trying to return images from my S3 bucket. Everything works perfect, my service uploads and gets images, but the problem is that when I try to return them through the REST Controller, the image doesn't appear on browser.

Here is where I fetch the image from S3:

public S3Object getImageFromS3Bucket(String fileName) {
    S3Object object = s3client.getObject(new GetObjectRequest(bucketName, fileName));
    return object;
}

public byte[] getByteArrayFromImageS3Bucket(String fileName) throws IOException {
    InputStream in = getImageFromS3Bucket(fileName).getObjectContent();
    byte[] byteArray = IOUtils.toByteArray(in);
    in.close();

    return byteArray;
}

and here is my controller:

@RequestMapping(value = "/getImage/{fileName}", method = RequestMethod.GET)
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody
public ResponseEntity<byte[]> downloadImage(@PathVariable("fileName") String fileName) throws IOException {
    byte[] media = s3BucketTestService.getByteArrayFromFile(fileName);
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.IMAGE_PNG);
    headers.setContentLength(media.length);

    return new ResponseEntity<>(media, headers, HttpStatus.OK);
}

Here are the response headers:

Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Length: 10645
Content-Type: image/png
Date: Mon, 14 May 2018 09:41:47 GMT
Expires: 0
Pragma: no-cache
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block

I also have this bean in my configuration:

@Bean
public ByteArrayHttpMessageConverter byteArrayHttpMessageConverter() {
    ByteArrayHttpMessageConverter arrayHttpMessageConverter = new ByteArrayHttpMessageConverter();
    arrayHttpMessageConverter.setSupportedMediaTypes(getSupportedMediaTypes());
    return arrayHttpMessageConverter;
}

private List<MediaType> getSupportedMediaTypes() {
    List<MediaType> list = new ArrayList<MediaType>();
    list.add(MediaType.IMAGE_JPEG);
    list.add(MediaType.IMAGE_PNG);
    list.add(MediaType.APPLICATION_OCTET_STREAM);
    return list;
}

Anyone knows what am I doing wrong? The browser creates a png image with the exact size of the file I'm trying to get, so I guess the only problem is the visualization of the image?

PD: I don't want to make the image downloadable, I just want to generate that resource so the front-end can read it.

dgarceran
  • 177
  • 4
  • 14

3 Answers3

3

Try Using ImageIO API to convert the raw bytes from S3 bucket to Image Bytes as below:

public byte[] getByteArrayFromImageS3Bucket(String fileName) throws IOException {
    InputStream in = getImageFromS3Bucket(fileName).getObjectContent();

    BufferedImage imageFromAWS = ImageIO.read(in);
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ImageIO.write(imageFromAWS, "png", baos );
    byte[] imageBytes = baos.toByteArray();
    in.close();
    return imageBytes;

}
piy26
  • 1,574
  • 11
  • 21
0

Might be a silly question, but are you very sure that the MIME type of the image is in fact PNG? Perhaps the browser has been doing some MIME sniffing and is getting confused between what it finds as the advertised content-type vs what the file format appears to be? I see you have set X-Content-Type-Options: nosniff but I am just curious to ensure first that the file format is correct.

David
  • 7,652
  • 21
  • 60
  • 98
  • I might also suggest, checking the hash sum of the file against the original, you can do this in a number of ways - perhaps most common is sha or md5 sums. – David May 14 '18 at 10:51
  • Yes it is, I double checked that! I'm 100% sure it is a PNG file. I'll do that just in case... – dgarceran May 14 '18 at 11:06
  • If I return the image as base64 I can convert it and show it, so my image is there, it is PNG and the problem is still the same. Maybe I'll end up only returning base64 and leaving the front-end the responsability to handle it... – dgarceran May 14 '18 at 11:39
  • I'd still suggest confirming binary equality via a hash sum such as sha/md5. – David May 14 '18 at 14:10
  • also as suggested above, are you fetching the image via AJAX or as the src attribute of a image tag for example? Have you tried pasting the URL directly in the browser or saving the output of wget to the URL? – David May 14 '18 at 14:11
  • I used this online tool (that I don't understand why it changes anything) https://resttesttest.com/ to do an ajax request and it returns me the same error – dgarceran May 14 '18 at 14:27
0

I answered to one of similar question, it was related to PDF. If you just change the MediaType to PNG/JPEG, it should work. Take a look at it.

Red Boy
  • 5,429
  • 3
  • 28
  • 41
  • I tried to use it, but it returns this: `InputStream resource [resource loaded through InputStream] cannot be resolved to URL` – dgarceran May 14 '18 at 13:57
  • Got it, are you trying to render the Image on browser itself? that's the reason, in my example, it will open a save as popup. I think you might be catching the stream may be in Ajax Call. Could you post the client side code from where you are calling the service. – Red Boy May 14 '18 at 14:06
  • I only have the access to the backend! So yes, I only have the browser and postman ): – dgarceran May 14 '18 at 14:11
  • I did tested it in Postman, it works for sure. Try doing systematic debug- just get rid of your {filename} path parameter and try hitting service. If it works then make it dynamic, do further move ...like that. – Red Boy May 14 '18 at 14:19
  • I'm sorry, it didn't work for me ): I don't know what's wrong, maybe we're using different versions of the libraries... I'll just return a base64 String – dgarceran May 14 '18 at 14:53