ImageGallery:

Versuch einen ImageCache einzubauen

git-svn-id: https://svn.libreccm.org/ccm/trunk@1834 8810af33-2d31-482b-a856-94f89814c4df
master
quasi 2012-08-27 12:32:52 +00:00
parent dc5e36d368
commit 06b3e0a0ee
3 changed files with 346 additions and 84 deletions

View File

@ -0,0 +1,220 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.arsdigita.cms;
import com.arsdigita.mimetypes.MimeType;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import javax.imageio.ImageIO;
import org.apache.log4j.Logger;
import org.imgscalr.Scalr;
import org.imgscalr.Scalr.*;
/**
*
* @author Sören Bernstein (quasimodo) <sbernstein@zes.uni-bremen.de>
*/
public class CachedImage {
private String hash;
private final String name;
private final String version;
private final MimeType mimetype;
private byte[] image;
private BigDecimal width;
private BigDecimal height;
private static final Logger s_log = Logger.getLogger(CachedImage.class);
public CachedImage(ImageAsset imageAsset, int width, int height) {
this(imageAsset);
this.resizeImage(width, height);
}
public CachedImage(ImageAsset imageAsset) {
this.hash = imageAsset.getOID().toString();
this.name = imageAsset.getName();
this.version = imageAsset.getVersion();
this.mimetype = imageAsset.getMimeType();
this.image = imageAsset.getContent();
this.width = imageAsset.getWidth();
this.height = imageAsset.getHeight();
}
public CachedImage(CachedImage cachedImage, String resizeParam) {
this(cachedImage);
int width = 0;
int height = 0;
String[] params = resizeParam.split("&");
for (int i = 0; i < params.length; i++) {
if (params[i].isEmpty()) {
continue;
}
String key = params[i].substring(0, params[i].indexOf("="));
String value = params[i].substring(params[i].indexOf("=") + 1);
if (key.equalsIgnoreCase("width")) {
width = Integer.parseInt(value);
}
if (key.equalsIgnoreCase("height")) {
height = Integer.parseInt(value);
}
}
this.resizeImage(width, height);
}
public CachedImage(CachedImage cachedImage, int width, int height) {
this(cachedImage);
this.resizeImage(width, height);
}
private CachedImage(CachedImage cachedImage) {
this.hash = cachedImage.hash;
this.name = cachedImage.getName();
this.version = cachedImage.getVersion();
this.mimetype = cachedImage.getMimeType();
this.image = cachedImage.getImage();
this.width = cachedImage.getWidth();
this.height = cachedImage.getHeight();
}
public String getName() {
return this.name;
}
public BigDecimal getWidth() {
return this.width;
}
public BigDecimal getHeight() {
return this.height;
}
public int getSize() {
return this.image.length;
}
public String getVersion() {
return this.version;
}
public MimeType getMimeType() {
return this.mimetype;
}
public byte[] getImage() {
return this.image;
}
/**
* Retrieves the Blob content.
*
* @return the Blob content
*/
/*
protected byte[] getContent() {
byte[] content = null;
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
ImageIO.write(image, "JPEG", out);
content = out.toByteArray();
} catch (IOException ioEx) {
s_log.warn("Could not write byte array", ioEx);
} catch (IllegalArgumentException illEx) {
s_log.warn("image is not initialized", illEx);
} finally {
return content;
}
}
*/
public long writeBytes(OutputStream os) throws IOException {
byte[] bytes = this.getImage();
os.write(bytes);
return (long) (bytes.length);
}
/**
* Write the image asset content to a file.
*
* @param file The file on the server to write to.
*/
public void writeToFile(File file)
throws IOException {
FileOutputStream fs = new FileOutputStream(file);
try {
fs.write(this.getImage());
} finally {
if (null != fs) {
fs.close();
}
}
}
private void resizeImage(int width, int height) {
// No valid resizing imformation
if (width <= 0 && height <= 0) {
return;
}
// Read byte array in BufferedImage
BufferedImage bufferedImage = null;
try {
bufferedImage = ImageIO.read(new ByteArrayInputStream(this.getImage()));
} catch (IOException ioEx) {
s_log.warn("Could not read image", ioEx);
}
// Resize image with imagescalr
if (width > 0 && height > 0) {
bufferedImage = Scalr.resize(bufferedImage, Scalr.Method.SPEED, width, height);
}
if (width > 0 && height <= 0) {
bufferedImage = Scalr.resize(bufferedImage, Scalr.Method.SPEED, Scalr.Mode.FIT_TO_WIDTH, width);
}
if (width <= 0 && height > 0) {
bufferedImage = Scalr.resize(bufferedImage, Scalr.Method.SPEED, Scalr.Mode.FIT_TO_HEIGHT, height);
}
// Set Dimensions
this.width = new BigDecimal(bufferedImage.getWidth());
this.height = new BigDecimal(bufferedImage.getHeight());
this.hash = this.hash + "&width=" + this.width + "&height=" + this.height;
// Write BufferedImage to byte array
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
ImageIO.write(bufferedImage, "JPEG", out);
this.image = out.toByteArray();
} catch (IOException ioEx) {
s_log.warn("Could not write byte array", ioEx);
} catch (IllegalArgumentException illEx) {
s_log.warn("image is not initialized", illEx);
}
}
public int hashCode() {
return this.hash.hashCode();
}
}

