File upload handling and buffering internals with Jersey API - java

How does Jersey handle receiving files from a client (e.g. web application)?
I've looked around for a longer while and cannot seem to get the answers I need.
Let's say that I have an exposed Jersey endpoint that consumes a multipart data form with a file that looks pretty much like this:
#POST
public Response upload(#FormDataParam("file") InputStream inputStream,
#FormDataParam("file") FormDataContentDisposition fileDetails { ... }
I've noticed that if I try to upload the file, the endpoint is not called until the uploading is finished. Does Jersey attempt to read and buffer (memory? disk?) an entire file before handling it for further processing? Does it mean that the inputStream source and size is already known when the processing of endpoint logic starts (because it was already read)?
And finally, is it possible to handle it in a "as comes" manner, without waiting with further actions for whole file to be uploaded first?

Related

Is it possible to send an ouput stream as response to a HTTP request?

Consider the case where Server B makes a HTTP request to Server A (Which is an encryption server) and get an output stream (which is encrypted). So that Server B could write with the help of this encrypted stream.
Now this encrypted output stream would be opened in Server A and closed at Server B.
Is it possible to send an output stream in HTTP response?
Is it a right way to send an output stream in response? Or are there any conventions like the output stream must be closed in the same server (or same application) where it is originated?
Yes, it's possible. That's how the file download works.
First you'll have to set the Content-Type that you're going to provide. If it's simple binary file then set it as application/octet-stream. After that get outputStream of the response and dump the file content in it.
Like below
response.setContentType("application/octet-stream");
response.setContentLength(fileSizeInBytes);//new File('myfile').length(), optional step
response.getOutputStream().write(fileBytes);//do it in chunks
#Edit
Streams are endpoints of data channels. Like an HTTP address is an endpoint to the server resource.
In case of stream our program doesn't need to know where the actual resource resides. I just need to know how to interact with the stream.
So in case of HttpServletResponse stream, data lies in your server. Client's browser (or any other client) establishes a connection with your server. When we call methods on the stream like read/write, data over this connection is sent or received. These calls in case of HttpServletResponse result in HTTP packet transfer over TCP connection.
For more information on Java Stream (or any other language with similar concept) check here.
https://docs.oracle.com/javase/tutorial/essential/io/streams.html

Using Jersey 2.x client, how can I read from an InputStream while the server is still writing to its OutputStream?

We recently upgraded from Jersey 1.x to 2.x and most of the migration went smoothly. There's one snag though.
In 1.x, the following code would let us grab the InputStream while the server was still writing to its respective OutputStream:
final ClientResponse response = webResource
.accept(acceptHeader)
.get(ClientResponse.class);
final InputStream stream = response.getEntity(InputStream.class);
/* Process the stream, waiting if necessary */
We are using this as a sort of server-send event (before we found out about sse), but a similar and more common problem would be downloading a large file. The Jersey 2.x code looks like:
final Response response = webTarget
.request()
.accept(acceptHeader)
.get(); /* debug shows this call hanging */
final InputStream stream = response.getEntity(InputStream.class);
/* Process the stream, waiting if necessary */
The get() method hangs because the server never closes the connection. Fortunately in our case, the server is just waiting for "events" to send to the client, but if the client were downloading say a 64 GB file...
Turns out the problem was on the server side.
In Jersey 1.x, the server was not buffering the response (or we had overridden that behavior and forgotten). The solution was to set the property jersey.config.contentLength.buffer to 0. This prevented the server from buffering, and the code listed in this question worked without modification.

What's the proper way to return a file as the response?

Basically I need to provide REST service that would receive a String param, use that param to fetch a file from another system and then return the fetched file back as the response.
The effect should be the same as when a user clicks on a pdf or any other binary file link and the browser prompts him to save/download that file.
A couple of points:
is it possible to stream the file (to send bytes as I receive them from source system). In other words, how to handle very large files?
also related to streaming, when using regular HttpServletResponse, do I have to wait until a large file is completely read to return response.build()?
How do I go around doing this using Apache Wink?
PS Sorry, this may be trivial for Wink gurus, but I'm just starting to wrap my head around developer guide.
You can just return the java.io.File from your method. You can wrap it with Response if you like. Wink will handle the streaming. The streaming doesn't start when you call to response.build(), but rather when your method finishes.
If you want a correct download dialog, you should return the proper Content-Disposition header. See How to set response header in JAX-RS so that user sees download popup for Excel?

Sending an error response after servlet response has been written to

I am writing a data transfer application using a servlet and would like to be able to send an error response if a problem occurs after the servlet response has been written to. Is that possible?
My issue is that I will be sending large compressed csv files that are created from data read from a database. Everything is done with streams so it is possible that an error could occur in the creation of the csv file after the servlet response has been written to. I have seen it happen.
I've noticed that this is only a problem after the servlet OutputStream has been flushed. If it has not been flushed I can send an error response but not after. Since I am dealing with large amounts of data it is not feasible to send everything in one go.
I am writing a data transfer application using a servlet and would like to be able to send an error response if a problem occurs after the servlet response has been written to. Is that possible?
Not from the server side on. The server cannot take the already flushed bytes back from the client. This is a point of no return. I assume that this concerns a different exception than IOException on the response's Writer or OutputStream.
If it were HTML (even though this is a poor practice; HTML belongs in JSP), you could print some JS code which forces a location change like so:
try {
writer.write(someHtml);
} catch (SomeException e) {
writer.write("<script>window.location = 'error.jsp';</script>");
// ...
}
But this is not possible in non-HTML responses. You'd really need to buffer the entire response in memory or on (temp) disk beforehand. If buffering went flawlessly, then you can pipe it to the response again.
try {
processAndSaveInMemoryOrTempDiskFile(someData, byteArrayOrFileLocation);
} catch (SomeException e) {
throw new ServletException(e, "Processing some data failed.");
}
copyFromMemoryOrTempDiskToResponse(byteArrayOrFileLocation, writer);

Find if InputStream of DataHandler is empty

In my application I develop web service that get attached file.
The file is mapped to DataHandler object via JaxB,
and I have access to the file via DataHandler.getInputStream()
My problem is this:
When the file attribute exist in the web service request, but no file is attached,
I still get the DataHandler object, and its getInputStream().available() = 11 bytes
(a header I guess...??).
So I can I know that the inputStream is empty?
Thanks,
Alon
Read it and parse the data as it should be parsed. The answer is in there.
The InputStream#available() certainly does not return the length of the stream or so as you seem to think. In some cases it (by coincidence) may, but you shouldn't rely on that. It just returns the amount of bytes which are available for read without blocking other threads. Just read the stream the usual Java IO way fully until the last bit returned -1 and then intercept on the whole data you received.

Categories

Resources