com.xpn.xwiki.plugin.fileupload.FileUploadPlugin.java Source code

Java tutorial

Introduction

Here is the source code for com.xpn.xwiki.plugin.fileupload.FileUploadPlugin.java

Source

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * 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 com.xpn.xwiki.plugin.fileupload;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUpload;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.RequestContext;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.servlet.ServletRequestContext;
import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.xpn.xwiki.XWiki;
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.XWikiException;
import com.xpn.xwiki.api.Api;
import com.xpn.xwiki.plugin.XWikiDefaultPlugin;
import com.xpn.xwiki.plugin.XWikiPluginInterface;

/**
 * Plugin that offers access to uploaded files. The uploaded files are automatically parsed and preserved as a list of
 * {@link FileItem}s.
 * 
 * @version $Id: b27704bb6d912c5d7fefd2a8671e662b791d1cea $
 * @deprecated the plugin technology is deprecated, consider rewriting as components
 */
@Deprecated
public class FileUploadPlugin extends XWikiDefaultPlugin {
    /**
     * The name of the plugin; the key that can be used to retrieve this plugin from the context.
     * 
     * @see XWikiPluginInterface#getName()
     */
    public static final String PLUGIN_NAME = "fileupload";

    /**
     * The context name of the uploaded file list. It can be used to retrieve the list of uploaded files from the
     * context.
     */
    public static final String FILE_LIST_KEY = "fileuploadlist";

    /**
     * The name of the parameter that can be set in the global XWiki preferences to override the default maximum file
     * size.
     */
    public static final String UPLOAD_MAXSIZE_PARAMETER = "upload_maxsize";

    /**
     * The name of the parameter that can be set in the global XWiki preferences to override the default size threshold
     * for on-disk storage.
     */
    public static final String UPLOAD_SIZETHRESHOLD_PARAMETER = "upload_sizethreshold";

    /**
     * Log object to log messages in this class.
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(FileUploadPlugin.class);

    /**
     * The default maximum size for uploaded documents. This limit can be changed using the <tt>upload_maxsize</tt>
     * XWiki preference.
     */
    private static final long UPLOAD_DEFAULT_MAXSIZE = 33554432L;

    /**
     * The default maximum size for in-memory stored uploaded documents. If a file is larger than this limit, it will be
     * stored on disk until the current request finishes. This limit can be changed using the
     * <tt>upload_sizethreshold</tt> XWiki preference.
     */
    private static final long UPLOAD_DEFAULT_SIZETHRESHOLD = 100000L;

    /**
     * @param name the plugin name
     * @param className the plugin classname (used in logs for example)
     * @param context the XWiki Context
     * @see XWikiDefaultPlugin#XWikiDefaultPlugin(String,String,com.xpn.xwiki.XWikiContext)
     */
    public FileUploadPlugin(String name, String className, XWikiContext context) {
        super(name, className, context);
    }

    /**
     * {@inheritDoc}
     * 
     * @see com.xpn.xwiki.plugin.XWikiDefaultPlugin#getName()
     */
    @Override
    public String getName() {
        return PLUGIN_NAME;
    }

    /**
     * {@inheritDoc}
     * 
     * @see com.xpn.xwiki.plugin.XWikiDefaultPlugin#init(XWikiContext)
     */
    @Override
    public void init(XWikiContext context) {
        super.init(context);
    }

    /**
     * {@inheritDoc}
     * 
     * @see com.xpn.xwiki.plugin.XWikiDefaultPlugin#virtualInit(XWikiContext)
     */
    @Override
    public void virtualInit(XWikiContext context) {
        super.virtualInit(context);
    }

    /**
     * {@inheritDoc}
     * 
     * @see com.xpn.xwiki.plugin.XWikiDefaultPlugin#getPluginApi(XWikiPluginInterface, XWikiContext)
     */
    @Override
    public Api getPluginApi(XWikiPluginInterface plugin, XWikiContext context) {
        return new FileUploadPluginApi((FileUploadPlugin) plugin, context);
    }

    /**
     * {@inheritDoc} Make sure we don't leave files in temp directories and in memory.
     */
    @Override
    public void endRendering(XWikiContext context) {
        // we used to call cleanFileList here but we should not anymore as endRendering is called to
        // many times and empties the file upload list. This is handled by XWikiAction and
        // XWikiPortlet which clean up lists in a finally block
    }

    /**
     * Deletes all temporary files of the upload.
     * 
     * @param context Context of the request.
     * @see FileUploadPluginApi#cleanFileList()
     */
    public void cleanFileList(XWikiContext context) {
        LOGGER.debug("Cleaning uploaded files");

        List<FileItem> fileuploadlist = getFileItems(context);
        if (fileuploadlist != null) {
            for (FileItem item : fileuploadlist) {
                try {
                    item.delete();
                } catch (Exception ex) {
                    LOGGER.warn("Exception cleaning uploaded files", ex);
                }
            }
            context.remove(FILE_LIST_KEY);
        }
    }

