net.formio.upload.MultipartRequestPreprocessor.java Source code

Java tutorial

Introduction

Here is the source code for net.formio.upload.MultipartRequestPreprocessor.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.formio.upload;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.formio.EncodingException;
import net.formio.internal.FormUtils;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;

/**
 * Preprocesses multipart/form-data request to {@link RequestUploadedFile}(s) and
 * string request parameters.
 * @author Radek Beran
 */
public class MultipartRequestPreprocessor {

    /** Max. size per single file */
    public static final int SINGLE_FILE_SIZE_MAX = 5242880; // 5 MB
    /** Max. for total size of request. */
    public static final int TOTAL_SIZE_MAX = 10485760; // 10 MB
    /** Max. size of file that is stored only in memory. */
    public static final int SIZE_THRESHOLD = 10240; // 10 KB
    public static final String DEFAULT_ENCODING = "utf-8";

    public static File getDefaultTempDir() {
        return FormUtils.getTempDir();
    }

    private final String defaultEncoding;
    private final RequestProcessingError error;

    /**
     * Wrapper which preprocesses multipart request.
     * @param parser multipart request parser
     * @param defaultEncoding header and request parameter encoding 
     * @param tempDir temporary directory to store files bigger than specified size threshold
     * @param sizeThreshold max size of file (in bytes) that is loaded into the memory and not temporarily stored to disk
     * @param totalSizeMax maximum allowed size of the whole request in bytes
     * @param singleFileSizeMax maximum allowed size of a single uploaded file
     */
    public MultipartRequestPreprocessor(MultipartRequestParser parser, String defaultEncoding, File tempDir,
            int sizeThreshold, long totalSizeMax, long singleFileSizeMax) {
        this.defaultEncoding = defaultEncoding;
        final DiskFileItemFactory fif = new DiskFileItemFactory();
        if (tempDir != null) {
            fif.setRepository(tempDir);
        }
        if (sizeThreshold > 0) {
            fif.setSizeThreshold(sizeThreshold);
        }
        try {
            List<FileItem> fileItems = parser.parseFileItems(fif, singleFileSizeMax, totalSizeMax, defaultEncoding);
            convertToMaps(fileItems);
        } finally {
            this.error = parser.getError();
        }
    }

    /**
     * Return all request parameter names, for both regular form fields and file
     * upload fields.
     */
    public Enumeration<String> getParameterNames() {
        Set<String> allNames = new LinkedHashSet<String>();
        allNames.addAll(regularParams.keySet());
        allNames.addAll(fileParams.keySet());
        return Collections.enumeration(allNames);
    }

    /**
     * Return the parameter value. Applies only to regular parameters, not to
     * file upload parameters.
     * 
     * <p>
     * If the parameter is not present in the underlying request, then
     * <tt>null</tt> is returned.
     * <p>
     * If the parameter is present, but has no associated value, then an empty
     * string is returned.
     * <p>
     * If the parameter is multivalued, the first value that appears in the
     * request is returned.
     */
    public String getParameter(String name) {
        String result = null;
        List<String> values = regularParams.get(name);
        if (values == null) {
            // you might try the wrappee, to see if it has a value
        } else if (values.isEmpty()) {
            // param name known, but no values present
            result = "";
        } else {
            // return first value in list
            result = values.get(FIRST_VALUE);
        }
        return result;
    }

    /**
     * Return the parameter values. Applies only to regular parameters, not to
     * file upload parameters.
     */
    public String[] getParameterValues(String name) {
        String[] result = null;
        List<String> values = regularParams.get(name);
        if (values != null) {
            result = values.toArray(new String[values.size()]);
        }
        return result;
    }

    /**
     * Return a {@code Map<String, String[]>} for all regular parameters. Does
     * not return any file upload parameters at all.
     */
    public Map<String, String[]> getParameterMap() {
        Map<String, String[]> res = new LinkedHashMap<String, String[]>();
        for (Map.Entry<String, List<String>> entry : regularParams.entrySet()) {
            res.put(entry.getKey(), entry.getValue().toArray(new String[0]));
        }
        return Collections.unmodifiableMap(res);
    }

