简体   繁体   中英

JSF Streaming PDF on iOS devices

I have a JSF page that uses a backing bean to stream a PDF document to the browser. This functionality works great and I haven't had any problems with it until now when testing this functionality on iOS devices.

On Safari on both an iPad and iPod Touch, the PDF stream ends up just being pasted as a non-scrollable layer on the webpage when it gets the response. In the browsers on a desktop, the file prompts to save or open and opens correctly with Adobe Reader. This is an issue because only part of the first page of the PDF shows on the iOS devices and there is no way to view other parts.

I've tested other PDF's on these devices from web and they do not behave the same. Either a PDF application is launched or Safari loads a scrollable preview which are the desired outcomes. Has anyone else had this?

My backing bean is writing a buffered input stream of the PDF to an output stream. I have set the following response headers:

response.setContentType ("application/pdf"); 
response.setContentLength(inputLength); 
response.setHeader ("Content-Disposition", "inline;filename=\"" + saveFilename + ".pdf\""); 
response.setHeader ("Cache-Control", "must-revalidate, post-check=0, pre-check=0, public"); 

I have attempted to switch content-disposition from inline to attachment, but that did not help.

I'd imagine that those devices require Range support in the server side so that the content can be requested in separate parts. This ultimately saves eventually unnecessary network bandwidth and improves the performance feeling in the client side.

If those PDFs are dynamically generated, then your best bet is to write them to a temporary location in the disk file system and redirect the request to an URL which reads them by a servlet. Having a physical file is mandatory because you'd like to use RandomAccessFile in order to be able to return partial content of the file. This class requires a physical File on disk.

Here's an example which uses File#createTempFile() to create a temp file and redirects the response to a servlet with the filename as URL parameter:

File tempFile = File.createTempFile(saveFilename, ".pdf");
OutputStream output = new FileOutputStream(tempFile);
// Now write PDF to output.
// ...

// Then, redirect to some servlet URL.
String tempFilename = tempFile.getName();
externalContext.redirect("/pdf/" + URLEncoder.encode(tempFilename, "UTF-8"));

Then, you need to have a servlet like this one which supports range requests. You only need to modify it to change the basePath value to System.getProperty("java.io.tmpdir") . Finally map this servlet on an URL pattern of /pdf/* .

Alternatively, if you have full admin control over the server, then you could also create an additional webapp context pointing to an absolute path somewhere on the server's disk file system. Eg /var/webapp/pdf . In case of for example Tomcat, you could make this public by adding the following to /conf/server.xml :

<Context docBase="/var/webapp/pdf" path="/pdf" />

This way the /var/webapp/pdf folder's content is available by http://localhost:8080/context/pdf .

With a minor change in the above code example to specify the temp file location,

File tempFile = File.createTempFile(saveFilename, ".pdf", "/var/webapp/pdf");
// ...

you don't need a custom servlet anymore. Tomcat's DefaultServlet will then do the job of properly handling Range requests.

Last but not least, consider storing the temp file in session and implementing a HttpSessionListener or HttpSessionAttributeListener which deletes the temp file when the session has expired.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM