String getCacheKey( XWikiAttachment attachment, ImageDimensions dimension, String copyright, String watermark, int cropX, int cropY, int cropW, int cropH) throws NoSuchAlgorithmException { String key = attachment.getId() + "-" + attachment.getVersion() + "-" + getType(attachment.getMimeType(getContext())) + "-" + attachment.getDate().getTime() + "-" + dimension.getWidth() + "-" + dimension.getHeight() + "-" + getAditionalInfoHash(copyright, watermark, cropX, cropY, cropW, cropH); return key; }
/** * Set the response HTTP headers common to both partial (Range) and full responses. * * @param attachment the attachment to get content from * @param request the current client request * @param response the response to write to. * @param context the current request context */ private static void setCommonHeaders( final XWikiAttachment attachment, final XWikiRequest request, final XWikiResponse response, final XWikiContext context) { // Choose the right content type String mimetype = attachment.getMimeType(context); response.setContentType(mimetype); try { response.setCharacterEncoding(""); } catch (IllegalCharsetNameException ex) { response.setCharacterEncoding(XWiki.DEFAULT_ENCODING); } String ofilename = Util.encodeURI(attachment.getFilename(), context).replaceAll("\\+", "%20"); // The inline attribute of Content-Disposition tells the browser that they should display // the downloaded file in the page (see http://www.ietf.org/rfc/rfc1806.txt for more // details). We do this so that JPG, GIF, PNG, etc are displayed without prompting a Save // dialog box. However, all mime types that cannot be displayed by the browser do prompt a // Save dialog box (exe, zip, xar, etc). String dispType = "inline"; // Determine whether the user who attached the file has Programming Rights or not. boolean hasPR = false; String author = attachment.getAuthor(); try { hasPR = context .getWiki() .getRightService() .hasAccessLevel("programming", author, "XWiki.XWikiPreferences", context); } catch (Exception e) { hasPR = false; } // If the mimetype is not authorized to be displayed inline, let's force its content disposition // to download. if ((!hasPR && !isAuthorized(mimetype)) || "1".equals(request.getParameter("force-download"))) { dispType = ATTACHMENT; } // Use RFC 2231 for encoding filenames, since the normal HTTP headers only allows ASCII // characters. // See http://tools.ietf.org/html/rfc2231 for more details. response.addHeader("Content-disposition", dispType + "; filename*=utf-8''" + ofilename); response.setDateHeader("Last-Modified", attachment.getDate().getTime()); // Advertise that downloads can be resumed response.setHeader("Accept-Ranges", "bytes"); }
/** * {@inheritDoc} * * <p>Allows to scale images server-side, in order to have real thumbnails for reduced traffic. * The new image dimensions are passed in the request as the {@code width} and {@code height} * parameters. If only one of the dimensions is specified, then the other one is computed to * preserve the original aspect ratio of the image. * * @see XWikiDefaultPlugin#downloadAttachment(XWikiAttachment, XWikiContext) */ @Override public XWikiAttachment downloadAttachment(XWikiAttachment attachment, XWikiContext context) { if (!this.imageProcessor.isMimeTypeSupported(attachment.getMimeType(context))) { return attachment; } int height = -1; try { height = Integer.parseInt(context.getRequest().getParameter("height")); } catch (NumberFormatException e) { // Ignore. } int width = -1; try { width = Integer.parseInt(context.getRequest().getParameter("width")); } catch (NumberFormatException e) { // Ignore. } float quality = -1; try { quality = Float.parseFloat(context.getRequest().getParameter("quality")); } catch (NumberFormatException e) { // Ignore. } catch (NullPointerException e) { // Ignore. } // If no scaling is needed, return the original image. if (height <= 0 && width <= 0 && quality < 0) { return attachment; } try { // Transform the image attachment before is it downloaded. return downloadImage(attachment, width, height, quality, context); } catch (Exception e) { LOG.warn("Failed to transform image attachment.", e); return attachment; } }
/** * 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; }