it.geosolutions.opensdi2.mvc.BaseFileManager.java Source code

Java tutorial

Introduction

Here is the source code for it.geosolutions.opensdi2.mvc.BaseFileManager.java

Source

/*
 *  OpenSDI Manager 2
 *  Copyright (C) 2014 GeoSolutions S.A.S.
 *  http://www.geo-solutions.it
 *
 *  GPLv3 + Classpath exception
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package it.geosolutions.opensdi2.mvc;

import static it.geosolutions.opensdi2.utils.ResponseConstants.RESULTS;
import static it.geosolutions.opensdi2.utils.ResponseConstants.ROOT;
import static it.geosolutions.opensdi2.utils.ResponseConstants.SUCCESS;
import it.geosolutions.opensdi2.service.FileUploadService;
import it.geosolutions.opensdi2.utils.ControllerUtils;
import it.geosolutions.opensdi2.utils.ResponseConstants;

import java.awt.Graphics;
import java.awt.Image;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.swing.ImageIcon;

import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.multipart.MultipartFile;

/**
 * File Manager controller base for ExtJS
 * 
 * @author adiaz
 * @author Lorenzo Natali
 * 
 */
public class BaseFileManager extends AbstractFileController {

    protected final static Logger LOGGER = Logger.getLogger(BaseFileManager.class);

    private FileUploadService fileUploadService;

    /**
     * Default width for thumb
     */
    protected static final int THUMB_W = 100;

    /**
     * Default height for thumb
     */
    protected static final int THUMB_H = 100;

    /**
     * Known operation: Extjs integration folder list
     */
    public static final String EXTJS_FOLDER_LIST = "get_folderlist";

    /**
     * Known operation: Extjs integration file list
     */
    public static final String EXTJS_FILE_LIST = "get_filelist";

    /**
     * Known operation: Extjs integration folder new
     */
    public static final String EXTJS_FOLDER_NEW = "folder_new";

    /**
     * Known operation: Extjs integration folder rename
     */
    public static final String EXTJS_FOLDER_RENAME = "folder_rename";

    /**
     * Known operation: Extjs integration folder delete
     */
    public static final String EXTJS_FOLDER_DEL = "folder_delete";

    /**
     * Known operation: Extjs integration file rename
     */
    public static final String EXTJS_FILE_RENAME = "file_rename";

    /**
     * Known operation: Extjs integration file delete
     */
    public static final String EXTJS_FILE_DELETE = "file_delete";

    /**
     * Known operation: Extjs integration file download
     */
    public static final String EXTJS_FILE_DOWNLOAD = "file_download";

    /**
     * Known operation: Extjs integration file upload
     */
    public static final String EXTJS_FILE_UPLOAD = "file_upload";

    /**
     * Known operation: Extjs integration file properties
     */
    public static final String EXTJS_FILE_PROPERTIES = "file_properties";

    /**
     * Known operation: Extjs integration file thumb
     */
    public static final String EXTJS_FILE_THUMB = "get_thumb";

    /**
     * Known operation: Extjs integration serve image
     */
    public static final String EXTJS_IMAGE = "get_image";

    /**
     * Name for new folders
     */
    protected String newFolderName = "New Folder";

    /**
     * Browser handler server side for ExtJS filebrowser.
     * 
     * @see https://code.google.com/p/ext-ux-filebrowserpanel/
     * 
     * @param action
     *            to perform
     * @param folder
     *            folder to browse
     * @param file
     *            to perform an operation
     * @param request
     *            servlet request
     * @param response
     *            servlet response
     * 
     * @return
     */
    public Object extJSbrowser(String action, String folder, String name, String oldName, String file,
            HttpServletRequest request, HttpServletResponse response) {

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Performing " + action + " in extJSFileBrowser");
        }

        String finalFolder = folder != null && !folder.equals("root") ? folder : null;

        Map<String, Object> result = new HashMap<String, Object>();