View File

@ -19,8 +19,10 @@
package com.arsdigita.cms.dispatcher; package com.arsdigita.cms.dispatcher;
import com.arsdigita.bebop.parameters.BigDecimalParameter; import com.arsdigita.bebop.parameters.BigDecimalParameter;
import com.arsdigita.caching.CacheTable;
import com.arsdigita.cms.Asset; import com.arsdigita.cms.Asset;
import com.arsdigita.cms.ImageAsset; import com.arsdigita.cms.ImageAsset;
import com.arsdigita.cms.CachedImage;
import com.arsdigita.dispatcher.DispatcherHelper; import com.arsdigita.dispatcher.DispatcherHelper;
import com.arsdigita.dispatcher.RequestContext; import com.arsdigita.dispatcher.RequestContext;
import com.arsdigita.domain.DataObjectNotFoundException; import com.arsdigita.domain.DataObjectNotFoundException;
@ -62,6 +64,8 @@ public class BaseImage extends ResourceHandlerImpl {
// private BigDecimalParameter m_objectID; // private BigDecimalParameter m_objectID;
private final boolean m_download; private final boolean m_download;
private String m_disposition; private String m_disposition;
// ImageCache
private static CacheTable s_imageCache = new CacheTable("BaseImageCache");
private static final Logger s_log = private static final Logger s_log =
Logger.getLogger(BaseImage.class); Logger.getLogger(BaseImage.class);
@ -88,8 +92,8 @@ public class BaseImage extends ResourceHandlerImpl {
* Content-Disposition in HTTP. * Content-Disposition in HTTP.
*/ */
protected void setFilenameHeader(HttpServletResponse response, protected void setFilenameHeader(HttpServletResponse response,
ImageAsset image) { CachedImage cachedImage) {
String filename = image.getName(); String filename = cachedImage.getName();
if (filename == null) { if (filename == null) {
filename = s_defaultName; filename = s_defaultName;
} }
@ -102,14 +106,12 @@ public class BaseImage extends ResourceHandlerImpl {
response.setHeader("Content-Disposition", disposition.toString()); response.setHeader("Content-Disposition", disposition.toString());
} }
private void setHeaders(HttpServletResponse response, private void setHeaders(HttpServletResponse response, CachedImage cachedImage) {
ImageAsset image) { setFilenameHeader(response, cachedImage);
setFilenameHeader(response, image);
Long contentLength = new Long(image.getSize()); response.setContentLength(cachedImage.getSize());
response.setContentLength(contentLength.intValue());
MimeType mimeType = image.getMimeType(); MimeType mimeType = cachedImage.getMimeType();
if (m_download || mimeType == null) { if (m_download || mimeType == null) {
// Section 19.5.1 of RFC2616 says this implies download // Section 19.5.1 of RFC2616 says this implies download
@ -120,19 +122,19 @@ public class BaseImage extends ResourceHandlerImpl {
} }
// Default caching for all other types // Default caching for all other types
if ("live".equals(image.getVersion())) { if ("live".equals(cachedImage.getVersion())) {
DispatcherHelper.cacheForWorld(response); DispatcherHelper.cacheForWorld(response);
} else { } else {
DispatcherHelper.cacheDisable(response); DispatcherHelper.cacheDisable(response);
} }
} }
private void send(HttpServletResponse response, private void send(HttpServletResponse response, CachedImage cachedImage) throws IOException {
ImageAsset image) throws IOException {
// Stream the blob. // Stream the blob.
OutputStream out = response.getOutputStream(); OutputStream out = response.getOutputStream();
try { try {
image.writeBytes(out); cachedImage.writeBytes(out);
} finally { } finally {
out.close(); out.close();
} }
@ -151,57 +153,137 @@ public class BaseImage extends ResourceHandlerImpl {
RequestContext actx) RequestContext actx)
throws IOException, ServletException { throws IOException, ServletException {
// Fetch and validate the image ID
OID oid = null; OID oid = null;
BigDecimal imageId = null; BigDecimal imageId = null;
// BigDecimal transactionID = null; CachedImage cachedImage = null;
// BigDecimal objectID = null; String resizeParam = "";
// Get URL parameters
String widthParam = request.getParameter("width");
String heightParam = request.getParameter("height");
// Need the OID, but can work with imageId
try { try {
// Try to get OID and imageId, there should only be one not both
oid = (OID) m_oid.transformValue(request); oid = (OID) m_oid.transformValue(request);
imageId = (BigDecimal) m_imageId.transformValue(request); imageId = (BigDecimal) m_imageId.transformValue(request);
// transactionID =
// (BigDecimal) m_transactionID.transformValue(request);
// objectID =
// (BigDecimal) m_objectID.transformValue(request);
} catch (Exception e) { } catch (Exception e) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, response.sendError(HttpServletResponse.SC_BAD_REQUEST,
e.toString()); e.toString());
return; return;
} }
// We can't handle both OID and imageId at the same time
if ((imageId == null && oid == null) || (imageId != null && oid != null)) { if ((imageId == null && oid == null) || (imageId != null && oid != null)) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, response.sendError(HttpServletResponse.SC_BAD_REQUEST,
"either " + IMAGE_ID + " or " + OID_PARAM + " is required."); "either " + IMAGE_ID + " or " + OID_PARAM + " is required.");
return; return;
} }
// If the OID is still null
if (oid == null) { if (oid == null) {
// Get the OID from the imageID
oid = new OID(ImageAsset.BASE_DATA_OBJECT_TYPE, imageId); oid = new OID(ImageAsset.BASE_DATA_OBJECT_TYPE, imageId);
} }
// Finally, we have a valid OID
// Transaction transaction = null; // Process URL parameter
// GenericArticle article = null; if (widthParam != null && heightParam != null) {
// XXX: add back rollback
/*if (transactionID != null) {
try { try {
transaction =
new Transaction(transactionID);
// we have a transaction so let's see if we have an article
if (objectID != null) {
article = new GenericArticle(objectID);
article.rollBackTo(transaction);
}
} catch (DataObjectNotFoundException e) {
s_log.warn("Unable to locate transaction " + transactionID);
// this is non-critical so we just continue
}
}*/
ImageAsset image = null; // Set width
// if (article == null) { if (!widthParam.isEmpty() && widthParam.matches("^[0-9]*$")) {
resizeParam += "&width=" + widthParam;
}
} catch (NumberFormatException numberEx) {
s_log.warn("width parameter invalid " + widthParam);
}
try {
// Set height
if (!heightParam.isEmpty() && heightParam.matches("^[0-9]*$")) {
resizeParam += "&height=" + heightParam;
}
} catch (NumberFormatException numberEx) {
s_log.warn("height parameter invalid " + heightParam);
}
}
// Now, we have all information we need to proceed
if (!resizeParam.isEmpty()) {
// Try to get the CachedImage with the OID from the imageCache
cachedImage = (CachedImage) s_imageCache.get(oid.toString() + resizeParam);
// If cachedImage is still null, the resized version of this oid is
// not in the cache. So, we try to find the original version to
// avoid unnesseccary database access
if (cachedImage == null) {
// Get the original version
cachedImage = (CachedImage) s_imageCache.get(oid.toString());
// If cachedImage is still null, it is not in the imageCache
if (cachedImage == null) {
// Get it from the database
cachedImage = this.getImageAssetFromDB(response, oid);
// If cachedImage is still null, we can't find the oid in the DB either
// There is something broken. Bail out.
if (cachedImage == null) {
return;
}
// Put the CachedImage into the imageCache
s_imageCache.put(oid.toString(), cachedImage);
}
// Create a resized version of the cachedImage
cachedImage = new CachedImage(cachedImage, resizeParam);
// Put the CacheImageAsset into the imageCache
s_imageCache.put(oid.toString(), cachedImage + resizeParam);
}
} else {
// Try to get the CachedImage with the OID from the imageCache
cachedImage = (CachedImage) (s_imageCache.get(oid.toString()));
// If cachedImage is still null, it is not in the imageCache
if (cachedImage == null) {
// Get it from the database
cachedImage = this.getImageAssetFromDB(response, oid);
// If cachedImage is still null, we can't find the oid in the DB either
// There is something broken. Bail out.
if (cachedImage == null) {
return;
}
}
// Put the CacheImageAsset into the imageCache
s_imageCache.put(oid.toString(), cachedImage);
}
setHeaders(response, cachedImage);
send(response, cachedImage);
}
private CachedImage getImageAssetFromDB(HttpServletResponse response, OID oid) throws IOException {
ImageAsset imageAsset = null;
s_log.info(oid.toString() + " is not in imageCache. Fetching from database");
// Try to get the Asset from database and test for ImageAsset
try { try {
Asset a = (Asset) DomainObjectFactory.newInstance(oid); Asset a = (Asset) DomainObjectFactory.newInstance(oid);
if (a instanceof ImageAsset) { if (a instanceof ImageAsset) {
image = (ImageAsset) a; imageAsset = (ImageAsset) a;
} else { } else {
if (s_log.isInfoEnabled()) { if (s_log.isInfoEnabled()) {
s_log.info("Asset " + oid + " is not an ImageAsset"); s_log.info("Asset " + oid + " is not an ImageAsset");
@ -210,33 +292,9 @@ public class BaseImage extends ResourceHandlerImpl {
} catch (DataObjectNotFoundException nfe) { } catch (DataObjectNotFoundException nfe) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, response.sendError(HttpServletResponse.SC_NOT_FOUND,
"no ImageAsset with oid " + oid); "no ImageAsset with oid " + oid);
return; return null;
} }
// }
// if (image.getMimeType() == null) { return new CachedImage(imageAsset);
// response.sendError(HttpServletResponse.SC_NOT_FOUND,
// "MIME type not found for ImageAsset " + imageId);
// }
// Not until permissions are properly assigned to assets
//checkUserAccess(request, response, actx, image);
// response.setContentType(image.getMimeType().getMimeType());
/* Quasimodo: on demand resizing of images
int width;
int height;
width = Integer.parseInt(request.getParameter("width"));
height = Integer.parseInt(request.getParameter("height"));
if(width || height) {
}
*/
setHeaders(response, image);
send(response, image);
} }
} }

