/** * Store the requested attachment on the filesystem and return a {@code file://} URL where FOP can * access that file. * * @param wiki the name of the owner document's wiki * @param space the name of the owner document's space * @param name the name of the owner document * @param filename the name of the attachment * @param revision an optional attachment version * @param context the current request context * @return a {@code file://} URL where the attachment has been stored * @throws Exception if the attachment can't be retrieved from the database and stored on the * filesystem */ private URL getURL( String wiki, String space, String name, String filename, String revision, XWikiContext context) throws Exception { Map<String, File> usedFiles = getFileMapping(context); String key = getAttachmentKey(space, name, filename, revision); if (!usedFiles.containsKey(key)) { File file = getTemporaryFile(key, context); XWikiDocument doc = context .getWiki() .getDocument( new DocumentReference( StringUtils.defaultString(wiki, context.getDatabase()), space, name), context); XWikiAttachment attachment = doc.getAttachment(filename); if (StringUtils.isNotEmpty(revision)) { attachment = attachment.getAttachmentRevision(revision, context); } FileOutputStream fos = new FileOutputStream(file); IOUtils.copy(attachment.getContentInputStream(context), fos); fos.close(); usedFiles.put(key, file); } return usedFiles.get(key).toURI().toURL(); }
/** * Send the attachment content in the response. * * @param attachment the attachment to get content from * @param request the current client request * @param response the response to write to. * @param filename the filename to show in the message in case an exception needs to be thrown * @param context the XWikiContext just in case it is needed to load the attachment content * @throws XWikiException if something goes wrong */ private static void sendContent( final XWikiAttachment attachment, final XWikiRequest request, final XWikiResponse response, final String filename, final XWikiContext context) throws XWikiException { InputStream stream = null; try { setCommonHeaders(attachment, request, response, context); response.setContentLength(attachment.getContentSize(context)); stream = attachment.getContentInputStream(context); IOUtils.copy(stream, response.getOutputStream()); } catch (IOException e) { throw new XWikiException( XWikiException.MODULE_XWIKI_APP, XWikiException.ERROR_XWIKI_APP_SEND_RESPONSE_EXCEPTION, "Exception while sending response", e); } finally { if (stream != null) { IOUtils.closeQuietly(stream); } } }
/** * Write a byte range from the attachment to the response, if the requested range is valid and * falls within the file limits. * * @param attachment the attachment to get content from * @param start the first byte to write * @param end the last byte to write * @param request the current client request * @param response the response to write to. * @param context the current request context * @throws XWikiException if the attachment content cannot be retrieved * @throws IOException if the response cannot be written */ private static void writeByteRange( final XWikiAttachment attachment, Long start, Long end, final XWikiRequest request, final XWikiResponse response, final XWikiContext context) throws XWikiException, IOException { if (start >= 0 && start < attachment.getContentSize(context)) { InputStream data = attachment.getContentInputStream(context); data = new BoundedInputStream(data, end + 1); data.skip(start); setCommonHeaders(attachment, request, response, context); response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); if ((end - start + 1L) < Integer.MAX_VALUE) { response.setContentLength((int) (end - start + 1)); } response.setHeader( "Content-Range", "bytes " + start + "-" + end + SEPARATOR + attachment.getContentSize(context)); IOUtils.copyLarge(data, response.getOutputStream()); } else { response.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); } }
public void addToCache(String key, XWikiAttachment attachment) throws XWikiException { if (getImageCache() != null) { try { getImageCache() .set(key, IOUtils.toByteArray(attachment.getContentInputStream(getContext()))); } catch (IOException exp) { LOGGER.error("Failed to cache image [" + key + "].", exp); } } else { LOGGER.info("Caching of images deactivated."); } }
/** * Reduces the size (i.e. the number of bytes) of an image by scaling its width and height and by * reducing its compression quality. This helps decreasing the time needed to download the image * attachment. * * @param attachment the image to be shrunk * @param requestedWidth the desired image width; this value is taken into account only if it is * greater than zero and less than the current image width * @param requestedHeight the desired image height; this value is taken into account only if it is * greater than zero and less than the current image height * @param keepAspectRatio {@code true} to preserve the image aspect ratio even when both requested * dimensions are properly specified (in this case the image will be resized to best fit the * rectangle with the requested width and height), {@code false} otherwise * @param requestedQuality the desired compression quality * @param context the XWiki context * @return the modified image attachment * @throws Exception if shrinking the image fails */ private XWikiAttachment shrinkImage( XWikiAttachment attachment, int requestedWidth, int requestedHeight, boolean keepAspectRatio, float requestedQuality, XWikiContext context) throws Exception { Image image = this.imageProcessor.readImage(attachment.getContentInputStream(context)); // Compute the new image dimension. int currentWidth = image.getWidth(null); int currentHeight = image.getHeight(null); int[] dimensions = reduceImageDimensions( currentWidth, currentHeight, requestedWidth, requestedHeight, keepAspectRatio); float quality = requestedQuality; if (quality < 0) { // If no scaling is needed and the quality parameter is not specified, return the original // image. if (dimensions[0] == currentWidth && dimensions[1] == currentHeight) { return attachment; } quality = this.defaultQuality; } // Scale the image to the new dimensions. RenderedImage shrunkImage = this.imageProcessor.scaleImage(image, dimensions[0], dimensions[1]); // Create an image attachment for the shrunk image. XWikiAttachment thumbnail = (XWikiAttachment) attachment.clone(); thumbnail.loadContent(context); OutputStream acos = thumbnail.getAttachment_content().getContentOutputStream(); this.imageProcessor.writeImage(shrunkImage, attachment.getMimeType(context), quality, acos); IOUtils.closeQuietly(acos); return thumbnail; }
/** * @param attachment an image attachment * @param context the XWiki context * @return the height of the specified image * @throws IOException if reading the image from the attachment content fails * @throws XWikiException if reading the attachment content fails */ public int getHeight(XWikiAttachment attachment, XWikiContext context) throws IOException, XWikiException { return this.imageProcessor.readImage(attachment.getContentInputStream(context)).getHeight(null); }