3

Similar to this question, we are developing a Web app where the client clicks a button to receive a PDF from the server. Right now we're using the .ajax() method with jQuery to POST the data the backend needs to generate the PDF (we're sending XML) when the button is pressed, and then the backend is generating the PDF entirely in-memory and sending it back as application/pdf in the HTTP response.

One answer to that question requires the server-side save the PDF to disk so it can give back a URL for the client to GET. But I don't want the backend caching content at all.

The other answer suggests the use of a jQuery plugin, but when you look at its code, it's actually generating a form element and then submitting the form. That method will not work for us since we are sending XML data in the body of the HTTP request.

Is there a way to have the browser open up the PDF without caching the PDF server-side, and without requiring us to throw out our send-data-to-the-server-using-XML solution?

(I'd like the browser to behave like it does when a form element is submitted -- a POST is made and then the browser looks at the Content-type header to determine what to do next, like load the PDF in the browser window, a la Safari)

Community
  • 1
  • 1
Shaggy Frog
  • 27,575
  • 16
  • 91
  • 128

4 Answers4

1

I see some solutions :

1) submit it to a simple servlet, stream the file through an xslt processor (e.g. saxon) which convert it to xsl-fo (a kind of xml page description language) which can be turned into PDF using FOP or RenderX.

2) submit to a servlet, parse the xml in bean arrays or whatever suitably structured data model which can be handled by a reporting engine, then pass that to a reporting engine (e.g Birt, JasperReports, ...) which support PDF rendering as a standard output format. If the XML is small, you could maybe just pass it completely to the reporting engine servlet and do the processing inside the report itself. I know one of them has a Javascript Data Source which can do the transformations.

3) Use iText to generate PDF from the parsed xml in a servlet, but I never used that before, so I have no idea how that would work.

Peter Tillemans
  • 34,983
  • 11
  • 83
  • 114
  • You misunderstand completely. I am already creating the PDF file. I don't need help with that. I need help serving that PDF file to the user in their browser after they hit the button. – Shaggy Frog May 28 '10 at 00:04
  • My apologies. I guess your issue is related to the fact that the requesst is being handled by javascript rather than the browser itself. If you change the calling to open in a new window or tab and just let the browser handle it, it should do the right thing provided the right contentType is set (and the right filename extension for good measure) in the response headers. – Peter Tillemans May 28 '10 at 09:41
  • Opening in a new window requires a location. Which requires a URL. Which means the PDF must be available, cached server-side, for a *second* HTTP request (a GET). As I stated originally, I don't want to cache anything (write to disk) server-side. – Shaggy Frog May 28 '10 at 18:43
  • It does require a URL for the GET, but that doesn't mean a file has to be written to disk. It just means that the request has to go to the process that can write the PDF output. Cocoon flowscript has continuations that can do this -- your script sends a continuation URL that picks up where the script left off. ( I think there's also a way to do something similar with frameworks like node.js or twisted, but not sure. ) – Steven D. Majewski May 28 '10 at 19:04
  • You're right, the file doesn't need to be written to disk. However, the idea of continuations replaces one problem (disk I/O management) with another (resource possibly shared by multiple threads or processes), because instead of writing the PDF to disk, you have to save some intermediate information that can create that PDF off to the side. And that information needs to be stored in some sort of data structure where multiple producers will be adding to it and multiple consumers will be removing from it. – Shaggy Frog Jun 04 '10 at 01:44
1

Here is a sample form that allows you to input some text, and a PDF appears in the browser. Well, it does for me. And all I do is create the PDF in memory, set the response body to the PDF, and the response header to application/pdf. And that's it.

See http://129.33.194.254:8080/makepdf.rsp for the working example.

If it doesn't work for you, trace the http transaction using wireshark.

<html>
<body>
<%
    either none? text: select request/content 'data [
        %>
        <strong> PDF Test </strong>
        <form method="post">
        Enter some text:
        <input type="text" name="data" value="">
        <input type="submit" value="Submit">
        </form>
        <%
    ][
    if not exists? %pdf-maker.r [
        write %pdf-maker.r read http://www.colellachiara.com/soft/Misc/pdf-maker.r
    ]
    if not value? 'layout-pdf [
        *do %pdf-maker.r
    ]
    response/buffer: layout-pdf compose/deep [[textbox [(dehex text)]]]
    response/set-header 'Content-type "application/pdf"
    ]
%>
</body>
</html>
Graham Chiu
  • 4,856
  • 1
  • 23
  • 41
  • What language is this? I have never seen code like this. I also can't parse if it's sending data via the HTTP Request body (and not as a URL encoded query string), and it looks like it's somehow modifying the response body. – Shaggy Frog Jun 09 '10 at 17:24
  • The language, Rebol ( see http://www.rebol.com ) is unimportant. It would work just the same if I used a GET instead of a POST. I don't understand why you say you can parse only by using a GET. And yes, you have to create a response to send back to the client, and in this framework, we just set the response/buffer, and the web server streams it back to the client. I'm pretty sure that's how it is done in Zope and other servers too. If you use Firefox, you can use http headers addon to see what your web server is sending back to you with your own code for crucial debugging. – Graham Chiu Jun 09 '10 at 20:14
  • (1) The language is definitely important, since I'm hoping to just use HTML/JS on the client side and Java on the server side (2) I was using "parse" in the sense "I am trying to read this to understand what it does". (3) I can't use a GET instead of a POST, since GET does not allow an arbitrary body of data (for more on that read the HTTP spec) (4) Based on your answer and your comment, I think you misunderstand the question and the requirements involved here – Shaggy Frog Jun 10 '10 at 01:22
  • The example I gave you uses a HTTP post (
    ).
    – Graham Chiu Jun 10 '10 at 02:41
0

I had exact the same problem, the only difference was: I used JSON to post the data to the server. My solution was:

Create a hidden form (#printform) on your page with an input field (#printdata) and submit the parameters as string:

  $("#printdata").val(JSON.stringify(my_json_data_object));
  $("#printform").submit();

In my_json_data_object i stored all the necessary data for the pdf creation. On the server I had to parse the parameters back to a JSON object with JSON.parse(...). With this solution I can create and stream pdf files to the client, without saving them on the server. Do not forget setting the following headers on the response:

content-type: application/json
content-disposition: attachment; filename="output.pdf"
artkoenig
  • 7,117
  • 2
  • 40
  • 61
0

Just create the appropriate content headers and write the PDF back in the response. You don't need to cache the PDF you created in the same way you don't need to cache dynamically generated html or images.

Graham Chiu
  • 4,856
  • 1
  • 23
  • 41