How to stream S3 object directly from Java web server to browser - java

What I have tried so far is
S3Object object = s3Object.getObject(new getObjectRequest(bucketName,FileName));
InputStreamResource inputStreamResource = object.getObjectContent();
// Setting Headers.
return new ResponseEntity(inputStreamResource,headers, HttpStatus.OK);
But this code is not streaming date to browser. I has first downloaded to server and then sent back to browser but requirement is to directly stream to browser without downloading at server.
Any pseudocode or link will help.
Thanks

Instead of calling getObject(), create a signed GET request and send a 302 redirect to the client. The browser will then retrieve the object directly from Amazon. In case you aren't already using it, the jets3t library makes dealing with S3 signatures much simpler.

Related

How to completely abort the output stream download?

we're currently working on the service that would archive the data and return it to the user as a ZipOutputStream. What we're currently looking for is an option to completely terminate the operation if something goes wrong on the server side. With our current implementation (just closing the response output stream) errors result in a malformed zip at the user side, but it can't be told if the archive is malformed or not before attempting to unzip it. The desired behavior would be something like download termination (from a browser perspective, for instance, it would result in an unsuccessful download indication (red cross icon or something similar, depending on the browser) explicitly telling the user that something went wrong). We're using Spring Boot, so any java code examples would really be appreciated, but if you know the underlying HTTP mechanism that is responsible for this kind of behavior, and can point in the right direction, that would be much appreciated too.
Here's what we have as of now (output being a response output stream of a Spring REST controller (HttpServletResponse.getOutputStream()) :
try (ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream)) {
try {
for (ZipRecordFile fileInfo : zipRecord.listZipFileOverride()) {
InputStream fileStream = getFileStream(fileInfo.s3region(), fileInfo.s3bucket(),
fileInfo.s3key());
ZipEntry zipEntry = new ZipEntry(fileInfo.fileName());
zipOutputStream.putNextEntry(zipEntry);
fileStream.transferTo(zipOutputStream);
}
}
catch (Exception e) {
outputStream.close();
}
}
There isn't a (clean) way to do what you want:
Once you have started writing the ZIP file to the output stream, it is too late to change the HTTP response code. The response code is sent at the start of response.
Therefore, there is no proper way for the HTTP server to tell the HTTP client: "Hey ... ignore that ZIP file I sent you 'cos it is corrupt".
So what are the alternatives?
On the server side, create the entire ZIP as an in-memory object or write it to a temporary file. If you succeed, send an 2xx response followed by the ZIP data. If you fail, send a 4xx or 5xx response.
The main problem is that you need enough memory or file system space to hold the ZIP file.
Redesign your HTTP API so that the client can sent a second request to check if the first request's response contained a complete ZIP file.
You might be able to exploit MIME multipart encoding; see RFC 1341. Each part of a well-formed MIME multipart has a start marker and an end-marker. What you could try is to have your web-app construct the multipart stream containing the ZIP "by hand". If it decides it must abort the ZIP, it could just close the output stream without adding the required end marker.
The main problem with this is that you are depending on the HTTP stack on the client side to tell the browser (or whatever) that the multipart is corrupted. Furthermore, the browser (or whatever) must not pass on the partial (i.e. corrupt) ZIP file on to the user. I'm not sure if you can rely on (particular) web browsers to do that.
If you are running the download via custom code on the client side, you could conceivably implement your own encapsulation protocol. The effect would be the same as for 3 ... but you wouldn't be abusing the MIME spec.

Amazon S3 AWS SDK [Java] - MultiPart Upload how to get a custom header in the http response?

