Spring MVC pdf generation - java

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.

Related

How to save objects in GridFS without taking them from a file?

I'm working in a Spring Boot api that can receive very large objects and try to save it in a MongoDB database. Because of this the program sometimes throws me the next error:
org.bson.BsonMaximumSizeExceededException: Payload document size is larger than maximum of 16793600.
I'd read that MongoDB only permits objects of size below 16MB, this is very inconvenient for my system because an object can easily surpass this gap. To solve this I had read about GridFS, technology that allows to surpass the 16MB files gap.
Now I'm trying to implement GridFS in my system but I only had seen examples using files to save in the database, something like this:
gridFsOperations.store(new FileInputStream("/Users/myuser/Desktop/text.txt"), "myText.txt", "text/plain", metaData);
But I want to do is not to take the data from a file, but to the api to receive a object and save it, something like this:
#PostMapping
public String save(#RequestBody Object object){
DBObject metaData = new BasicDBObject();
metaData.put("type", "data");
gridFsOperations.store(object, metaData);
return "Stored successfully...";
}
Is it a posible way to doing this?
Get an InputStream from the request and pass it to a GridFSBucket. Here's a rough example:
In your controller:
#PostMapping
public ResponseEntity<String> uploadFile(MultipartHttpServletRequest request)
{
Iterator<String> iterator = request.getFilenames();
String filename = iterator.next();
MultipartFile mf = request.getFile(filename);
// I always have a service layer between controller and repository but for purposes of this example...
myDao.uploadFile(filename, mf.getInputStream());
}
In your DAO/repository:
private GridFSBucket bucket;
#Autowired
void setMongoDatabase(MongoDatabase db)
{
bucket = GridFSBuckets.create(db);
}
public ObjectId uploadFile(String filename, InputStream is)
{
Document metadata = new Document("type", "data");
GridFSUploadOptions opts = new GridFSUploadOptions().metadata(metadata);
ObjectId oid = bucket.uploadFromStream(filename, is, opts);
try
{
is.close();
}
catch (IOException ioe)
{
throw new UncheckedIOException(ioe);
}
return oid;
}
I paraphrased this from existing code so it may not be perfect but will be good enough to point you in the right direction.

How to send an image over Java HTTP server

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

Accessing uploaded file in Apache Tapestry page

I'm using Apache Tapestry v5.3.7 and I already use the normal Tapestry upload component in a form. For a better user experience I try now to integrate Dropzone.js in a normal Tapestry page without any form. The JavaScript integration works fine. The uploaded file data are transferred to my server with a post request and I can access the request with all of its parameters.
My question is now how can I access the binary data of the uploaded file (maybe as InputStream) to save them in my system? I already injected the http request but getInputStream returns a empty stream.
Thanks for any suggestions
/** Code snippet of page java part */
...
#Inject
protected HttpServletRequest _request;
public void onActivate (String rowId) {
String fileName=_request.getParameter("file");
try {
InputStream is=_request.getInputStream();
// if I do read from is it returns -1
// :-(
doSomeSaveStuff(is); // dummy code
}
catch(Exception e) {
e.printStackTrace();
}
}
...
Here's one way to do it:
In template:
<t:form t:id="testForm" class="dropzone">
</t:form>
In page.java
#Inject
MultipartDecoder multipartDecoder;
#Component(id = "testForm")
private Form testForm;
#Inject
RequestGlobals requestGlobals;
void onSubmitFromTestForm() throws ManagerException {
System.out.println("test form invoked");
HttpServletRequest r = requestGlobals.getHTTPServletRequest();
UploadedFile u = multipartDecoder.getFileUpload("file");
The uploaded file contains what you uploaded and you can work with it the way you want.
Note: the HttpServletRequest::getParameterMap() , told me that the handle to to the file is called file which is how I know that passing file to getFileUpload makes the decoder correctly parse the multipart/post

Showing images outside my application using Tapestry5

I am developing my first project with Tapestry and I am about to finish, except for the images..
What do I want? I just need to display an image outside my application, example: /home/app/images/image.jpg
What did I try? I have been "googling" and reading Tapestry5 forums, I found this: http://wiki.apache.org/tapestry/Tapestry5HowToStreamAnExistingBinaryFile
I followed the steps, creating classes but I need to display the image embed on another page (so I can't use ImagePage), I tried this:
On page java class
public StreamResponse getImage() {
InputStream input = DetallesMultimedia.class
.getResourceAsStream("/home/santi/Escritorio/evolution-of-mario.jpg"); //On application, i will retrieve this from DB
return new JPEGInline(input,"hellow");
}
On page template
...
<img src="${image}" alt:image/>
...
or
...
${image}
...
Obviusly, this didn't work and I really don't know how can I do it. I read about loading the image on an event (returning the OutputStream on that event, as it's said in the HowTo linked above) but my english is so bad (I am sure you already noticed) and I don't understand well how can I do that.
Could you help me please?
Thanks you all.
I've never seen the examples as on the wiki page. Below some code on how to load an image on the classpath though using a StreamResponse.
#Inject
private ComponentResources resources;
#OnEvent(value = "GET_IMAGE_STREAM_EVENT")
private Object getProfilePic() throws Exception {
InputStream openStream = DetallesMultimedia.class.getResourceAsStream("/home/santi/Escritorio/evolution-of-mario.jpg");
byte[] imageBytes = IOUtils.toByteArray(openStream);
final ByteArrayInputStream output = new ByteArrayInputStream(imageBytes);
final StreamResponse response = new StreamResponse() {
public String getContentType() {
"image/jpegOrPngOrGif";
}
public InputStream getStream() throws IOException {
return output;
}
public void prepareResponse(Response response) {
// add response headers if you need to here
}
};
return response;
}
public String getPicUrl() throws Exception {
return resources.createFormEventLink("GET_IMAGE_STREAM_EVENT");
}
In your template:
<img src="${picUrl}"/>

JSP compilation to string or in memory bytearray with Tomcat/Websphere

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.

Categories