I'm developing an HTTP server using HttpServer and HttpHandler.
The server should response to clients with XML data or images.
So far, I have developed HttpHandler implementations which respond to the clients with the XML data but I couldn't implement a HttpHandler which reads the image from file and send it to the client (e.g., a browser).
The image should not be loaded fully into memory so I need some kind of streaming solution.
public class ImagesHandler implements HttpHandler {
#Override
public void handle(HttpExchange arg0) throws IOException {
File file=new File("/root/images/test.gif");
BufferedImage bufferedImage=ImageIO.read(file);
WritableRaster writableRaster=bufferedImage.getRaster();
DataBufferByte data=(DataBufferByte) writableRaster.getDataBuffer();
arg0.sendResponseHeaders(200, data.getData().length);
OutputStream outputStream=arg0.getResponseBody();
outputStream.write(data.getData());
outputStream.close();
}
}
This code just sends 512 bytes of data to the browser.
You're doing way too much work here: decoding the image, and storing it in memory. You shouldn't try to read the file as an image. That is useless. All the browser needs is the bytes that are in the image file. So you should simply send the bytes in the image file as is:
File file = new File("/root/images/test.gif");
arg0.sendResponseHeaders(200, file.length());
// TODO set the Content-Type header to image/gif
OutputStream outputStream=arg0.getResponseBody();
Files.copy(file.toPath(), outputStream);
outputStream.close();
DataBufferByte stores its data in banks. getData() retrieves only the first bank, so you're declaring a length of only the first bank and then writing only the first bank.
Instead of your current write line, try this instead (untested):
arg0.sendResponseHeaders(200, data.getDataTypeSize(TYPE_BYTE));
OutputStream outputStream=arg0.getResponseBody();
for (byte[] dataBank : data.getBankData()) {
outputStream.write(dataBank);
}
outputStream.close
Related
I want to retrieve the pdf (Stored as BLOB) from Database using jersey api
I am using mybatis as data base framework .
I am able to download the pdf but the problem is i get the input stream as database to which i save it as file and then pass that it in Response but i don't want to save that file in server , i want file directly to be downloaded to user .
Current Process :
DATABASE-------> input stream-----> File -----------> add to response ----->user downloads it
retrieving making file passing file user downloads
What i want :
DATABASE---------->input stream------------> add to response -------> user downloads it
retrieving passing file user downloads
I want remove File making in server as data is confidential
Resource interface
#GET
#Path("v1/download/{id}")
#Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response downloadFile(#PathParam("id") int id) throws IOException, SQLException;
Resource Impl
#Override
public Response downloadFile(int id) throws IOException, SQLException {
// TODO Auto-generated method stub
File file = fileUploadService.downloadFile(id);
ResponseBuilder response = Response.ok(file);
response.header("Content-Disposition", "attachment;filename=aman.pdf");
return response.build();
}
Service method
#Override
public File downloadFile(int id) throws IOException {
// TODO Auto-generated method stub
File fil=new File("src/main/resources/Sample.pdf");
FileUploadModel fm =mapper.downloadFile(id);
InputStream inputStream = fm.getDaFile();
outputStream = new FileOutputStream(fil);
int read = 0;
byte[] bytes = new byte[102400000];
while ((read = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read);
}
return fil;
}
This code is working but i want to remove making of file on server side i.e i want to remove File fil=new File("src/main/resources/Sample.pdf"), this operation which is in service method .
Thanks in advance.
Instead of using File, use ByteArrayOutputStream and write to it. Then return the result as a byte[] which you can pass to your Response.ok(content).
Didn't test this, but something like this:
public byte[] downloadFile(int id) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
FileUploadModel fm =mapper.downloadFile(id);
InputStream inputStream = fm.getDaFile();
int read = 0;
byte[] bytes = new byte[1024];
while ((read = inputStream.read(bytes)) != -1) {
out.write(bytes, 0, read);
}
return out.toByteArray();
}
Also, that's a lot of bytes to allocate to an array. You can experiment with what works for you, but something like 1024 would likely be totally sufficient.
You'll probably also want to add another header to your response for Content-Type.
I need to build a webservice with Jersey that downloads a big file from another service and returns to the client.
I would like jersey to read some bytes into a buffer and write those bytes to client socket.
I would like it to use non blocking I/O so I dont keep a thread busy. (This could not be achieved)
#GET
#Path("mypath")
public void getFile(final #Suspended AsyncResponse res) {
Client client = ClientBuilder.newClient();
WebTarget t = client.target("http://webserviceURL");
t.request()
.header("some header", "value for header")
.async().get(new InvocationCallback<byte[]>(){
public void completed(byte[] response) {
res.resume(response);
}
public void failed(Throwable throwable) {
res.resume(throwable.getMessage());
throwable.printStackTrace();
//reply with error
}
});
}
So far I have this code and I believe Jersey would download the complete file and then write it to the client which is not what I want to do.
any thoughts??
The client side async request, isn't going to do much for your use case. It's more mean for "fire and forget" use cases. What you can do though is just get the InputStream from the client Response and mix with a server side StreamingResource to stream the results. The server will start sending the data as it is coming in from the other remote resource.
Below is an example. The "/file" endpoint is the dummy remote resource that serves up the file. The "/client" endpoint consumes it.
#Path("stream")
#Produces(MediaType.APPLICATION_OCTET_STREAM)
public class ClientStreamingResource {
private static final String INFILE = "Some File";
#GET
#Path("file")
public Response fileEndpoint() {
final File file = new File(INFILE);
final StreamingOutput output = new StreamingOutput() {
#Override
public void write(OutputStream out) {
try (FileInputStream in = new FileInputStream(file)) {
byte[] buf = new byte[512];
int len;
while ((len = in.read(buf)) != -1) {
out.write(buf, 0, len);
out.flush();
System.out.println("---- wrote 512 bytes file ----");
}
} catch (IOException ex) {
throw new InternalServerErrorException(ex);
}
}
};
return Response.ok(output)
.header(HttpHeaders.CONTENT_LENGTH, file.length())
.build();
}
#GET
#Path("client")
public void clientEndpoint(#Suspended final AsyncResponse asyncResponse) {
final Client client = ClientBuilder.newClient();
final WebTarget target = client.target("http://localhost:8080/stream/file");
final Response clientResponse = target.request().get();
final StreamingOutput output = new StreamingOutput() {
#Override
public void write(OutputStream out) {
try (final InputStream entityStream = clientResponse.readEntity(InputStream.class)) {
byte[] buf = new byte[512];
int len;
while ((len = entityStream.read(buf)) != -1) {
out.write(buf, 0, len);
out.flush();
System.out.println("---- wrote 512 bytes client ----");
}
} catch (IOException ex) {
throw new InternalServerErrorException(ex);
}
}
};
ResponseBuilder responseBuilder = Response.ok(output);
if (clientResponse.getHeaderString("Content-Length") != null) {
responseBuilder.header("Content-Length", clientResponse.getHeaderString("Content-Length"));
}
new Thread(() -> {
asyncResponse.resume(responseBuilder.build());
}).start();
}
}
I used cURL to make the request, and jetty-maven-plugin to be able to run the example from the command line. When you do run it, and make the request, you should see the server logging
---- wrote 512 bytes file ----
---- wrote 512 bytes file ----
---- wrote 512 bytes client ----
---- wrote 512 bytes file ----
---- wrote 512 bytes client ----
---- wrote 512 bytes file ----
---- wrote 512 bytes client ----
---- wrote 512 bytes file ----
---- wrote 512 bytes client ----
...
while cURL client is keeping track of the results
The point to take away from this is that the "remote server" logging is happening the same time as the client resource is logging. This shows that the client doesn't wait to receive the entire file. It starts sending out bytes as soon as it starts receiving them.
Some things to note about the example:
I used a very small buffer size (512) because I was testing with a small (1Mb) file. I really didn't want to wait for a large file for testing. But I would imagine large files should work just the same. Of course you will want to increase the buffer size to something larger.
In order to use the smaller buffer size, you need to set the Jersey property ServerProperties.OUTBOUND_CONTENT_LENGTH_BUFFER to 0. The reason is that Jersey keeps in internal buffer of size 8192, which will cause my 512 byte chunks of data not to flush, until 8192 bytes were buffered. So I just disabled it.
When using AsyncResponse, you should use another thread, as I did. You may want to use executors instead of explicitly creating threads though. If you don't use another thread, then you are still holding up the thread from the container's thread pool.
UPDATE
Instead of managing your own threads/executor, you can annotate the client resource with #ManagedAsync, and let Jersey manage the threads
#ManagedAsync
#GET
#Path("client")
public void clientEndpoint(#Suspended final AsyncResponse asyncResponse) {
...
asyncResponse.resume(responseBuilder.build());
}
I want to stream directly from an Oracle database blobs files via WS with MTOM directly to the WS client.
I thought I found a way which is described here:
http://www.java.net/forum/topic/glassfish/metro-and-jaxb/mtom-best-practices
but after i took a look on InputStreamDataSource and javax.mail.util.ByteArrayDataSource i realized that they acutal hava a byte[] of the 'document' in memory meaning the streaming ideea is in vain, cause what i try to avoid is to have multiple docs in the same time fully in memory.
So how can I stream from DB via WS and MTOM to a WS client ?
Any idea ?
Thanks
Cris
I tried experimenting and finally i had some positive results.
In order to stream from DB directly to clients browser the above
things are valid but the InputStreamDataSource should be like this:
public class InputStreamDataSource implements DataSource {
private InputStream inputStream;
public InputStreamDataSource(InputStream inputStream) {
this.inputStream = inputStream;
}
public InputStream getInputStream() throws IOException {
return inputStream;
}
public OutputStream getOutputStream() throws IOException {
throw new UnsupportedOperationException("Not implemented");
}
public String getContentType() {
return "*/*";
}
public String getName() {
return "InputStreamDataSource";
}
}
What I was affraid is that once I closed the input stream myself...
the ws client did not received the binary content...
Than i check and actually the DataHandler creates a new thread and closes the input stream
I was able to stream 500MB from DB to client fast and with low memory footprint !
I have a java web application based on Spring MVC.
The task is to generate a pdf file. As all knows the spring engine has its own built-in iText library so the generating of pdf file is really simple. First of all we need to do is to overload AbstractView and create some PdfView. And the seconf thing is to use that view in controller. But in my application I am also have to be able to store generated pdf files on local drive or give my users some link to download that file. So the view in that case is not suitable for me.
I want to create some universal pdf generator that creates a pdf file and returns the bytes array. So I can use that array for file storing (on hard drive) or printing it directly in browser. And the question is - are there any way to use such engine (that returns only the bytes array) in PdfVIew solution? I am asking because overloaded buildPdfDocument method (in PdfView) already have PdfWriter and Document parameters.
Thank you
tldr; you should be able to use a view and save it to a file.
Try using Flying Saucer and its iTextRenderer when you overload AbstractPdfView.
import org.xhtmlrenderer.pdf.ITextRenderer;
public class MyAbstractView extends AbstractView {
OutputStream os;
public void buildPdfDocument(Map<String,Object> model, com.lowagie.text.Document document, com.lowagie.text.pdf.PdfWriter writer, HttpServletRequest request, HttpServletResponse response){
//process model params
os = new FileOutputStream(outputFile);
ITextRenderer renderer = new ITextRenderer();
String url = "http://www.mysite.com"; //set your sample url namespace here
renderer.setDocument(document, url); //use the passed in document
renderer.layout();
renderer.createPDF(os);
os.close();
}
}
protected final void renderMergedOutputModel(Map<String,Object> model,
HttpServletRequest request,
HttpServletResponse response)
throws Exception{
if(os != null){
response.outputStream = os;
}
public byte[] getPDFAsBytes(){
if(os != null){
byte[] stuff;
os.write(stuff);
return stuff;
}
}
}
You'll probably have to tweak the sample implementation shown here, but that should provide a basic gist.
I am doing conversion to image and PDF output. I need an input HTML document that is generated by our application JSPs. Essentially, I need to render the final output product of a JSP based application to a String or memory and then use that string for other processing.
What are some ways that I can just invoke the JSP renderer to get the final HTML content that is normally output to the user?
Ideally, I am looking for something that will work for multiple application servers like websphere. But something that is Tomcat specific will also work.
There are a couple of other different approaches, but I think rendering the JSP (which may include sub JSPs) is the best approach.
Optional Paths that I would rather stay away from.
I could perform a network request to the page using the Socket APIs and then read the final output that is rendered from that particular page. This is probably the next best option, but we work on multiple servers and JVMs, targeting the page I need would be complicated.
Use a filter to get that final page output. This Ok but I have always had problems with filters and illegalstateexceptions. It never seems to work 100% the way I need to.
It seems like this should be simple. The JSP compiler is essentially just a library for parsing an input JSP document and subdocuments and then output some HTML content. I would like to invoke that process through Java code. On the server and possibly as a standalone console application.
This is a downright irritating problem, one I've had to handle a few times and one I've never found a satisfactory solution to.
The basic problem is that the servlet API is of no help here, so you have to trick it. My solution is to write a subclass of HttpServletResponseWrapper which override the getWriter() and getOutput() methods and captures the data into a buffer. You then forward() your request to the URI of the JSP you want to capture, substituting your wrapper response for the original response. You then extract the data from the buffer, manipulate it, and write the end result back to the original response.
Here's my code that does this:
public class CapturingResponseWrapper extends HttpServletResponseWrapper {
private final OutputStream buffer;
private PrintWriter writer;
private ServletOutputStream outputStream;
public CapturingResponseWrapper(HttpServletResponse response, OutputStream buffer) {
super(response);
this.buffer = buffer;
}
#Override
public ServletOutputStream getOutputStream() {
if (outputStream == null) {
outputStream = new DelegatingServletOutputStream(buffer);
}
return outputStream;
}
#Override
public PrintWriter getWriter() {
if (writer == null) {
writer = new PrintWriter(buffer);
}
return writer;
}
#Override
public void flushBuffer() throws IOException {
if (writer != null) {
writer.flush();
}
if (outputStream != null) {
outputStream.flush();
}
}
}
The code to use it can be something like this:
HttpServletRequest originalRequest = ...
HttpServletResponse originalResponse = ...
ByteArrayOutputStream bufferStream = new ByteArrayOutputStream();
CapturingResponseWrapper responseWrapper = new CapturingResponseWrapper(originalResponse, bufferStream);
originalRequest.getRequestDispatcher("/my.jsp").forward(originalRequest, responseWrapper);
responseWrapper.flushBuffer();
byte[] buffer = bufferStream.toByteArray();
// now use the data
It's very ugly, but it's the best solution I've found. In case you're wondering, the wrapper response has to contain the original response because the servlet spec says that you cannot substitute a completely different request or response object when you forward, you have to use the originals, or wrapped versions of them.