    /**
     * Return the {@link FileItem} of the given name.
     * <p>
     * If the name is unknown, then return <tt>null</tt>.
     */
    public RequestUploadedFile[] getUploadedFiles(String paramName) {
        List<RequestUploadedFile> files = fileParams.get(paramName);
        if (files == null)
            return null;
        return files.toArray(new RequestUploadedFile[files.size()]);
    }

    /**
     * Returns error from processing the request if there was one, or {@code null}.
     * @return
     */
    public RequestProcessingError getError() {
        return this.error;
    }

    // PRIVATE

    /** Store regular params only. May be multivalued (hence the List). */
    private final Map<String, List<String>> regularParams = new LinkedHashMap<String, List<String>>();

    /** Store file params only. */
    private final Map<String, List<RequestUploadedFile>> fileParams = new LinkedHashMap<String, List<RequestUploadedFile>>();
    private static final int FIRST_VALUE = 0;
    private static final Pattern EXTRACT_LASTPART_PATTERN = Pattern.compile(".*[\\\\/]([^\\\\/]+)");

    private void convertToMaps(List<FileItem> fileItems) {
        for (FileItem item : fileItems) {
            String cts = item.getContentType();

            if (!item.isFormField()) {
                // not simple form field
                String filename = item.getName();
                long size = item.getSize(); // size in bytes
                // some browsers are sending "files" even if none is selected?
                if (size == 0
                        && ((cts == null || cts.isEmpty()) || "application/octet-stream".equalsIgnoreCase(cts))
                        && (filename == null || filename.isEmpty()))
                    continue;

                String fileNameLastPart = filename;
                if (filename != null) {
                    Matcher m = EXTRACT_LASTPART_PATTERN.matcher(filename);
                    if (m.matches()) {
                        fileNameLastPart = m.group(1);
                    }
                }
                String mime = cts;
                if (mime == null) {
                    mime = guessContentType(filename);
                }

                RequestUploadedFile file = new RequestUploadedFile(fileNameLastPart, mime, size, item);
                if (fileParams.get(item.getFieldName()) != null) {
                    addMultivaluedFile(file, item);
                } else {
                    addSingleValueFile(file, item);
                }
            } else {
                if (regularParams.get(item.getFieldName()) != null) {
                    addMultivaluedItem(item);
                } else {
                    addSingleValueItem(item);
                }
            }
        }
    }

    private void addSingleValueItem(FileItem item) {
        List<String> list = new ArrayList<String>();
        addItemValue(list, item);
        regularParams.put(item.getFieldName(), list);
    }

    private void addMultivaluedItem(FileItem item) {
        List<String> values = regularParams.get(item.getFieldName());
        addItemValue(values, item);
    }

    private void addSingleValueFile(RequestUploadedFile file, FileItem item) {
        List<RequestUploadedFile> list = new ArrayList<RequestUploadedFile>();
        list.add(file);
        fileParams.put(item.getFieldName(), list);
    }

    private void addMultivaluedFile(RequestUploadedFile file, FileItem item) {
        List<RequestUploadedFile> values = fileParams.get(item.getFieldName());
        values.add(file);
    }

    private void addItemValue(List<String> list, FileItem item) {
        try {
            list.add(item.getString(defaultEncoding));
        } catch (UnsupportedEncodingException ex) {
            throw new EncodingException(ex.getMessage(), ex);
        }
    }

    private String guessContentType(String filename) {
        if (filename != null) {
            int d = filename.lastIndexOf('.');
            if (d >= 0) {
                String ext = filename.substring(d);
                if (ext.equalsIgnoreCase("txt"))
                    return "text/plain";
                if (ext.equalsIgnoreCase("html"))
                    return "text/html";
                if (ext.equalsIgnoreCase("htm"))
                    return "text/htm";
                if (ext.equalsIgnoreCase("jpg"))
                    return "image/jpeg";
                if (ext.equalsIgnoreCase("jpeg"))
                    return "image/jpeg";
                if (ext.equalsIgnoreCase("gif"))
                    return "image/gif";
                if (ext.equalsIgnoreCase("png"))
                    return "image/png";
                if (ext.equalsIgnoreCase("bmp"))
                    return "image/bmp";
            }
        }
        return "application/octet-stream";
    }
}