    /**
     * Loads the list of uploaded files in the context if there are any uploaded files.
     * 
     * @param context Context of the request.
     * @throws XWikiException An XWikiException is thrown if the request could not be parsed.
     * @see FileUploadPluginApi#loadFileList()
     */
    public void loadFileList(XWikiContext context) throws XWikiException {
        XWiki xwiki = context.getWiki();
        loadFileList(xwiki.getSpacePreferenceAsLong(UPLOAD_MAXSIZE_PARAMETER, UPLOAD_DEFAULT_MAXSIZE, context),
                (int) xwiki.getSpacePreferenceAsLong(UPLOAD_SIZETHRESHOLD_PARAMETER, UPLOAD_DEFAULT_SIZETHRESHOLD,
                        context),
                xwiki.Param("xwiki.upload.tempdir"), context);
    }

    /**
     * Loads the list of uploaded files in the context if there are any uploaded files.
     * 
     * @param uploadMaxSize Maximum size of the uploaded files.
     * @param uploadSizeThreashold Threashold over which the file data should be stored on disk, and not in memory.
     * @param tempdir Temporary directory to store the uploaded files that are not kept in memory.
     * @param context Context of the request.
     * @throws XWikiException if the request could not be parsed, or the maximum file size was reached.
     * @see FileUploadPluginApi#loadFileList(long, int, String)
     */
    public void loadFileList(long uploadMaxSize, int uploadSizeThreashold, String tempdir, XWikiContext context)
            throws XWikiException {
        LOGGER.debug("Loading uploaded files");

        // If we already have a file list then loadFileList was already called
        // Continuing would empty the list.. We need to stop.
        if (context.get(FILE_LIST_KEY) != null) {
            LOGGER.debug("Called loadFileList twice");

            return;
        }

        // Get the FileUpload Data
        // Make sure the factory only ever creates file items which will be deleted when the jvm is stopped.
        DiskFileItemFactory factory = new DiskFileItemFactory() {
            public FileItem createItem(String fieldName, String contentType, boolean isFormField, String fileName) {
                try {
                    final DiskFileItem item = (DiskFileItem) super.createItem(fieldName, contentType, isFormField,
                            fileName);
                    // Needed to make sure the File object is created.
                    item.getOutputStream();
                    item.getStoreLocation().deleteOnExit();
                    return item;
                } catch (IOException e) {
                    String path = System.getProperty("java.io.tmpdir");
                    if (super.getRepository() != null) {
                        path = super.getRepository().getPath();
                    }
                    throw new RuntimeException("Unable to create a temporary file for saving the attachment. "
                            + "Do you have write access on " + path + "?");
                }
            }
        };

        factory.setSizeThreshold(uploadSizeThreashold);

        if (tempdir != null) {
            File tempdirFile = new File(tempdir);
            if (tempdirFile.mkdirs() && tempdirFile.canWrite()) {
                factory.setRepository(tempdirFile);
            }
        }

        // TODO: Does this work in portlet mode, or we must use PortletFileUpload?
        FileUpload fileupload = new ServletFileUpload(factory);
        RequestContext reqContext = new ServletRequestContext(context.getRequest().getHttpServletRequest());
        fileupload.setSizeMax(uploadMaxSize);
        // context.put("fileupload", fileupload);

        try {
            @SuppressWarnings("unchecked")
            List<FileItem> list = fileupload.parseRequest(reqContext);
            if (list.size() > 0) {
                LOGGER.info("Loaded " + list.size() + " uploaded files");
            }
            // We store the file list in the context
            context.put(FILE_LIST_KEY, list);
        } catch (FileUploadBase.SizeLimitExceededException e) {
            throw new XWikiException(XWikiException.MODULE_XWIKI_APP,
                    XWikiException.ERROR_XWIKI_APP_FILE_EXCEPTION_MAXSIZE, "Exception uploaded file");
        } catch (Exception e) {
            throw new XWikiException(XWikiException.MODULE_XWIKI_APP,
                    XWikiException.ERROR_XWIKI_APP_UPLOAD_PARSE_EXCEPTION, "Exception while parsing uploaded file",
                    e);
        }
    }

    /**
     * Allows to retrieve the current list of uploaded files, as a list of {@link FileItem}s.
     * {@link #loadFileList(XWikiContext)} needs to be called beforehand
     * 
     * @param context Context of the request.
     * @return A list of FileItem elements.
     * @see FileUploadPluginApi#getFileItems()
     */
    public List<FileItem> getFileItems(XWikiContext context) {
        return (List<FileItem>) context.get(FILE_LIST_KEY);
    }

