views:

5165

answers:

3

I'm having two problems when trying to configure the Struts 2 File Upload Interceptor in my application. I want to change the parameter maximumSize (the default value is 2 MB, I need it to be 5 MB) and the message resource struts.messages.error.file.too.large (the app locale is pt_BR, so the message is in portuguese, not english).

The app current configuration follows:

struts.properties

struts.locale=pt_BR 
struts.custom.i18n.resources=MessageResources

struts.xml

<package name="default" namespace="/" extends="struts-default">
    <interceptors>
        <interceptor name="login" class="br.com.probank.interceptor.LoginInterceptor"/>
        <interceptor-stack name="defaultLoginStack">
            <interceptor-ref name="login" />
            <interceptor-ref name="defaultStack"/>
        </interceptor-stack>
    </interceptors>

    <default-interceptor-ref name="defaultLoginStack" />
    ...
</package>

...
<package name="proposta" namespace="/proposta" extends="default">
    <action name="salvarAnexoProposta" method="salvarAnexoProposta" class="br.com.probank.action.AnexoPropostaAction">
        <interceptor-ref name="defaultLoginStack">
            <param name="fileUpload.maximumSize">5242880</param>
        </interceptor-ref>
        <result name="success">/jsp/listagemAnexosPropostaForm.jsp</result>
        <result name="input">/jsp/crudAnexoPropostaForm.jsp</result>
        <result name="error">/jsp/error.jsp</result>
        <result name="redirect" type="redirect">${redirectLink}</result>
    </action>
</package>

MessageResources.properties

...
struts.messages.error.file.too.large=O tamanho do arquivo...

There is nothing special about my Action implementation and my JSP code. They follow the example found http://struts.apache.org/2.1.6/docs/file-upload-interceptor.html. When I try to upload a file with more than 5 MB the app shows the message "the request was rejected because its size (6229458) exceeds the configured maximum (2097152)" - the default File Upload message with the default maximumSize value.

I try to put the message resource struts.messages.error.file.too.large in a struts-messages.properties but the message didn't change after that. What is the proper way to configure the File Upload Interceptor? I'm using Struts 2 2.1.7. Thanks in advance.

+1  A: 

Finally solved the entire puzzle! struts.xml and MessageResource.properties were correctly configured. The problem was struts.multipart.maxSize value. This value have to be bigger than the desired upload limit (5242880 in my app), so I set it as 10000000. If struts.multipart.maxSize value is equal or less then fileUpload.maximumSize the library used by Struts 2 to do the upload stops the upload process (and writes the error message) before the file upload interceptor has a chance to do its job.

matheus.emm
+2  A: 

The solution provided by you isn't entirely correct in the sense that If I want strict upload limits along with i18n, this won't work. I've also created an issue with strut2 for this. Please look at the following link http://issues.apache.org/struts/browse/WW-3177. It's due to be fixed in struts2.1.9 and is already assigned to a struts team member.

In between, I'm using a hack. I browsed the struts2 source code and found the code for FileUploadInterceptor. Using that code, I created my own. Here's the code below. You can find details of problem at the link above. Hope this helps.

import java.io.File; import java.util.*;

import javax.servlet.http.HttpServletRequest; import org.apache.struts2.ServletActionContext; import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.ActionProxy; import com.opensymphony.xwork2.ValidationAware; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; import com.opensymphony.xwork2.util.LocalizedTextUtil; import com.opensymphony.xwork2.util.TextParseUtil; import com.opensymphony.xwork2.util.logging.Logger; import com.opensymphony.xwork2.util.logging.LoggerFactory;

