Recently I was adding a new XPages front-end onto an existing database. I chose to split database and XPages application, so the data is in a separate database. So, for security purposes and to prevent access to the old web front-end, I had the bright of locking down URL access by enabling the “Don’t allow URL open” property on the database. It seemed like a great idea, until I realised the documents also included images; and the image is displayed by URL; which goes to the original database, not the XPages application. All of that means, with “Don’t allow URL open”, the image doesn’t display.
I’m not sure how (or if) Bluemix handles this. I would hope it does some clever proxying and pulls the image via NRPC to stream on the Bluemix runtime. Obviously if you are paying for an XPages runtime, you wouldn’t want to still need HTTP on the data server to display images and attachments. (I didn’t bother looking in open source, because the image control is core XPages, so if Bluemix does something clever, I wouldn’t expect it to be in the Extension Library, but the Bluemix runtime.)
I needed to get something up and running pretty quickly. I had recently been involved with doing something for a servlet and had web services which streamed images across HTTP. So my thought was whether there was some way with an XAgent to read the image as an image stream, then write that in the HttpServletResponse
.
I’m not sure if the result I came up with is best practice, but I found a couple of other questions on a similar topic useful. One was a question about manipulating images to resize them, conceptually a similar idea. I think this StackOverflow question on ImageIO and XAgents may have been another source (it was a few weeks ago that I solved the problem). What I came up with was the following code, in the render response of an XAgent.
-
FacesContext facesContext = FacesContext.getCurrentInstance();
-
HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext()
-
.getResponse();
-
ServletOutputStream responseStream = response.getOutputStream();
-
try {
-
// Write the image to the responseStream
-
Vector<Object> attNames = IntecUtils.getUserSession().evaluate(“@AttachmentNames”, doc);
-
if (attNames.size() > 0) {
-
if (imageStream != null) {
-
ImageIO.write(srcImage, “jpg”, responseStream);
-
}
-
}
-
FacesContext.getCurrentInstance().responseComplete();
-
ioe.printStackTrace();
-
} finally {
-
responseStream.close();
-
// It shouldn’t be null if things are going well, but a check never hurt
-
if (facesContext != null) {
-
facesContext.responseComplete();
-
}
-
}
A couple of points here are that I’m using OpenNTF Domino API and a couple of helper functions of my own. Line 8 just gets the underlying Document
object from the dominoDocument
datasource on the XPage. Line 9 uses the current user session.
Here I know there’s only one image attachment per document, so in line 9 I just evaluate @AttachmentNames
to get its name. I then get the embeddedObject
and its imageStream
. I use javax.imageio.ImageIO
to read and then write the image to the ServletOutputStream
. Finally I close the responseStream
and set responseComplete
.
Another important element is to set viewState="nostate"
on the XPage. There is no point serializing the XPage, because the browser cannot retrieve the component tree for any future partial refresh.
Unfortunately, the Java 6 VM of Domino is very old and does not support the newest file formats like some JPEG compression types, so loading the image via ImageIO fails sometimes and crashes the whole Domino server for some other images. Other things you might be facing are OutOfMemoryExceptions because the JVM needs to allocate heap memory for the whole image between the read and write operation.
The workaround is not easy to build in a generic way. For a project where lots of users upload large images (digital photos), we used an external ImageMagick process and two disk caches (one for the slow EmbeddedObject.getInputStream() operation which internally creates a temp file first to produce the InputStream and another one for the scaled images to not scale them for each request).
Thanks for the information, I’ll need to investigate further.
Paul, we went with a much simpler approach. We have all the attachments and images stored in a separate database and use readers fields for access. Only this database has URL access turned on.