    /**
     * Allows to retrieve the contents of an uploaded file as a sequence of bytes. {@link #loadFileList(XWikiContext)}
     * needs to be called beforehand.
     * 
     * @param formfieldName The name of the form field.
     * @param context Context of the request.
     * @return The contents of the file.
     * @throws XWikiException if the data could not be read.
     * @see FileUploadPluginApi#getFileItemData(String)
     */
    public byte[] getFileItemData(String formfieldName, XWikiContext context) throws XWikiException {
        int size = getFileItemSize(formfieldName, context);

        if (size == 0) {
            return null;
        }

        byte[] data = new byte[size];

        try {
            InputStream fileis = getFileItemInputStream(formfieldName, context);
            if (fileis != null) {
                fileis.read(data);
                fileis.close();
            }
        } catch (java.lang.OutOfMemoryError e) {
            throw new XWikiException(XWikiException.MODULE_XWIKI_APP,
                    XWikiException.ERROR_XWIKI_APP_JAVA_HEAP_SPACE, "Java Heap Space, Out of memory exception", e);
        } catch (IOException ie) {
            throw new XWikiException(XWikiException.MODULE_XWIKI_APP,
                    XWikiException.ERROR_XWIKI_APP_UPLOAD_FILE_EXCEPTION,
                    "Exception while reading uploaded parsed file", ie);
        }

        return data;
    }

    /**
     * Allows to retrieve the contents of an uploaded file as a stream. {@link #loadFileList(XWikiContext)} needs to be
     * called beforehand.
     * 
     * @param formfieldName The name of the form field.
     * @param context Context of the request.
     * @return a InputStream on the file content
     * @throws IOException if I/O problem occurs
     * @since 2.3M2
     */
    public InputStream getFileItemInputStream(String formfieldName, XWikiContext context) throws IOException {
        FileItem fileitem = getFile(formfieldName, context);

        if (fileitem == null) {
            return null;
        }

        return fileitem.getInputStream();
    }

    /**
     * Retrieve the size of a file content in byte. {@link #loadFileList(XWikiContext)} needs to be called beforehand.
     * 
     * @param formfieldName The name of the form field.
     * @param context Context of the request.
     * @return the size of the file in byte
     * @since 2.3M2
     */
    public int getFileItemSize(String formfieldName, XWikiContext context) {
        FileItem fileitem = getFile(formfieldName, context);

        if (fileitem == null) {
            return 0;
        }

        return ((int) fileitem.getSize());
    }

    /**
     * Allows to retrieve the contents of an uploaded file as a string. {@link #loadFileList(XWikiContext)} needs to be
     * called beforehand.
     * 
     * @param formfieldName The name of the form field.
     * @param context Context of the request.
     * @return The contents of the file.
     * @throws XWikiException if the data could not be read.
     * @see FileUploadPluginApi#getFileItemAsString(String)
     */
    public String getFileItemAsString(String formfieldName, XWikiContext context) throws XWikiException {
        byte[] data = getFileItemData(formfieldName, context);
        if (data == null) {
            return null;
        }

        return new String(data);
    }

    /**
     * Allows to retrieve the contents of an uploaded file as a string. {@link #loadFileList(XWikiContext)} needs to be
     * called beforehand.
     * 
     * @deprecated not well named, use {@link #getFileItemAsString(String, com.xpn.xwiki.XWikiContext)}
     * @param formfieldName The name of the form field.
     * @param context Context of the request.
     * @return The contents of the file.
     * @throws XWikiException Exception is thrown if the data could not be read.
     * @see FileUploadPluginApi#getFileItemAsString(String)
     */
    @Deprecated
    public String getFileItem(String formfieldName, XWikiContext context) throws XWikiException {
        return getFileItemAsString(formfieldName, context);
    }

    /**
     * Retrieves the list of FileItem names. {@link #loadFileList(XWikiContext)} needs to be called beforehand.
     * 
     * @param context Context of the request
     * @return List of strings of the item names
     */
    public List<String> getFileItemNames(XWikiContext context) {
        List<String> itemnames = new ArrayList<String>();
        List<FileItem> fileuploadlist = getFileItems(context);
        if (fileuploadlist == null) {
            return itemnames;
        }

        for (FileItem item : fileuploadlist) {
            itemnames.add(item.getFieldName());
        }

        return itemnames;
    }

    /**
     * Get the name of the file uploaded for a form field.
     * 
     * @param formfieldName The name of the form field.
     * @param context Context of the request.
     * @return The file name, or <tt>null</tt> if no file was uploaded for that form field.
     */
    public String getFileName(String formfieldName, XWikiContext context) {
        FileItem fileitem = getFile(formfieldName, context);

        // We need to strip the file path. See http://commons.apache.org/fileupload/faq.html#whole-path-from-IE
        return (fileitem == null) ? null : FilenameUtils.getName(fileitem.getName());
    }

    /**
     * Return the FileItem corresponding to the file uploaded for a form field.
     * 
     * @param formfieldName The name of the form field.
     * @param context Context of the request.
     * @return The corresponding FileItem, or <tt>null</tt> if no file was uploaded for that form field.
     */
    public FileItem getFile(String formfieldName, XWikiContext context) {
        LOGGER.debug("Searching file uploaded for field " + formfieldName);

        List<FileItem> fileuploadlist = getFileItems(context);
        if (fileuploadlist == null) {
            return null;
        }

        FileItem fileitem = null;
        for (FileItem item : fileuploadlist) {
            if (formfieldName.equals(item.getFieldName())) {
                fileitem = item;
                LOGGER.debug("Found uploaded file!");
                break;
            }
        }

        return fileitem;
    }
}