        // TODO: Known operations for ExtJS file browser.
        if (EXTJS_FILE_DELETE.equals(action)) {
            result.put(SUCCESS, deleteFile(file, finalFolder));
        } else if (EXTJS_FILE_DOWNLOAD.equals(action)) {
            download(response, file, getFilePath(file, finalFolder));
            return null;
        } else if (EXTJS_FILE_LIST.equals(action)) {
            return getFileList(folder);
        } else if (EXTJS_FILE_PROPERTIES.equals(action)) {
            LOGGER.error("TODO operation: " + EXTJS_FILE_PROPERTIES);
        } else if (EXTJS_FILE_RENAME.equals(action)) {
            result.put(SUCCESS, renameFolder(finalFolder, name, oldName));
        } else if (EXTJS_FILE_THUMB.equals(action)) {
            serveImageThumb(response, file, getFilePath(file, finalFolder));
            return null;
        } else if (EXTJS_IMAGE.equals(action)) {
            download("image/" + ControllerUtils.getExtension(file), null, response, file,
                    getFilePath(file, finalFolder));
            return null;
        } else if (EXTJS_FILE_UPLOAD.equals(action)) {
            LOGGER.error("TODO operation: " + EXTJS_FILE_UPLOAD);
        } else if (EXTJS_FOLDER_DEL.equals(action)) {
            result.put(SUCCESS, deleteFolder(finalFolder, null));
        } else if (EXTJS_FOLDER_LIST.equals(action)) {
            return getFolderList(folder);
        } else if (EXTJS_FOLDER_NEW.equals(action)) {
            result.put(SUCCESS, newFolder(finalFolder));
        } else if (EXTJS_FOLDER_RENAME.equals(action)) {
            result.put(SUCCESS, renameFolder(file, name, oldName));
        } else {
            LOGGER.error("Unknown operation " + action);
            result.put(SUCCESS, false);
            result.put(ROOT, action);
            result.put(RESULTS, 1);
        }

