I am assuming only s3:GetObject is needed for code
import software.amazon.awssdk.core.async.AsyncResponseTransformer;
import software.amazon.awssdk.services.s3.S3AsyncClient;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
private final S3AsyncClient s3;
@GetMapping(path = "/{file}")
Mono<ResponseEntity<Flux<ByteBuffer>>> downloadFile(@PathVariable("file") String file) {
final var request =
GetObjectRequest.builder()
.bucket("my-bucket")
.key("aPrefix/" + file)
.build();
return Mono.fromFuture(s3.getObject(request, AsyncResponseTransformer.toPublisher()))
.map(
response ->
ResponseEntity.ok()
.header(CONTENT_TYPE, response.response().contentType())
.header(
CONTENT_LENGTH,
Long.toString(response.response().contentLength()))
.header(CACHE_CONTROL, CACHE_CONTROL_VALUE)
.body(Flux.from(response)));
}
I am surprised to see error:
... is not authorized to perform: s3:ListBucket on resource: ...
What and why there is listing?
Documentation for GetObject sheds some light
If the object that you request doesn’t exist, the error that Amazon S3 returns depends on whether you also have the s3:ListBucket
permission.If you have the s3:ListBucket permission on the bucket, Amazon S3 returns an HTTP status code 404 Not Found error.
If you don’t have the s3:ListBucket permission, Amazon S3 returns an HTTP status code 403 Access Denied error.
So this could be an indication that the object does not exist. So it seems to be due to SDK implementation as I assume GetObject is invoked somewhere by GetObjectRequest behind the scenes.