how can I access a custom header from a server response when using TransferManager ?
we have a custom header added in the response from our server, from the client side we use multi part upload with default transfer manager
any suggestion how in how i could hook up it ?
so basically i want to pass over the response from the return response.getAwsResponse(); found in the class: AmazonS3Client on the method
private <X, Y extends AmazonWebServiceRequest> X invoke(Request<Y> request,
HttpResponseHandler<AmazonWebServiceResponse<X>> responseHandler,
String bucket, String key, boolean isAdditionalHeadRequestToFindRegion) {
that response will have the HTTP response from the server containing the custom heather which I'm after, basically is a unique Id send back when the file was 100% completed so than i can manipulate it.
I need to pass over this custom header from the response to the very beginning where I use the transfer manager and the upload.waitForCompletion(),
also i don't want to edit the amazon's,
so does anyone know if there is an interface or some other object which provides me access to it ?
After some debug into the framework I strongly believe that there is no way to have access to the http response when using the TransferManager
for what we are trying to do we need to send an unique id from the server to the client when the file upload is completed and assembled
** therefore if you don't mind in do not use the beauty of the TransferManager you could write "your own TransferMananger" than you will have full control, but again on the client side we don't really want to add custom code but have a standard and simple approach (but that is for my scenario), if you decide to do it manually it can be done I have already tried and works !
So as a alternative we though in send from the server via the eTag, which is not great but will do the job and will keep the client side simple and clean
Any suggestion in how to send this value back in a better way ?
Upload up = tm.upload(bucketName, file.getName(), file);
UploadResult result = (UploadResult) ((UploadImpl) up).getMonitor().getFuture().get();
String uniqueIdFromServer = result.getETag();

Downloading binary file from url

I am using this code to download files from a url:
FileUtils.copyURLToFile(url, new File("C:/Songs/newsong.mp3"));
When I create the url using for instance,
"https://mjcdn.cc/2/282676442/MjUgU2FhbCAtIFZlZXQgQmFsaml0Lm1wMw==",
this works just fine and the mp3 is downloaded.
However,
if I use another url:
"https://dl.jatt.link/hd.jatt.link/a0339e7c772ed44a770a3fe29e3921a8/uttzv/Hummer-(Mr-Jatt.com).mp3",
the file is 0kb.
I am able to download files from both these urls from within a web browser.
What's wrong here, and how can I fix it.
I noticed a difference between your 2 URLs:
The first one just gives back the file without redirection.
But the second one responds with a redirect (HTTP/1.1 302 Moved Temporarily). It's also a special case, because it's a redirect from HTTPS to HTTP protocol.
Browsers can follow redirects, but your program - for some reason (see below) - can't.
I suggest you to use a HTTP client library (e.g. Apache HTTP client or Jsoup), and configure it to follow redirects (if they don't do it by default).
For example, with Jsoup, you would need a code like this:
String url = "https://dl.jatt.link/hd.jatt.link/a0339e7c772ed44a770a3fe29e3921a8/uttzv/Hummer-(Mr-Jatt.com).mp3";
String filename = "C:/Songs/newsong.mp3";
Response r = Jsoup.connect(url)
//.followRedirects(true) // follow redirects (it's the default)
.ignoreContentType(true) // accept not just HTML
.maxBodySize(10*1000*1000) // accept 10M bytes (default is 1M), or set to 0 for unlimited
.execute(); // send GET request
FileOutputStream out = new FileOutputStream(new File(filename));
out.write(r.bodyAsBytes());
out.close();
Update on #EJP's comment:
I looked up Apache Commons IO's FileUtils class on GitHub. It calls openStream() of the received URL object.
openStream() is a shorthand for openConnection().inputStream().
openConnection() returns an URLConnection object. If there is an appropriate subclass for the protocol used by URL, it will return an instance of that subclass. In this case that's a HttpsURLConnection which is the subclass of HttpURLConnection.
The followRedirects option is defined in HttpURLConnection and it's indeed true by default:
Sets whether HTTP redirects (requests with response code 3xx) should be automatically followed by this class. True by default.
So OP's approach would normally work with redirects too, but it seems that redirection from HTTPS to HTTP is not handled (properly) by HttpsURLConnection. - It's the case that #VGR mentioned in the comments below.
It's possible to handle redirects manually by reading the Location header with HttpsURLConnection, then use it in a new HttpURLConnection. (Example) (I wouldn't be surprised if Jsoup did the same.)
I suggested Jsoup because it already implements a way to handle HTTPS to HTTP redirections correctly and also provides tons of useful features.

Send and receive different types of object in netty

I am building an application in Netty which will allow people to download and upload file. But before that they should be able to log into the server. Once they log in they can download or upload the file.
Issue I am having is how will I know the data I have received is a string(username password string) or a chunked file(as I send files as ChunkedFile) or any other java object. How will I properly get them to original form. I know we can use encoder and decoders but there are no proper example available which shows anything close to the problem I am having?
Thanks
Send a POST http request with Headers
// Prepare the HTTP request.
FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uriJson.toASCIIString());
request.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");
ByteBuf buf = Unpooled.copiedBuffer(data, CharsetUtil.UTF_8);
request.content().writeBytes(buf);
request.headers().set(CONTENT_LENGTH,request.content().readableBytes());

The best way to send a file over a Network

I want to send a file to the Browser via the REST Interface.
Can you suggest the most efficient way to do it, Keeping in mind the following?
Not much traffic.
I am fetching the file from HBase which means when I fetch it from HBase I get it in Byte Array.
The files are not in any folder in the server. The files can only be fetched from the HBase table.
The Front end is PHP and I do not know PHP.
In the REST api you can just pass the byte array to Response and it takes care of itself.
Using the following code -
#Produces("image/jpg")
public Response getImage() {
<Fetch it from where ever you have it>
Response.ok(<byteArrayOfTheFile>).build();
}
I am giving case study of WebService by which i send file:
It is always good to encode the file content and send it to the destination where they will be decode it and read the content.
Sending as an attachment is always open to the world becasue it is not encrypted.And if the network having high trafic chances of failure is high.

Categories