/* * JBoss, Home of Professional Open Source * Copyright 2013, Red Hat, Inc. and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.richfaces.photoalbum.manager; /** * Class encapsulated all functionality, related to working with the file system. * * @author Andrey Markhel */ import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import javax.activation.MimetypesFileTypeMap; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.event.Event; import javax.enterprise.event.Observes; import javax.inject.Inject; import javax.inject.Named; import org.richfaces.photoalbum.model.Album; import org.richfaces.photoalbum.model.Image; import org.richfaces.photoalbum.model.User; import org.richfaces.photoalbum.model.event.AlbumEvent; import org.richfaces.photoalbum.model.event.ErrorEvent; import org.richfaces.photoalbum.model.event.EventType; import org.richfaces.photoalbum.model.event.Events; import org.richfaces.photoalbum.model.event.ImageEvent; import org.richfaces.photoalbum.model.event.ShelfEvent; import org.richfaces.photoalbum.model.event.SimpleEvent; import org.richfaces.photoalbum.util.Constants; import org.richfaces.photoalbum.util.FileHandler; import org.richfaces.photoalbum.util.FileManipulation; import org.richfaces.photoalbum.util.ImageDimension; import com.google.common.io.Files; @Named @ApplicationScoped public class FileManager { @Inject private File uploadRoot; @Inject private String uploadRootPath; @Inject UserBean userBean; @Inject @EventType(Events.ADD_ERROR_EVENT) Event<ErrorEvent> error; /** * Method, that invoked at startup application. Used to determine where application will be write new images. This method * set uploadRoot field - it is reference to the file where images will be copied. */ // @PostConstruct // public void create() { // } /** * This method used to get reference to the file with the specified relative path to the uploadRoot field * * @param path - relative path of file * @return File reference */ public File getFileByPath(String path) { if (this.uploadRoot != null) { File result = new File(this.uploadRoot, path); try { final String resultCanonicalPath = result.getCanonicalPath(); if (!resultCanonicalPath.startsWith(this.uploadRootPath)) { result = null; } return result; } catch (IOException e) { error.fire(new ErrorEvent("no file found")); result = null; } return result; } return null; } /** * This method observes <code>Constants.ALBUM_DELETED_EVENT</code> and invoked after the user delete album. This method * delete album directory from the disk * * @param album - deleted album * @param path - relative path of the album directory * */ public void onAlbumDeleted(@Observes @EventType(Events.ALBUM_DELETED_EVENT) AlbumEvent ae) { if (!userBean.isLoggedIn()) { return; } deleteDirectory(ae.getPath()); } /** * This method observes <code>Constants.SHELF_DELETED_EVENT</code> and invoked after the user delete her shelf This method * delete shelf directory from the disk * * @param shelf - deleted shelf * @param path - relative path of the shelf directory */ public void onShelfDeleted(@Observes @EventType(Events.SHELF_DELETED_EVENT) ShelfEvent se) { if (!userBean.isLoggedIn()) { return; } deleteDirectory(se.getPath()); } /** * This method observes <code>Constants.USER_DELETED_EVENT</code> and invoked after the user was deleted(used in livedemo to * prevent flooding) This method delete user directory from the disk * * @param user - deleted user * @param path - relative path of the user directory */ public void onUserDeleted(@Observes @EventType(Events.USER_DELETED_EVENT) SimpleEvent se) { deleteDirectory(userBean.getUser().getPath()); } /** * This method observes <code>SHELF_ADDED_EVENT</code> and invoked after the user add new shelf This method add shelf * directory to the disk * * @param shelf - added shelf */ public void onShelfAdded(@Observes @EventType(Events.SHELF_ADDED_EVENT) ShelfEvent se) { File directory = getFileByPath(se.getShelf().getPath()); FileManipulation.addDirectory(directory); } /** * This method observes <code>ALBUM_ADDED_EVENT</code> and invoked after the user add new album This method add album * directory to the disk * * @param album - added album */ public void onAlbumAdded(@Observes @EventType(Events.ALBUM_ADDED_EVENT) AlbumEvent ae) { File directory = getFileByPath(ae.getAlbum().getPath()); FileManipulation.addDirectory(directory); } /** * This method invoked after user set new avatar icon * * @param avatarData - avatar file * @param user - user, that add avatar */ public boolean saveAvatar(File avatarData, User user) { String avatarPath = File.separator + user.getLogin() + File.separator + Constants.AVATAR_JPG; createDirectoryIfNotExist(avatarPath); try { InputStream is = new FileInputStream(avatarData); return writeFile(avatarPath, is, "", Constants.AVATAR_SIZE, true); } catch (IOException ioe) { error.fire(new ErrorEvent("error saving avatar")); return false; } } /** * This method observes <code>Constants.IMAGE_DELETED_EVENT</code> and invoked after the user delete her image This method * delete image and all thumbnails of this image from the disk * * @param image - deleted image * @param path - relative path of the image file */ public void deleteImage(@Observes @EventType(Events.IMAGE_DELETED_EVENT) ImageEvent ie) { if (!userBean.isLoggedIn()) { return; } for (ImageDimension d : ImageDimension.values()) { FileManipulation.deleteFile(getFileByPath(transformPath(ie.getPath(), d.getFilePostfix()))); } } /** * This method invoked after user upload new image * * @param fileName - new relative path to the image file * @param tempFilePath - absolute path to uploaded image * @throws IOException */ public boolean addImage(String fileName, FileHandler fileHandler) throws IOException { if (!userBean.isLoggedIn()) { return false; } createDirectoryIfNotExist(fileName); for (ImageDimension d : ImageDimension.values()) { try { InputStream in = fileHandler.getInputStream(); if (!writeFile(fileName, in, d.getFilePostfix(), d.getX(), true)) { return false; } in.close(); } catch (IOException ioe) { error.fire(new ErrorEvent("Error", "error saving image: " + ioe.getMessage())); return false; } } return true; } /** * This method used to transform one path to another. For example you want get path of the file with dimensioms 80 of image * with path /user/1/2/image.jpg, this method return /user/1/2/image_substitute.jpg * * @param target - path to transform * @param substitute - new 'addon' to the path */ public String transformPath(String target, String substitute) { if (target.length() < 2 || target.lastIndexOf(Constants.DOT) == -1) { return ""; } final String begin = target.substring(0, target.lastIndexOf(Constants.DOT)); final String end = target.substring(target.lastIndexOf(Constants.DOT)); return begin + substitute + end; } /** * This method used to get reference to the file with the absolute path * * @param path - absolute path of file * @return File reference */ public File getFileByAbsolutePath(String path) { return new File(path); } /** * This utility method used to determine if the directory with specified relative path exist * * @param path - absolute path of directory * @return File reference */ public boolean isDirectoryPresent(String path) { final File file = getFileByPath(path); return file.exists() && file.isDirectory(); } /** * This utility method used to determine if the file with specified relative path exist * * @param path - absolute path of file * @return File reference */ public boolean isFilePresent(String path) { final File file = getFileByPath(path); return file.exists(); } /** * This method observes <code>Constants.ALBUM_DRAGGED_EVENT</code> and invoked after the user dragged album form one shelf * to the another. This method rename album directory to the new directory * * @param album - dragged album * @param pathOld - old path of album directory */ public void renameAlbumDirectory(@Observes @EventType(Events.ALBUM_DRAGGED_EVENT) AlbumEvent ae) { String pathOld = ae.getPath(); Album album = ae.getAlbum(); File file = getFileByPath(pathOld); File file2 = getFileByPath(album.getPath()); if (file2.exists()) { if (file2.isDirectory()) { FileManipulation.deleteDirectory(file2, false); } else { FileManipulation.deleteFile(file2); } } try { Files.createParentDirs(file2); } catch (IOException ioe) { error.fire(new ErrorEvent("Error moving file", ioe.getMessage())); } file.renameTo(file2); } /** * This method observes <code>Constants.IMAGE_DRAGGED_EVENT</code> and invoked after the user dragged image form one album * to the another. This method rename image file and all thumbnails to the new name * * @param image - dragged image * @param pathOld - old path of image file */ public void renameImageFile(@Observes @EventType(Events.IMAGE_DRAGGED_EVENT) ImageEvent ie) { File file = null; File file2 = null; String pathOld = ie.getPath(); Image image = ie.getImage(); for (ImageDimension dimension : ImageDimension.values()) { file = getFileByPath(transformPath(pathOld, dimension.getFilePostfix())); file2 = getFileByPath(transformPath(image.getFullPath(), dimension.getFilePostfix())); if (file2.exists()) { if (file2.isDirectory()) { FileManipulation.deleteDirectory(file2, false); } else { FileManipulation.deleteFile(file2); } } file.renameTo(file2); } } private boolean writeFile(String newFileName, InputStream inputStream, String pattern, int size, boolean includeUploadRoot) { BufferedImage bsrc = null; String format = MimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(newFileName).split("/")[1]; try { // Read file form disk bsrc = FileManipulation.bitmapToImage(inputStream, format); } catch (IOException e1) { error.fire(new ErrorEvent("Error", "error reading file<br/>" + e1.getMessage())); return false; } int resizedParam = bsrc.getWidth() > bsrc.getHeight() ? bsrc.getWidth() : bsrc.getHeight(); double scale = (double) size / resizedParam; Double widthInDouble = ((Double) scale * bsrc.getWidth()); int width = widthInDouble.intValue(); Double heightInDouble = ((Double) scale * bsrc.getHeight()); int height = heightInDouble.intValue(); // Too small picture or original size if (width > bsrc.getWidth() || height > bsrc.getHeight() || size == 0) { width = bsrc.getWidth(); height = bsrc.getHeight(); } // scale image if need BufferedImage bdest = FileManipulation .getScaledInstance(bsrc, width, height, RenderingHints.VALUE_INTERPOLATION_BICUBIC, true); // Determine new path of image file String dest = includeUploadRoot ? this.uploadRootPath + transformPath(newFileName, pattern) : transformPath( newFileName, pattern); try { // save to disk FileManipulation.imageToBitmap(bdest, dest, format); } catch (IOException ex) { error.fire(new ErrorEvent("Error", "error saving image to disc: " + ex.getMessage())); return false; } return true; } private void deleteDirectory(String directory) { final File file = getFileByPath(directory); FileManipulation.deleteDirectory(file, false); } private void createDirectoryIfNotExist(String fileNameNew) { final int lastIndexOf = fileNameNew.lastIndexOf(File.separator); if (lastIndexOf > 0) { final String directory = fileNameNew.substring(0, lastIndexOf); final File file = getFileByPath(directory); if (!file.exists()) { file.mkdirs(); } } } }