View File

@ -187,28 +187,12 @@ public class ImageBrowser extends Table {
boolean isSelected, Object key, boolean isSelected, Object key,
int row, int column) { int row, int column) {
ImageAsset a = (ImageAsset) value; ImageAsset a = (ImageAsset) value;
// String url = Utilities.getImageURL(a);
String url = Service.getImageURL(a); String url = Service.getImageURL(a);
String resizeParam = "&width=" + new Double(m_thumbSize.getWidth()).intValue() + "&height=" + new Double(m_thumbSize.getHeight()).intValue();
Image img = new Image(URL.getDispatcherPath() + url); Image img = new Image(URL.getDispatcherPath() + url + resizeParam, a.getName());
img.setBorder("0"); img.setBorder("0");
img.setAlt(a.getName());
BigDecimal width = a.getWidth(), height = a.getHeight();
int w, h;
if (width == null || height == null) {
w = (int) m_thumbSize.getWidth();
h = (int) m_thumbSize.getHeight();
} else {
Dimension d = ImageSizer.getScaledSize(
width.intValue(), height.intValue(), (int) m_thumbSize.getWidth(), (int) m_thumbSize.getHeight());
w = (int) d.getWidth();
h = (int) d.getHeight();
}
img.setWidth(Integer.toString(w));
img.setHeight(Integer.toString(h));
return new Link(img, url); return new Link(img, url);
} }