        return result;

    }

    /**
     * Handler for upload files
     * 
     * @param operationId
     * @param gotHeaders
     * @param file
     *            uploaded
     * @param request
     * @param model
     * @return
     * @throws IOException
     */
    public void upload(MultipartFile file, String name, int chunks, int chunk, String folder,
            HttpServletRequest request, HttpServletResponse servletResponse) throws IOException {

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("upload (name, chunks, chunk) --> " + name + "," + chunks + "," + chunk);
            LOGGER.debug("Uploading " + fileUploadService.size() + " files");
        }

        if (chunks > 0) {
            // init bytes for the chunk upload
            Entry<String, ?> entry = fileUploadService.addChunk(name, chunks, chunk, file);
            if (entry == null) {
                String msg = "Expired file upload dor file " + name;
                LOGGER.error(msg);
                throw new IOException(msg);
            }
            if (chunk == chunks - 1) {
                // get the final file
                fileUploadService.getCompletedFile(name, getFilePath(name, folder), entry);
            }
        } else {
            // init bytes for the chunk upload
            Entry<String, ?> entry = fileUploadService.addChunk(name, 1, 0, file);
            // get the final file
            fileUploadService.getCompletedFile(name, getFilePath(name, folder), entry);
        }
    }

    /**
     * Scheduled every day at 4:00. If an user stop an upload, it
     * will be removed from memory
     */
    @Scheduled(cron = "0 0 4 * * ?")
    public void cleanupUploadedFiles() {
        fileUploadService.cleanup();
    }

    /**
     * Download a file
     * 
     * @param folder
     *            folder for the file
     * @param file
     *            to be downloaded
     * @param resp
     *            servlet response
     */
    public void downloadFile(String folder, String file, HttpServletResponse resp) {
        download(resp, file, getFilePath(file, folder != null && !folder.equals("root") ? folder : null));
    }

    /**
     * Delete a file
     * 
     * @param fileName
     * @param subFolder
     * @return true if the file has been delete or false otherwise
     */
    protected boolean deleteFile(String fileName, String subFolder) {
        String filePath = getFilePath(fileName, subFolder);
        LOGGER.debug("Deleting file '" + filePath + "'");
        File file = new File(filePath);
        if (file.exists()) {
            if (file.canWrite()) {
                try {
                    if (file.isDirectory()) {
                        FileUtils.deleteQuietly(file);
                    } else {
                        file.delete();
                    }
                    return true;
                } catch (Exception e) {
                    LOGGER.error("Error deleting '" + filePath + "' file");
                    return false;
                }
            } else {
                LOGGER.error("Incorrect permissions on file '" + filePath + "'. We can't delete it");
                return false;
            }
        } else {
            LOGGER.error("File '" + filePath + "' not exists");
            return false;
        }
    }

    /**
     * Create a new folder in a subFolder
     * 
     * @param folder
     * 
     * @return true if the new folder is successfully created and false otherwise
     */
    protected boolean newFolder(String absolutePath) {

        // get the new folder path
        String newFolderPath = absolutePath;
        // absolute path could contain target new path or only the parent folder
        if (newFolderPath != null && newFolderPath.contains(ControllerUtils.SEPARATOR)) {
            File targetFile = new File(getFilePath(newFolderPath, null));
            if (targetFile.exists()) {
                // absolute path is parent folder
                newFolderPath = generateNewFolderName(newFolderPath);
            } else {
                // absolute path is target new folder. We're going to check the
                // parent folder
                String parentFolder = newFolderPath.substring(0,
                        newFolderPath.lastIndexOf(ControllerUtils.SEPARATOR));
                File parent = new File(getFilePath(parentFolder, null));
                if (!parent.exists()) {
                    LOGGER.error("Can't create folder '" + absolutePath + "'. Parent folder don't exists");
                    return false;
                }
            }

        }
        // now, we create the folder
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Creating new folder in " + newFolderPath);
        }
        try {
            new File(getFilePath(newFolderPath, null)).mkdir();
            return true;
        } catch (Exception e) {
            LOGGER.error("Error creating '" + newFolderPath + "' folder");
            return false;
        }
    }

    /**
     * Generate a new folder name
     * @param parent
     * @return new name for the folder
     */
    protected String generateNewFolderName(String parent) {
        String filePath = getFilePath(newFolderName, parent);
        File file = new File(filePath);
        // generate new folder name
        if (file.exists()) {
            int i = 1;
            while (file.exists()) {
                filePath = getFilePath(newFolderName + " (" + (i++) + ")", parent);
                file = new File(filePath);
            }
        }
        return filePath;
    }

    protected Object renameFolder(String folder, String newName, String oldName) {
        String filePath = getFilePath(oldName, folder);
        String newPath = getFilePath(newName, folder);
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Renaming folder " + folder + " to " + newName);
        }
        File file = new File(filePath);
        File targetFile = new File(newPath);
        if (file.exists()) {
            if (!targetFile.exists()) {
                try {
                    file.renameTo(targetFile);
                    return true;
                } catch (Exception e) {
                    LOGGER.error("Error renaming '" + filePath + "' file");
                    return false;
                }
            } else {
                LOGGER.error("Target folder '" + newPath + "' already exists");
                return false;
            }
        } else {
            LOGGER.error("File '" + filePath + "' don't exists exists");
            return false;
        }
    }

    /**
     * @param folder
     * @return folder list in the folder
     */
    protected List<Map<String, Object>> getFolderList(String folder) {
        List<Map<String, Object>> data = new LinkedList<Map<String, Object>>();
        String subFolder = folder != null && !folder.equals("root") ? folder : "";
        String path = getFilePath("", subFolder);

        File folderToList = new File(path);
        if (folderToList.exists() && folderToList.isDirectory()) {
            for (String sub : folderToList.list()) {
                File file = new File(getFilePath(sub, subFolder));
                Map<String, Object> objectData = new HashMap<String, Object>();
                String id = ControllerUtils.SEPARATOR + sub;
                if (folder != null) {
                    id = folder + id;
                }
                objectData.put("id", id);
                //            objectData.put("id", ControllerUtils.SEPARATOR + sub);
                objectData.put("text", sub);
                objectData.put("size", file.length());
                objectData.put("mtime", file.lastModified());
                objectData.put("loaded", !file.isDirectory());
                objectData.put("iconCls", file.isDirectory() ? "folder" : "file");
                objectData.put("leaf", file.isDirectory() ? false : true);
                objectData.put("expanded", false);
                objectData.put("actions", getActions(file));
                data.add(objectData);
            }
        }
        return data;
    }

    /**
     * This provides actions associated to the file
     * @param file
     * @return
     */
    protected List<Object> getActions(File file) {

        return new ArrayList<Object>();
    }

    /**
     * @param folder
     * @return file list in the folder
     */
    protected Map<String, Object> getFileList(String folder) {
        Map<String, Object> result = new HashMap<String, Object>();
        String subFolder = folder != null && !folder.equals("root") ? folder : "";
        String path = getFilePath("", subFolder);

        File folderToList = new File(path);
        List<Map<String, Object>> data = new LinkedList<Map<String, Object>>();
        int index = 0;
        if (folderToList.exists() && folderToList.isDirectory()) {
            String[] children = folderToList.list();
            for (String sub : children) {
                File file = new File(getFilePath(sub, subFolder));
                Map<String, Object> objectData = new HashMap<String, Object>();
                objectData.put("name", sub);
                objectData.put("size", file.length());
                objectData.put("mtime", file.lastModified());
                objectData.put("iconCls", file.isDirectory() ? "folder" : "file");
                objectData.put("leaf", file.isDirectory() ? false : true);
                objectData.put("web_path", subFolder + ControllerUtils.SEPARATOR + sub);
                result.put(index++ + "", objectData);
                data.add(objectData);
            }
        }
        result.put(ResponseConstants.DATA, data);
        result.put(ResponseConstants.COUNT, index);

        return result;
    }

    /**
     * Delete a folder
     * 
     * @param folderName
     * @param subFolder
     */
    protected boolean deleteFolder(String folderName, String subFolder) {
        String folderPath = getFilePath(folderName, subFolder);
        LOGGER.debug("Deleting folder '" + folderPath + "'");
        File file = new File(folderPath);
        if (file.exists()) {
            if (file.canWrite()) {
                try {
                    FileUtils.deleteDirectory(file);
                    return true;
                } catch (IOException e) {
                    LOGGER.error("Error deleting '" + folderPath + "' folder");
                    return false;
                }
            } else {
                LOGGER.error("Incorrect permissions on folder '" + folderPath + "'. We can't delete it");
                return false;
            }
        } else {
            LOGGER.error("Folder '" + folderPath + "' not exists");
            return false;
        }
    }

    /**
     * Download a file with a stream
     * 
     * @param contentType
     * @param contentDisposition
     * @param resp
     * @param fileName
     * @param filePath
     * @return
     */
    protected ResponseEntity<byte[]> serveImageThumb(HttpServletResponse resp, String fileName, String filePath) {

        String contentType = "image/jpg";

        final HttpHeaders headers = new HttpHeaders();
        File toServeUp = new File(filePath);
        InputStream inputStream = null;
        String thumbPath = filePath + "_thumb";
        File fileThumb = new File(thumbPath);

        if (fileThumb.exists()) {
            try {
                inputStream = new FileInputStream(fileThumb);
            } catch (FileNotFoundException e) {

                // Also useful, this is a good was to serve down an error
                // message
                String msg = "ERROR: Could not find the file specified.";
                headers.setContentType(MediaType.TEXT_PLAIN);
                return new ResponseEntity<byte[]>(msg.getBytes(), headers, HttpStatus.NOT_FOUND);

            }
        } else {
            try {
                inputStream = getImageThumb(toServeUp, thumbPath);
                fileThumb = new File(thumbPath);
            } catch (Exception e) {

                // Also useful, this is a good was to serve down an error
                // message
                String msg = "ERROR: Could not find the file specified.";
                headers.setContentType(MediaType.TEXT_PLAIN);
                return new ResponseEntity<byte[]>(msg.getBytes(), headers, HttpStatus.NOT_FOUND);

            }
        }

        // content type
        resp.setContentType(contentType);

        Long fileSize = fileThumb.length();
        resp.setContentLength(fileSize.intValue());

        OutputStream outputStream = null;

        try {
            outputStream = resp.getOutputStream();
        } catch (IOException e) {
            String msg = "ERROR: Could not generate output stream.";
            headers.setContentType(MediaType.TEXT_PLAIN);
            return new ResponseEntity<byte[]>(msg.getBytes(), headers, HttpStatus.NOT_FOUND);
        }

        byte[] buffer = new byte[1024];

        int read = 0;
        try {

            while ((read = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, read);
            }

            // close the streams to prevent memory leaks
            outputStream.flush();
            outputStream.close();
            inputStream.close();

        } catch (Exception e) {
            String msg = "ERROR: Could not read file.";
            headers.setContentType(MediaType.TEXT_PLAIN);
            return new ResponseEntity<byte[]>(msg.getBytes(), headers, HttpStatus.NOT_FOUND);
        }

        return null;
    }

    /**
     * Generate a image thumb
     * 
     * @param toServeUp
     * @param thumbPath
     * @return
     * @throws IOException
     */
    protected InputStream getImageThumb(File toServeUp, String thumbPath) throws IOException {
        BufferedImage image = ImageIO.read(toServeUp);
        ImageIcon img = new ImageIcon(
                new ImageIcon(image).getImage().getScaledInstance(THUMB_W, THUMB_H, Image.SCALE_FAST));
        BufferedImage bimage = new BufferedImage(THUMB_W, THUMB_H, BufferedImage.TYPE_INT_RGB);

        // Copy image to buffered image
        Graphics g = bimage.createGraphics();
        g.drawImage(img.getImage(), 0, 0, null);
        g.dispose();
        ImageIO.write(bimage, "jpg", new File(thumbPath));
        return new java.io.FileInputStream(thumbPath);
    }

    /**
     * Download a file with a stream
     * 
     * @param resp
     * @param fileName
     * @param filePath
     * @return
     */
    protected ResponseEntity<byte[]> download(HttpServletResponse resp, String fileName, String filePath) {

        return download("application/octet-stream", "attachment; filename=\"" + fileName + "\"", resp, fileName,
                filePath);
    }

    /**
     * Download a file with a stream
     * 
     * @param contentType
     * @param contentDisposition
     * @param resp
     * @param fileName
     * @param filePath
     * @return
     */
    protected ResponseEntity<byte[]> download(String contentType, String contentDisposition,
            HttpServletResponse resp, String fileName, String filePath) {

        final HttpHeaders headers = new HttpHeaders();
        File toServeUp = new File(filePath);
        InputStream inputStream = null;

        try {
            inputStream = new FileInputStream(toServeUp);
        } catch (FileNotFoundException e) {

            // Also useful, this is a good was to serve down an error message
            String msg = "ERROR: Could not find the file specified.";
            headers.setContentType(MediaType.TEXT_PLAIN);
            return new ResponseEntity<byte[]>(msg.getBytes(), headers, HttpStatus.NOT_FOUND);

        }

        // content type
        if (contentType != null) {
            resp.setContentType(contentType);
        }

        // content disposition
        if (contentDisposition != null) {
            resp.setHeader("Content-Disposition", contentDisposition);
        }

        Long fileSize = toServeUp.length();
        resp.setContentLength(fileSize.intValue());

        OutputStream outputStream = null;

        try {
            outputStream = resp.getOutputStream();
        } catch (IOException e) {
            String msg = "ERROR: Could not generate output stream.";
            headers.setContentType(MediaType.TEXT_PLAIN);
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e1) {
                    // nothing
                }
            }
            return new ResponseEntity<byte[]>(msg.getBytes(), headers, HttpStatus.NOT_FOUND);
        }

        byte[] buffer = new byte[1024];

        int read = 0;
        try {

            while ((read = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, read);
            }

            // close the streams to prevent memory leaks
            outputStream.flush();
            outputStream.close();
            inputStream.close();

        } catch (Exception e) {
            String msg = "ERROR: Could not read file.";
            headers.setContentType(MediaType.TEXT_PLAIN);
            return new ResponseEntity<byte[]>(msg.getBytes(), headers, HttpStatus.NOT_FOUND);
        }

        return null;
    }

    /**
     * @return the newFolderName
     */
    public String getNewFolderName() {
        return newFolderName;
    }

    /**
     * @param newFolderName the newFolderName to set
     */
    public void setNewFolderName(String newFolderName) {
        this.newFolderName = newFolderName;
    }

    /**
     * @return the fileUploadService
     */
    public FileUploadService getFileUploadService() {
        return fileUploadService;
    }

    /**
     * @param fileUploadService the fileUploadService to set
     */
    @Autowired
    public void setFileUploadService(FileUploadService fileUploadService) {
        this.fileUploadService = fileUploadService;
    }

}