public class CustomFileUploaderInterceptor extends AbstractInterceptor {

private static final long serialVersionUID = -4764627478894962478L;

protected static final Logger LOG =           LoggerFactory.getLogger(CustomFileUploaderInterceptor.class);
private static final String DEFAULT_MESSAGE = "no.message.found";

protected boolean useActionMessageBundle;

protected Long maximumSize;
protected Set<String> allowedTypesSet = Collections.emptySet();
protected Set<String> allowedExtensionsSet = Collections.emptySet();

public void setUseActionMessageBundle(String value) {
    this.useActionMessageBundle = Boolean.valueOf(value);
}

/**
 * Sets the allowed extensions
 *
 * @param allowedExtensions A comma-delimited list of extensions
 */
public void setAllowedExtensions(String allowedExtensions) {
    allowedExtensionsSet = TextParseUtil.commaDelimitedStringToSet(allowedExtensions);
}

/**
 * Sets the allowed mimetypes
 *
 * @param allowedTypes A comma-delimited list of types
 */
public void setAllowedTypes(String allowedTypes) {
    allowedTypesSet = TextParseUtil.commaDelimitedStringToSet(allowedTypes);
}

/**
 * Sets the maximum size of an uploaded file
 *
 * @param maximumSize The maximum size in bytes
 */
public void setMaximumSize(Long maximumSize) {
    this.maximumSize = maximumSize;
}

/* (non-Javadoc)
 * @see com.opensymphony.xwork2.interceptor.Interceptor#intercept(com.opensymphony.xwork2.ActionInvocation)
 */
public String intercept(ActionInvocation invocation) throws Exception {
    ActionContext ac = invocation.getInvocationContext();
    Map<String, Object> params1 = ac.getParameters();
    Set<String> keySet = params1.keySet();
    for(String s : keySet){
        LOG.debug("Key: "+ s +", Value: " + params1.get(s).toString());
    }
    HttpServletRequest request = (HttpServletRequest) ac.get(ServletActionContext.HTTP_REQUEST);

    if (!(request instanceof MultiPartRequestWrapper)) {
        if (LOG.isDebugEnabled()) {
            ActionProxy proxy = invocation.getProxy();
            LOG.debug(getTextMessage("struts.messages.bypass.request", new Object[]{proxy.getNamespace(), proxy.getActionName()}, ac.getLocale()));
        }

        return invocation.invoke();
    }

    ValidationAware validation = null;

    Object action = invocation.getAction();

    if (action instanceof ValidationAware) {
        validation = (ValidationAware) action;
    }

    MultiPartRequestWrapper multiWrapper = (MultiPartRequestWrapper) request;

    if (multiWrapper.hasErrors()) {
        String inputName = null;
        if(multiWrapper.getFileParameterNames().hasMoreElements()){
            inputName = (String)multiWrapper.getFileParameterNames().nextElement();

        }
        for (String error : multiWrapper.getErrors()) {
            if (validation != null) {
                Object[] args = new Object[]{inputName};
                validation.addActionError(getTextMessage(action, "struts.messages.error.file.too.large", args, ac.getLocale()));
            }
            LOG.error(error);
        }
    }

    // bind allowed Files
    Enumeration fileParameterNames = multiWrapper.getFileParameterNames();
    while (fileParameterNames != null && fileParameterNames.hasMoreElements()) {
        // get the value of this input tag
        String inputName = (String) fileParameterNames.nextElement();

        // get the content type
        String[] contentType = multiWrapper.getContentTypes(inputName);

        if (isNonEmpty(contentType)) {
            // get the name of the file from the input tag
            String[] fileName = multiWrapper.getFileNames(inputName);

            if (isNonEmpty(fileName)) {
                // get a File object for the uploaded File
                File[] files = multiWrapper.getFiles(inputName);
                if (files != null && files.length > 0) {
                    List<File> acceptedFiles = new ArrayList<File>(files.length);
                    List<String> acceptedContentTypes = new ArrayList<String>(files.length);
                    List<String> acceptedFileNames = new ArrayList<String>(files.length);
                    String contentTypeName = inputName + "ContentType";
                    String fileNameName = inputName + "FileName";

                    for (int index = 0; index < files.length; index++) {
                        if (acceptFile(action, files[index], fileName[index], contentType[index], inputName, validation, ac.getLocale())) {
                            acceptedFiles.add(files[index]);
                            acceptedContentTypes.add(contentType[index]);
                            acceptedFileNames.add(fileName[index]);
                        }
                    }

                    if (!acceptedFiles.isEmpty()) {
                        Map<String, Object> params = ac.getParameters();

                        params.put(inputName, acceptedFiles.toArray(new File[acceptedFiles.size()]));
                        params.put(contentTypeName, acceptedContentTypes.toArray(new String[acceptedContentTypes.size()]));
                        params.put(fileNameName, acceptedFileNames.toArray(new String[acceptedFileNames.size()]));
                    }
                }
            } else {
                LOG.error(getTextMessage(action, "struts.messages.invalid.file", new Object[]{inputName}, ac.getLocale()));
            }
        } else {
            LOG.error(getTextMessage(action, "struts.messages.invalid.content.type", new Object[]{inputName}, ac.getLocale()));
        }
    }

    // invoke action
    String result = invocation.invoke();

    // cleanup
    fileParameterNames = multiWrapper.getFileParameterNames();
    while (fileParameterNames != null && fileParameterNames.hasMoreElements()) {
        String inputValue = (String) fileParameterNames.nextElement();
        File[] files = multiWrapper.getFiles(inputValue);

        for (File currentFile : files) {
            if (LOG.isInfoEnabled()) {
                LOG.info(getTextMessage(action, "struts.messages.removing.file", new Object[]{inputValue, currentFile}, ac.getLocale()));
            }

            if ((currentFile != null) && currentFile.isFile()) {
                currentFile.delete();
            }
        }
    }

    return result;
}

/**
 * Override for added functionality. Checks if the proposed file is acceptable based on contentType and size.
 *
 * @param action      - uploading action for message retrieval.
 * @param file        - proposed upload file.
 * @param contentType - contentType of the file.
 * @param inputName   - inputName of the file.
 * @param validation  - Non-null ValidationAware if the action implements ValidationAware, allowing for better
 *                    logging.
 * @param locale
 * @return true if the proposed file is acceptable by contentType and size.
 */
protected boolean acceptFile(Object action, File file, String filename, String contentType, String inputName, ValidationAware validation, Locale locale) {
    boolean fileIsAcceptable = false;

    // If it's null the upload failed
    if (file == null) {
        String errMsg = getTextMessage(action, "struts.messages.error.uploading", new Object[]{inputName}, locale);
        if (validation != null) {
            validation.addFieldError(inputName, errMsg);
        }

        LOG.error(errMsg);
    } else if (maximumSize != null && maximumSize < file.length()) {
        String errMsg = getTextMessage(action, "struts.messages.error.file.too.large", new Object[]{inputName, filename, file.getName(), "" + file.length()}, locale);
        if (validation != null) {
            validation.addFieldError(inputName, errMsg);
        }

        LOG.error(errMsg);
    } else if ((!allowedTypesSet.isEmpty()) && (!containsItem(allowedTypesSet, contentType))) {
        String errMsg = getTextMessage(action, "struts.messages.error.content.type.not.allowed", new Object[]{inputName, filename, file.getName(), contentType}, locale);
        if (validation != null) {
            validation.addFieldError(inputName, errMsg);
        }

        LOG.error(errMsg);
    } else if ((! allowedExtensionsSet.isEmpty()) && (!hasAllowedExtension(allowedExtensionsSet, filename))) {
        String errMsg = getTextMessage(action, "struts.messages.error.file.extension.not.allowed", new Object[]{inputName, filename, file.getName(), contentType}, locale);
        if (validation != null) {
            validation.addFieldError(inputName, errMsg);
        }

        LOG.error(errMsg);
    } else {
        fileIsAcceptable = true;
    }

    return fileIsAcceptable;
}

/**
 * @param extensionCollection - Collection of extensions (all lowercase).
 * @param filename            - filename to check.
 * @return true if the filename has an allowed extension, false otherwise.
 */
private static boolean hasAllowedExtension(Collection<String> extensionCollection, String filename) {
    if (filename == null) {
        return false;
    }

    String lowercaseFilename = filename.toLowerCase();
    for (String extension : extensionCollection) {
        if (lowercaseFilename.endsWith(extension)) {
            return true;
        }
    }

    return false;
}

/**
 * @param itemCollection - Collection of string items (all lowercase).
 * @param item           - Item to search for.
 * @return true if itemCollection contains the item, false otherwise.
 */
private static boolean containsItem(Collection<String> itemCollection, String item) {
    return itemCollection.contains(item.toLowerCase());
}

private static boolean isNonEmpty(Object[] objArray) {
    boolean result = false;
    for (int index = 0; index < objArray.length && !result; index++) {
        if (objArray[index] != null) {
            result = true;
        }
    }
    return result;
}

private String getTextMessage(String messageKey, Object[] args, Locale locale) {
    return getTextMessage(null, messageKey, args, locale);
}

private String getTextMessage(Object action, String messageKey, Object[] args, Locale locale) {
    if (args == null || args.length == 0) {
        if ( action != null && useActionMessageBundle) {
            return LocalizedTextUtil.findText(action.getClass(), messageKey, locale);
        }
        return LocalizedTextUtil.findText(this.getClass(), messageKey, locale);                        
    } else {
        if ( action != null && useActionMessageBundle) {
            return LocalizedTextUtil.findText(action.getClass(), messageKey, locale, DEFAULT_MESSAGE, args);
        }
        return LocalizedTextUtil.findText(this.getClass(), messageKey, locale, DEFAULT_MESSAGE, args);
    }
}

}

Amit Sharma
A: 

How to show the Interceptor error message back to the form....While using FileUpload Interceptor.

jayavardan