Java tutorial
/* * @(#)UploadProcessor.java Dec 13, 2009 * * Copyright 2008 Painiu. All rights reserved. */ package com.painiu.webapp.upload; import java.awt.Dimension; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.servlet.http.HttpServletRequest; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang.time.DateFormatUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.drew.imaging.jpeg.JpegProcessingException; import com.drew.imaging.jpeg.JpegSegmentData; import com.drew.metadata.Directory; import com.drew.metadata.Metadata; import com.drew.metadata.MetadataException; import com.drew.metadata.Tag; import com.drew.metadata.exif.ExifDirectory; import com.drew.metadata.iptc.IptcDirectory; import com.opensymphony.xwork.TextProvider; import com.painiu.Painiu; import com.painiu.webapp.image.Image; import com.painiu.webapp.image.ImageException; import com.painiu.webapp.image.ImageFactory; import com.painiu.webapp.image.ImageTooLargeInDimensionException; import com.painiu.webapp.image.MetadataUtils; import com.painiu.webapp.image.UnsupportedImageFormatException; import com.painiu.webapp.image.PhotoProcessor; import com.painiu.core.service.PhotoManager; import com.painiu.core.model.Photo; import com.painiu.core.model.PhotoAddress; import com.painiu.core.model.User; import com.painiu.core.model.UserPreference; import com.painiu.webapp.util.PhotoAddressUtils; import com.painiu.webapp.util.PhotoUtils; import com.painiu.webapp.util.FileUtils; /** * <p> * <a href="UploadProcessor.java.html"><i>View Source</i></a> * </p> * * @author Zhang Songfu * @version $Id: UploadProcessor.java 50 2010-06-14 15:02:09Z zhangsf $ */ public class UploadProcessor { //~ Static fields/initializers ============================================= private static final Log log = LogFactory.getLog(UploadProcessor.class); private static final Set JPEGS = new HashSet(3); static { JPEGS.add("image/jpg"); JPEGS.add("image/jpeg"); JPEGS.add("image/pjpeg"); } private static final Date MIN_TAKEN_DATE = new Date(631123200000L); // 1990-01-01 public static class Result implements Serializable { private List<Photo> succeed; private List<String> errors; public Result() { this.succeed = new ArrayList(5); this.errors = new ArrayList(5); } public Result(List<Photo> succeed, List<String> errors) { this.succeed = succeed; this.errors = errors; } public List<Photo> getSucceed() { return succeed; } public List<String> getErrors() { return errors; } } private static final int maxWidth = Painiu.getPhotoConfig().getMaxWidth(); private static final int maxHeight = Painiu.getPhotoConfig().getMaxHeight(); //~ Instance fields ======================================================== private PhotoManager photoManager; //~ Constructors =========================================================== //~ Methods ================================================================ /** * @param photoManager the photoManager to set */ public void setPhotoManager(PhotoManager photoManager) { this.photoManager = photoManager; } public Result process(HttpServletRequest request, List uploadedFiles) { String title = request.getParameter("title"); String description = request.getParameter("description"); String tags = request.getParameter("tags"); /*if ( Blacklist.getBlacklist().isBlacklisted(title)) { title = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm"); } if ( Blacklist.getBlacklist().isBlacklisted(description)) { description = ""; } if ( Blacklist.getBlacklist().isBlacklisted(tags)) { tags = ""; }*/ Integer creative = getCreative(request); //Privacy privacy = PhotoUtils.getPrivacy(request); // String activity = request.getParameter("activity"); List<Photo> photos = new ArrayList<Photo>(uploadedFiles.size()); List<String> errors = new ArrayList<String>(); // sort filenames Collections.sort(uploadedFiles); int total = uploadedFiles.size(); for (Iterator i = uploadedFiles.iterator(); i.hasNext();) { UploadedFile uploadedFile = (UploadedFile) i.next(); // TODO display process result try { Photo photo = processPhoto(null, null, uploadedFile, title, description, tags, creative); photos.add(photo); } catch (UnsupportedImageFormatException e) { log.warn("Unsupported image format: ", e); //errors.add(textProvider.getText("messages.invalid.content.type", new String[] { e.getFilename() })); } catch (ImageTooLargeInDimensionException e) { log.warn("Large dimension photo: " + e.getSize()); //errors.add(textProvider.getText("errors.dimension.too.large", new String[] { e.getFilename() })); } catch (Exception e) { log.warn("Error occurred while processing image file: " + uploadedFile.getDiskFile().getAbsolutePath()); log.error(e); //errors.add(textProvider.getText("errors.upload.processing", new String[] { uploadedFile.getOriginalFilename() })); } } return new Result(photos, errors); } public Result process(HttpServletRequest request, User user, UserPreference pref, List uploadedFiles, UploadMonitor monitor, TextProvider textProvider) { String title = request.getParameter("title"); String description = request.getParameter("description"); String tags = request.getParameter("tags"); /*if ( Blacklist.getBlacklist().isBlacklisted(title)) { title = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm"); } if ( Blacklist.getBlacklist().isBlacklisted(description)) { description = ""; } if ( Blacklist.getBlacklist().isBlacklisted(tags)) { tags = ""; }*/ Integer creative = getCreative(request); //Privacy privacy = PhotoUtils.getPrivacy(request); // String activity = request.getParameter("activity"); List<Photo> photos = new ArrayList<Photo>(uploadedFiles.size()); List<String> errors = new ArrayList<String>(); // sort filenames Collections.sort(uploadedFiles); int total = uploadedFiles.size(); if (monitor != null) { monitor.updateUploadProgress(new ProgressEvent(this, total, 0, ProgressEvent.START)); } for (Iterator i = uploadedFiles.iterator(); i.hasNext();) { UploadedFile uploadedFile = (UploadedFile) i.next(); // TODO display process result try { Photo photo = processPhoto(user, pref, uploadedFile, title, description, tags, creative); photos.add(photo); } catch (UnsupportedImageFormatException e) { log.warn("Unsupported image format: ", e); errors.add(textProvider.getText("messages.invalid.content.type", new String[] { e.getFilename() })); } catch (ImageTooLargeInDimensionException e) { log.warn("Large dimension photo: " + e.getSize()); errors.add(textProvider.getText("errors.dimension.too.large", new String[] { e.getFilename() })); } catch (Exception e) { log.warn("Error occurred while processing image file: " + uploadedFile.getDiskFile().getAbsolutePath()); log.error(e); errors.add(textProvider.getText("errors.upload.processing", new String[] { uploadedFile.getOriginalFilename() })); } if (monitor != null) { monitor.updateProcessProgress(new ProgressEvent(this, total, 1, ProgressEvent.PROGRESS, uploadedFile.getOriginalFilename())); } } if (monitor != null) { monitor.updateProcessProgress(new ProgressEvent(this, total, total, ProgressEvent.FINISH)); } return new Result(photos, errors); } private Photo processPhoto(User user, UserPreference pref, UploadedFile uploadedFile, String title, String description, String tags, Integer creative) throws Exception { String filename = uploadedFile.getOriginalFilename(); //if ( Blacklist.getBlacklist().isBlacklisted(filename)) { // filename = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm"); //} String contentType = uploadedFile.getContentType(); File file = uploadedFile.getDiskFile(); if (log.isInfoEnabled()) { log.info("Processing Photo: " + filename); } Photo photo = createPhoto(user, filename, title, description, new Integer((int) file.length()), tags, creative); // Default PhotoPerm //if (privacy.grant(Relation.NONE)) { // photo.setPermission(newPermission(pref)); //} // default license //photo.setLicense(pref.getLicense()); // first ping the image file, check its dimension... Image image = ImageFactory.getFactory().createImage(file).ping(); Dimension size = image.getSize(); if (size.width > maxWidth || size.height > maxHeight) { throw new ImageTooLargeInDimensionException(filename, size); } if (user.isInRole(Painiu.FROZEN_ROLE) || (size.width < 500 && size.height < 500)) { photo.setState(Photo.State.BLOCKED); } photo.setWidth(size.width); photo.setHeight(size.height); // Old photo repository: to be removed String path = new StringBuffer().append(getRepository(photo.getAddress().getHost())).append(File.separator) .append(photo.getAddress().getDir()).append(File.separator).append(photo.getAddress().getFilename()) .toString(); String distFilename = path + "_" + photo.getAddress().getSecret() + ".jpg"; /* Image.Type type = null; // ok, load it and process... try { image.destroy(); image = ImageFactory.getFactory().createImage(file).load(); // type = image.getType(); // PhotoProcessor.process(image, distFilename); // remeber to destroy it explicitly, i do not want to lie on JVM's GC. // } catch (ImageException e) { // log.warn("PhotoProcessing Error: ", e); // throw e; // } finally { // image.destroy(); // } // Metadata: Exif Iptc... // photo.setMetadata(new PhotoMeta()); // YPFS ---------------- testing ----------------------------- PhotoFile photoFile = new PhotoFile(user.getUsername(), file, type.getContentType(), photo.getWidth().intValue(), photo.getHeight().intValue()); try { photoFile.store(); } catch (YPFSException e) { log.error("YPFSException: ", e); throw e; } PhotoAddress addr = photo.getAddress(); addr.setUsername(photoFile.getUsername()); addr.setFileKey(photoFile.getFilename()); addr.setSecret(photoFile.getSecret()); // YPFS */ // read metadata if (isJpegImage(contentType)) { // String metaFilename = path + "_" + photo.getAddress().getSecret() + Painiu.SUFFIX_META; // readJpegMetadata(photo, file, metaFilename); readJpegMetadata(photo, file); } // copy original photo File distFile = new File(path + "_" + photo.getAddress().getSecret() + ".jpg"); File parentFile = distFile.getParentFile(); if (!parentFile.exists()) { parentFile.mkdirs(); } // ok, load it and process... try { image.destroy(); image = ImageFactory.getFactory().createImage(file).load(); log.info(image); PhotoProcessor.process(image, distFilename); // remeber to destroy it explicitly, i do not want to lie on JVM's GC. } catch (ImageException e) { log.warn("PhotoProcessing Error: ", e); throw e; } finally { image.destroy(); } FileUtils.copyFile(file, distFile); savePhoto(photo); return photo; } private void savePhoto(Photo photo) { if (log.isInfoEnabled()) { log.info("Saving Photo: " + photo.getOriginalFilename()); } photoManager.createPhoto(photo); } private void readJpegMetadata(Photo photo, File file) { try { JpegSegmentData data = MetadataUtils.readSegments(file); Metadata metadata = MetadataUtils.extractMetadataFromSegmentData(data); if (log.isDebugEnabled()) { log.debug("********* Metadata of uploaded photo *********"); // iterate over the exif data and print it Iterator directories = metadata.getDirectoryIterator(); while (directories.hasNext()) { Directory directory = (Directory) directories.next(); Iterator tagsIterator = directory.getTagIterator(); while (tagsIterator.hasNext()) { Tag tag = (Tag) tagsIterator.next(); log.debug(tag); } if (directory.hasErrors()) { Iterator errors = directory.getErrors(); while (errors.hasNext()) { log.debug("ERROR: " + errors.next()); } } } } Directory exifDirectory = metadata.getDirectory(ExifDirectory.class); Date takenDate = null; if (exifDirectory.containsTag(ExifDirectory.TAG_DATETIME_ORIGINAL)) { takenDate = exifDirectory.getDate(ExifDirectory.TAG_DATETIME_ORIGINAL); } else if (exifDirectory.containsTag(ExifDirectory.TAG_DATETIME_DIGITIZED)) { takenDate = exifDirectory.getDate(ExifDirectory.TAG_DATETIME_DIGITIZED); } else if (exifDirectory.containsTag(ExifDirectory.TAG_DATETIME)) { takenDate = exifDirectory.getDate(ExifDirectory.TAG_DATETIME); } if (takenDate != null && takenDate.after(MIN_TAKEN_DATE)) { photo.setTakenDate(takenDate); } if (photo.getTitle() == null) { String titleFromMeta = extractTitleFromMetadata(metadata); if (titleFromMeta != null) { photo.setTitle(titleFromMeta); } } if (photo.getDescription() == null) { photo.setDescription(extractDescFromMetadata(metadata)); } if (exifDirectory.containsTag(ExifDirectory.TAG_MODEL)) { String cameraModel = exifDirectory.getString(ExifDirectory.TAG_MODEL); photo.setCameraModel(cameraModel); // save metadata as file File localFile = new File(file.getAbsolutePath() + ".meta"); MetadataUtils.toFile(localFile, data); // PNFS testing ----------------------------------- /*MetaFile metaFile = new MetaFile( photo.getAddress().getUsername(), photo.getAddress().getFileKey(), localFile); try { metaFile.store(); } catch (YPFSException e) { log.error("YPFSException: " + e); photo.setCameraModel(null); } finally { localFile.delete(); }*/ // PNFS } // we dont save any metadata, if there have no camera tag } catch (JpegProcessingException e) { log.warn("JpegProcessingException: ", e); } catch (MetadataException e) { log.warn("MetadataException: ", e); } catch (IOException e) { log.warn("IOException: ", e); } } private static String extractTitleFromMetadata(Metadata metadata) throws MetadataException { String title = null; Directory exifDirectory = metadata.getDirectory(ExifDirectory.class); if (exifDirectory.containsTag(ExifDirectory.TAG_DOCUMENT_NAME)) { title = exifDirectory.getDescription(ExifDirectory.TAG_DOCUMENT_NAME); } else if (exifDirectory.containsTag(ExifDirectory.TAG_WIN_TITLE)) { title = exifDirectory.getDescription(ExifDirectory.TAG_WIN_TITLE); } else { // try to find title from iptc directory Directory iptcDirectory = metadata.getDirectory(IptcDirectory.class); if (iptcDirectory.containsTag(IptcDirectory.TAG_OBJECT_NAME)) { title = iptcDirectory.getDescription(IptcDirectory.TAG_OBJECT_NAME); } } return title; } private static String extractDescFromMetadata(Metadata metadata) throws MetadataException { String desc = null; Directory exifDirectory = metadata.getDirectory(ExifDirectory.class); if (exifDirectory.containsTag(ExifDirectory.TAG_IMAGE_DESCRIPTION)) { desc = exifDirectory.getDescription(ExifDirectory.TAG_IMAGE_DESCRIPTION); } else if (exifDirectory.containsTag(ExifDirectory.TAG_WIN_COMMENT)) { desc = exifDirectory.getDescription(ExifDirectory.TAG_WIN_COMMENT); } else { // try to find description from iptc directory Directory iptcDirectory = metadata.getDirectory(IptcDirectory.class); if (iptcDirectory.containsTag(IptcDirectory.TAG_CAPTION)) { desc = iptcDirectory.getDescription(IptcDirectory.TAG_CAPTION); } } return desc; } //~ Utitlities ================================================================ private static boolean isJpegImage(String contentType) { return JPEGS.contains(contentType); } /*private static PhotoPermission newPermission(UserPreference preference) { PhotoPermission perm = new PhotoPermission(); perm.setBlog(preference.getBlog()); perm.setComment(preference.getComment()); perm.setDownload(preference.getDownload()); perm.setExif(preference.getExif()); perm.setNote(preference.getNote()); perm.setTag(preference.getTag()); return perm; }*/ private static Integer getCreative(HttpServletRequest request) { int creative = Painiu.CREATIVE_AUTHOR; String string = request.getParameter("creative"); if (String.valueOf(Painiu.CREATIVE_REFERENCED).equals(string)) { creative = Painiu.CREATIVE_REFERENCED; } return new Integer(creative); } /** * @return */ private static String getRepository(Integer host) { return Painiu.getPhotoConfig().getRepository(host).getDirectory(); } private static Photo createPhoto(User user, String originalFilename, String title, String description, Integer fileLength, String tags, Integer creative) { Photo photo = new Photo(); photo.setUser(user); photo.setAddress(PhotoAddressUtils.newAddress()); photo.setOriginalFilename(originalFilename); photo.setTitle(title != null ? title : FilenameUtils.getBaseName(originalFilename)); if (description != null) { photo.setDescription(description); } photo.setFileLength(fileLength); photo.setRawTags(tags); photo.setCreativeType(creative); //photo.setPrivacy(privacy); //photo.setPermission(new PhotoPermission(privacy)); //photo.setStat(new PhotoStat()); photo.setState(Photo.State.valueOf(user.getUserRank().value() + Photo.OFFSET_UNCHECKED_PHOTO_FROM_USER)); long now = System.currentTimeMillis(); // make sure one person's photos have different timestamp. if (getTimestamp() >= now) { now = getTimestamp() + 1; } if (now % 1000 == 0) { // prevent posted date overlap. (user editing). now = now + 1; } photo.setPostedDate(new Date(now)); photo.setTimestamp(new Long(now)); photo.setTakenDate(photo.getPostedDate()); setTimestamp(now); return photo; } private static ThreadLocal timestampHolder = new ThreadLocal(); public static void setTimestamp(long timestamp) { timestampHolder.set(new Long(timestamp)); } public static long getTimestamp() { if (timestampHolder.get() == null) { return -1; } return ((Long) timestampHolder.get()).longValue(); } }