This is an automated email from the ASF dual-hosted git repository.

lukaszlenart pushed a commit to branch feature/WW-5371-modern-upload
in repository https://gitbox.apache.org/repos/asf/struts.git

commit 0bc0217b9de49545b8307a98d6ca2cce3a85390f
Author: Lukasz Lenart <lukaszlen...@apache.org>
AuthorDate: Mon Dec 11 07:37:38 2023 +0100

    WW-5371 Implements action based file upload
---
 .../UploadedFilesAware.java}                       |  32 +-
 .../multipart/JakartaMultiPartRequest.java         |   6 +-
 .../multipart/JakartaStreamMultiPartRequest.java   |  36 ++-
 .../dispatcher/multipart/StrutsUploadedFile.java   |  51 ++-
 .../struts2/dispatcher/multipart/UploadedFile.java |   4 +
 .../interceptor/AbstractFileUploadInterceptor.java | 247 ++++++++++++++
 .../interceptor/ActionFileUploadInterceptor.java   | 248 ++++++++++++++
 .../struts2/interceptor/FileUploadInterceptor.java | 256 ++-------------
 core/src/main/resources/struts-default.xml         |   9 +
 .../conversion/UploadedFileConverterTest.java      |  19 +-
 ...t.java => ActionFileUploadInterceptorTest.java} | 359 ++++++++++-----------
 .../interceptor/FileUploadInterceptorTest.java     | 170 +++++-----
 .../dispatcher/multipart/PellMultiPartRequest.java |  13 +-
 13 files changed, 897 insertions(+), 553 deletions(-)

diff --git 
a/core/src/main/java/org/apache/struts2/dispatcher/multipart/UploadedFile.java 
b/core/src/main/java/org/apache/struts2/action/UploadedFilesAware.java
similarity index 54%
copy from 
core/src/main/java/org/apache/struts2/dispatcher/multipart/UploadedFile.java
copy to core/src/main/java/org/apache/struts2/action/UploadedFilesAware.java
index d4edb5221..635e0b2d4 100644
--- 
a/core/src/main/java/org/apache/struts2/dispatcher/multipart/UploadedFile.java
+++ b/core/src/main/java/org/apache/struts2/action/UploadedFilesAware.java
@@ -16,23 +16,25 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.dispatcher.multipart;
+package org.apache.struts2.action;
 
-/**
- * Virtual representation of a uploaded file used by {@link MultiPartRequest}
- */
-public interface UploadedFile {
-
-    Long length();
-
-    String getName();
+import org.apache.struts2.dispatcher.multipart.UploadedFile;
 
-    boolean isFile();
+import java.util.List;
 
-    boolean delete();
-
-    String getAbsolutePath();
-
-    Object getContent();
+/**
+ * Actions that want to be aware of all the uploaded file should implement 
this interface.
+ * The {@link org.apache.struts2.interceptor.ActionFileUploadInterceptor} will 
use the interface
+ * to notify action about the multiple uploaded files.
+ */
+public interface UploadedFilesAware {
+
+    /**
+     * Notifies action about the multiple uploaded files, when a single file 
is uploaded
+     * the list will have just one element
+     *
+     * @param uploadedFiles a list of {@link UploadedFile}.
+     */
+    void withUploadedFiles(List<UploadedFile> uploadedFiles);
 
 }
diff --git 
a/core/src/main/java/org/apache/struts2/dispatcher/multipart/JakartaMultiPartRequest.java
 
b/core/src/main/java/org/apache/struts2/dispatcher/multipart/JakartaMultiPartRequest.java
index 20b948fd3..1c0f458a0 100644
--- 
a/core/src/main/java/org/apache/struts2/dispatcher/multipart/JakartaMultiPartRequest.java
+++ 
b/core/src/main/java/org/apache/struts2/dispatcher/multipart/JakartaMultiPartRequest.java
@@ -243,7 +243,11 @@ public class JakartaMultiPartRequest extends 
AbstractMultiPartRequest {
                     LOG.error("Cannot write uploaded empty file to disk: {}", 
storeLocation.getAbsolutePath(), e);
                 }
             }
-            fileList.add(new StrutsUploadedFile(storeLocation));
+            UploadedFile uploadedFile = 
StrutsUploadedFile.Builder.create(storeLocation)
+                .withContentType(fileItem.getContentType())
+                .withOriginalName(fileItem.getName())
+                .build();
+            fileList.add(uploadedFile);
         }
 
         return fileList.toArray(new UploadedFile[0]);
diff --git 
a/core/src/main/java/org/apache/struts2/dispatcher/multipart/JakartaStreamMultiPartRequest.java
 
b/core/src/main/java/org/apache/struts2/dispatcher/multipart/JakartaStreamMultiPartRequest.java
index dec0b6841..2618d6ace 100644
--- 
a/core/src/main/java/org/apache/struts2/dispatcher/multipart/JakartaStreamMultiPartRequest.java
+++ 
b/core/src/main/java/org/apache/struts2/dispatcher/multipart/JakartaStreamMultiPartRequest.java
@@ -22,6 +22,7 @@ import org.apache.commons.fileupload.FileItemIterator;
 import org.apache.commons.fileupload.FileItemStream;
 import org.apache.commons.fileupload.FileUploadBase;
 import 
org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException;
+import org.apache.commons.fileupload.FileUploadException;
 import org.apache.commons.fileupload.servlet.ServletFileUpload;
 import org.apache.commons.fileupload.util.Streams;
 import org.apache.logging.log4j.LogManager;
@@ -110,7 +111,11 @@ public class JakartaStreamMultiPartRequest extends 
AbstractMultiPartRequest {
 
         List<UploadedFile> files = new ArrayList<>(infos.size());
         for (FileInfo fileInfo : infos) {
-            files.add(new StrutsUploadedFile(fileInfo.getFile()));
+            UploadedFile file = 
StrutsUploadedFile.Builder.create(fileInfo.getFile())
+                .withContentType(fileInfo.contentType)
+                .withOriginalName(fileInfo.originalName)
+                .build();
+            files.add(file);
         }
 
         return files.toArray(new UploadedFile[0]);
@@ -162,7 +167,7 @@ public class JakartaStreamMultiPartRequest extends 
AbstractMultiPartRequest {
      */
     public String getParameter(String name) {
         List<String> values = parameters.get(name);
-        if (values != null && values.size() > 0) {
+        if (values != null && !values.isEmpty()) {
             return values.get(0);
         }
         return null;
@@ -180,7 +185,7 @@ public class JakartaStreamMultiPartRequest extends 
AbstractMultiPartRequest {
      */
     public String[] getParameterValues(String name) {
         List<String> values = parameters.get(name);
-        if (values != null && values.size() > 0) {
+        if (values != null && !values.isEmpty()) {
             return values.toArray(new String[0]);
         }
         return null;
@@ -207,9 +212,8 @@ public class JakartaStreamMultiPartRequest extends 
AbstractMultiPartRequest {
      *
      * @param request the servlet request
      * @param saveDir location of the save dir
-     * @throws Exception
      */
-    protected void processUpload(HttpServletRequest request, String saveDir) 
throws Exception {
+    protected void processUpload(HttpServletRequest request, String saveDir) 
throws IOException, FileUploadException {
 
         // Sanity check that the request is a multi-part/form-data request.
         if (ServletFileUpload.isMultipartContent(request)) {
@@ -292,7 +296,7 @@ public class JakartaStreamMultiPartRequest extends 
AbstractMultiPartRequest {
      * Add a file skipped message notification for action messages.
      *
      * @param fileName file name
-     * @param request the servlet request
+     * @param request  the servlet request
      */
     protected void addFileSkippedError(String fileName, HttpServletRequest 
request) {
         String exceptionMessage = "Skipped file " + fileName + "; request size 
limit exceeded.";
@@ -330,11 +334,11 @@ public class JakartaStreamMultiPartRequest extends 
AbstractMultiPartRequest {
      * Processes the FileItemStream as a file field.
      *
      * @param itemStream file item stream
-     * @param location location
+     * @param location   location
      */
     protected void processFileItemStreamAsFileField(FileItemStream itemStream, 
String location) {
         // Skip file uploads that don't have a file name - meaning that no 
file was selected.
-        if (itemStream.getName() == null || 
itemStream.getName().trim().length() < 1) {
+        if (itemStream.getName() == null || 
itemStream.getName().trim().isEmpty()) {
             LOG.debug("No file has been uploaded for the field: {}", 
itemStream.getFieldName());
             return;
         }
@@ -368,8 +372,8 @@ public class JakartaStreamMultiPartRequest extends 
AbstractMultiPartRequest {
      */
     protected File createTemporaryFile(String fileName, String location) 
throws IOException {
         String name = fileName
-                .substring(fileName.lastIndexOf('/') + 1)
-                .substring(fileName.lastIndexOf('\\') + 1);
+            .substring(fileName.lastIndexOf('/') + 1)
+            .substring(fileName.lastIndexOf('\\') + 1);
 
         String prefix = name;
         String suffix = "";
@@ -392,14 +396,14 @@ public class JakartaStreamMultiPartRequest extends 
AbstractMultiPartRequest {
      * Streams the file upload stream to the specified file.
      *
      * @param itemStream file item stream
-     * @param file the file
+     * @param file       the file
      * @return true if stream was successfully
      * @throws IOException in case of IO errors
      */
     protected boolean streamFileToDisk(FileItemStream itemStream, File file) 
throws IOException {
         boolean result;
         try (InputStream input = itemStream.openStream();
-                OutputStream output = new 
BufferedOutputStream(Files.newOutputStream(file.toPath()), bufferSize)) {
+             OutputStream output = new 
BufferedOutputStream(Files.newOutputStream(file.toPath()), bufferSize)) {
             byte[] buffer = new byte[bufferSize];
             LOG.debug("Streaming file using buffer size {}.", bufferSize);
             for (int length; ((length = input.read(buffer)) > 0); ) {
@@ -416,7 +420,7 @@ public class JakartaStreamMultiPartRequest extends 
AbstractMultiPartRequest {
      * invocation process.
      *
      * @param itemStream file item stream
-     * @param file the file
+     * @param file       the file
      */
     protected void createFileInfoFromItemStream(FileItemStream itemStream, 
File file) {
         // gather attributes from file upload stream.
@@ -440,7 +444,7 @@ public class JakartaStreamMultiPartRequest extends 
AbstractMultiPartRequest {
      *
      * @since 7.0.0
      */
-     public static class FileInfo implements Serializable {
+    public static class FileInfo implements Serializable {
 
         private static final long serialVersionUID = 1083158552766906037L;
 
@@ -451,8 +455,8 @@ public class JakartaStreamMultiPartRequest extends 
AbstractMultiPartRequest {
         /**
          * Default constructor.
          *
-         * @param file the file
-         * @param contentType content type
+         * @param file         the file
+         * @param contentType  content type
          * @param originalName original file name
          */
         public FileInfo(File file, String contentType, String originalName) {
diff --git 
a/core/src/main/java/org/apache/struts2/dispatcher/multipart/StrutsUploadedFile.java
 
b/core/src/main/java/org/apache/struts2/dispatcher/multipart/StrutsUploadedFile.java
index e09d79aff..97cb9cefd 100644
--- 
a/core/src/main/java/org/apache/struts2/dispatcher/multipart/StrutsUploadedFile.java
+++ 
b/core/src/main/java/org/apache/struts2/dispatcher/multipart/StrutsUploadedFile.java
@@ -22,10 +22,21 @@ import java.io.File;
 
 public class StrutsUploadedFile implements UploadedFile {
 
-    private File file;
+    private final File file;
+    private final String contentType;
+    private final String originalName;
 
+    @Deprecated
     public StrutsUploadedFile(File file) {
         this.file = file;
+        this.contentType = null;
+        this.originalName = null;
+    }
+
+    private StrutsUploadedFile(File file, String contentType, String 
originalName) {
+        this.file = file;
+        this.contentType = contentType;
+        this.originalName = originalName;
     }
 
     @Override
@@ -57,4 +68,42 @@ public class StrutsUploadedFile implements UploadedFile {
     public File getContent() {
         return file;
     }
+
+    @Override
+    public String getContentType() {
+        return this.contentType;
+    }
+
+    @Override
+    public String getOriginalName() {
+        return originalName;
+    }
+
+    public static class Builder {
+        private final File file;
+        private String contentType;
+        private String originalName;
+
+        private Builder(File file) {
+            this.file = file;
+        }
+
+        public static Builder create(File file) {
+            return new Builder(file);
+        }
+
+        public Builder withContentType(String contentType) {
+            this.contentType = contentType;
+            return this;
+        }
+
+        public Builder withOriginalName(String originalName) {
+            this.originalName = originalName;
+            return this;
+        }
+
+        public UploadedFile build() {
+            return new StrutsUploadedFile(this.file, this.contentType, 
this.originalName);
+        }
+    }
 }
diff --git 
a/core/src/main/java/org/apache/struts2/dispatcher/multipart/UploadedFile.java 
b/core/src/main/java/org/apache/struts2/dispatcher/multipart/UploadedFile.java
index d4edb5221..896c681be 100644
--- 
a/core/src/main/java/org/apache/struts2/dispatcher/multipart/UploadedFile.java
+++ 
b/core/src/main/java/org/apache/struts2/dispatcher/multipart/UploadedFile.java
@@ -27,6 +27,8 @@ public interface UploadedFile {
 
     String getName();
 
+    String getOriginalName();
+
     boolean isFile();
 
     boolean delete();
@@ -35,4 +37,6 @@ public interface UploadedFile {
 
     Object getContent();
 
+    String getContentType();
+
 }
diff --git 
a/core/src/main/java/org/apache/struts2/interceptor/AbstractFileUploadInterceptor.java
 
b/core/src/main/java/org/apache/struts2/interceptor/AbstractFileUploadInterceptor.java
new file mode 100644
index 000000000..3857a7b4e
--- /dev/null
+++ 
b/core/src/main/java/org/apache/struts2/interceptor/AbstractFileUploadInterceptor.java
@@ -0,0 +1,247 @@
+/*
+ * 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 org.apache.struts2.interceptor;
+
+import com.opensymphony.xwork2.LocaleProvider;
+import com.opensymphony.xwork2.LocaleProviderFactory;
+import com.opensymphony.xwork2.TextProvider;
+import com.opensymphony.xwork2.TextProviderFactory;
+import com.opensymphony.xwork2.inject.Container;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
+import com.opensymphony.xwork2.interceptor.ValidationAware;
+import com.opensymphony.xwork2.util.TextParseUtil;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.struts2.dispatcher.LocalizedMessage;
+import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper;
+import org.apache.struts2.dispatcher.multipart.UploadedFile;
+import org.apache.struts2.util.ContentTypeMatcher;
+
+import java.text.NumberFormat;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Set;
+
+public abstract class AbstractFileUploadInterceptor extends 
AbstractInterceptor {
+
+    private static final Logger LOG = 
LogManager.getLogger(AbstractFileUploadInterceptor.class);
+
+    protected Long maximumSize;
+    protected Set<String> allowedTypesSet = Collections.emptySet();
+    protected Set<String> allowedExtensionsSet = Collections.emptySet();
+
+    private ContentTypeMatcher<Object> matcher;
+    private Container container;
+
+    @Inject
+    public void setMatcher(ContentTypeMatcher<Object> matcher) {
+        this.matcher = matcher;
+    }
+
+    @Inject
+    public void setContainer(Container container) {
+        this.container = container;
+    }
+
+    /**
+     * 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;
+    }
+
+    /**
+     * 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 filename    - name of the file.
+     * @param contentType - contentType of the file.
+     * @param inputName   - inputName of the file.
+     * @return true if the proposed file is acceptable by contentType and size.
+     */
+    protected boolean acceptFile(Object action, UploadedFile file, String 
filename, String contentType, String inputName) {
+        boolean fileIsAcceptable = false;
+
+        ValidationAware validation = null;
+        if (action instanceof ValidationAware) {
+            validation = (ValidationAware) action;
+        }
+
+        // If it's null the upload failed
+        if (file == null) {
+            String errMsg = getTextMessage(action, 
"struts.messages.error.uploading", new String[]{inputName});
+            if (validation != null) {
+                validation.addFieldError(inputName, errMsg);
+            }
+            LOG.warn(errMsg);
+        } else if (file.getContent() == null) {
+            String errMsg = getTextMessage(action, 
"struts.messages.error.uploading", new String[]{filename});
+            if (validation != null) {
+                validation.addFieldError(inputName, errMsg);
+            }
+            LOG.warn(errMsg);
+        } else if (maximumSize != null && maximumSize < file.length()) {
+            String errMsg = getTextMessage(action, 
"struts.messages.error.file.too.large", new String[]{inputName, filename, 
file.getName(), "" + file.length(), getMaximumSizeStr(action)});
+            if (validation != null) {
+                validation.addFieldError(inputName, errMsg);
+            }
+            LOG.warn(errMsg);
+        } else if ((!allowedTypesSet.isEmpty()) && 
(!containsItem(allowedTypesSet, contentType))) {
+            String errMsg = getTextMessage(action, 
"struts.messages.error.content.type.not.allowed", new String[]{inputName, 
filename, file.getName(), contentType});
+            if (validation != null) {
+                validation.addFieldError(inputName, errMsg);
+            }
+            LOG.warn(errMsg);
+        } else if ((!allowedExtensionsSet.isEmpty()) && 
(!hasAllowedExtension(allowedExtensionsSet, filename))) {
+            String errMsg = getTextMessage(action, 
"struts.messages.error.file.extension.not.allowed", new String[]{inputName, 
filename, file.getName(), contentType});
+            if (validation != null) {
+                validation.addFieldError(inputName, errMsg);
+            }
+            LOG.warn(errMsg);
+        } else {
+            fileIsAcceptable = true;
+        }
+
+        return fileIsAcceptable;
+    }
+
+    private String getMaximumSizeStr(Object action) {
+        return 
NumberFormat.getNumberInstance(getLocaleProvider(action).getLocale()).format(maximumSize);
+    }
+
+    /**
+     * @param extensionCollection - Collection of extensions (all lowercase).
+     * @param filename            - filename to check.
+     * @return true if the filename has an allowed extension, false otherwise.
+     */
+    private 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 boolean containsItem(Collection<String> itemCollection, String 
item) {
+        for (String pattern : itemCollection)
+            if (matchesWildcard(pattern, item))
+                return true;
+        return false;
+    }
+
+    private boolean matchesWildcard(String pattern, String text) {
+        Object o = matcher.compilePattern(pattern);
+        return matcher.match(new HashMap<>(), text, o);
+    }
+
+    protected boolean isNonEmpty(Object[] objArray) {
+        boolean result = false;
+        for (Object o : objArray) {
+            if (o != null) {
+                result = true;
+                break;
+            }
+        }
+        return result;
+    }
+
+    protected String getTextMessage(String messageKey, String[] args) {
+        return getTextMessage(this, messageKey, args);
+    }
+
+    protected String getTextMessage(Object action, String messageKey, String[] 
args) {
+        if (action instanceof TextProvider) {
+            return ((TextProvider) action).getText(messageKey, args);
+        }
+        return getTextProvider(action).getText(messageKey, args);
+    }
+
+    protected TextProvider getTextProvider(Object action) {
+        TextProviderFactory tpf = 
container.getInstance(TextProviderFactory.class);
+        return tpf.createInstance(action.getClass());
+    }
+
+    private LocaleProvider getLocaleProvider(Object action) {
+        LocaleProvider localeProvider;
+        if (action instanceof LocaleProvider) {
+            localeProvider = (LocaleProvider) action;
+        } else {
+            LocaleProviderFactory localeProviderFactory = 
container.getInstance(LocaleProviderFactory.class);
+            localeProvider = localeProviderFactory.createLocaleProvider();
+        }
+        return localeProvider;
+    }
+
+    protected void applyValidation(Object action, MultiPartRequestWrapper 
multiWrapper) {
+        ValidationAware validation = null;
+        if (action instanceof ValidationAware) {
+            validation = (ValidationAware) action;
+        }
+
+        if (multiWrapper.hasErrors() && validation != null) {
+            TextProvider textProvider = getTextProvider(action);
+            for (LocalizedMessage error : multiWrapper.getErrors()) {
+                String errorMessage;
+                if (textProvider.hasKey(error.getTextKey())) {
+                    errorMessage = textProvider.getText(error.getTextKey(), 
Arrays.asList(error.getArgs()));
+                } else {
+                    errorMessage = 
textProvider.getText("struts.messages.error.uploading", 
error.getDefaultMessage());
+                }
+                validation.addActionError(errorMessage);
+            }
+        }
+    }
+
+}
diff --git 
a/core/src/main/java/org/apache/struts2/interceptor/ActionFileUploadInterceptor.java
 
b/core/src/main/java/org/apache/struts2/interceptor/ActionFileUploadInterceptor.java
new file mode 100644
index 000000000..4cff8617e
--- /dev/null
+++ 
b/core/src/main/java/org/apache/struts2/interceptor/ActionFileUploadInterceptor.java
@@ -0,0 +1,248 @@
+/*
+ * 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 org.apache.struts2.interceptor;
+
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.ActionProxy;
+import com.opensymphony.xwork2.interceptor.ValidationAware;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.struts2.action.UploadedFilesAware;
+import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper;
+import org.apache.struts2.dispatcher.multipart.UploadedFile;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+/**
+ * <!-- START SNIPPET: description -->
+ * <p>
+ * Interceptor that is based off of {@link MultiPartRequestWrapper}, which is 
automatically applied for any request that
+ * includes a file. It adds the following parameters, where [File Name] is the 
name given to the file uploaded by the
+ * HTML form:
+ * </p>
+ * <ul>
+ *
+ * <li>[File Name] : File - the actual File</li>
+ *
+ * <li>[File Name]ContentType : String - the content type of the file</li>
+ *
+ * <li>[File Name]FileName : String - the actual name of the file uploaded 
(not the HTML name)</li>
+ *
+ * </ul>
+ *
+ * <p>You can get access to these files by merely providing setters in your 
action that correspond to any of the three
+ * patterns above, such as setDocument(File document), 
setDocumentContentType(String contentType), etc.
+ * <br>See the example code section.
+ * </p>
+ *
+ * <p> This interceptor will add several field errors, assuming that the 
action implements {@link ValidationAware}.
+ * These error messages are based on several i18n values stored in 
struts-messages.properties, a default i18n file
+ * processed for all i18n requests. You can override the text of these 
messages by providing text for the following
+ * keys:
+ * </p>
+ *
+ * <ul>
+ *
+ * <li>struts.messages.error.uploading - a general error that occurs when the 
file could not be uploaded</li>
+ *
+ * <li>struts.messages.error.file.too.large - occurs when the uploaded file is 
too large</li>
+ *
+ * <li>struts.messages.error.content.type.not.allowed - occurs when the 
uploaded file does not match the expected
+ * content types specified</li>
+ *
+ * <li>struts.messages.error.file.extension.not.allowed - occurs when the 
uploaded file does not match the expected
+ * file extensions specified</li>
+ *
+ * </ul>
+ * <p>
+ * <!-- END SNIPPET: description -->
+ *
+ * <p><u>Interceptor parameters:</u></p>
+ * <p>
+ * <!-- START SNIPPET: parameters -->
+ *
+ * <ul>
+ *
+ * <li>maximumSize (optional) - the maximum size (in bytes) that the 
interceptor will allow a file reference to be set
+ * on the action. Note, this is <b>not</b> related to the various properties 
found in struts.properties.
+ * Default to approximately 2MB.</li>
+ *
+ * <li>allowedTypes (optional) - a comma separated list of content types (ie: 
text/html) that the interceptor will allow
+ * a file reference to be set on the action. If none is specified allow all 
types to be uploaded.</li>
+ *
+ * <li>allowedExtensions (optional) - a comma separated list of file 
extensions (ie: .html) that the interceptor will allow
+ * a file reference to be set on the action. If none is specified allow all 
extensions to be uploaded.</li>
+ * </ul>
+ * <p>
+ * <p>
+ * <!-- END SNIPPET: parameters -->
+ *
+ * <p><u>Extending the interceptor:</u></p>
+ * <p>
+ * <p>
+ * <p>
+ * <!-- START SNIPPET: extending -->
+ * <p>
+ * You can extend this interceptor and override the acceptFile method to 
provide more control over which files
+ * are supported and which are not.
+ * </p>
+ * <!-- END SNIPPET: extending -->
+ *
+ * <p><u>Example code:</u></p>
+ *
+ * <pre>
+ * <!-- START SNIPPET: example-configuration -->
+ * &lt;action name="doUpload" class="com.example.UploadAction"&gt;
+ *     &lt;interceptor-ref name="fileUpload"/&gt;
+ *     &lt;interceptor-ref name="basicStack"/&gt;
+ *     &lt;result name="success"&gt;good_result.jsp&lt;/result&gt;
+ * &lt;/action&gt;
+ * <!-- END SNIPPET: example-configuration -->
+ * </pre>
+ * <p>
+ * <!-- START SNIPPET: multipart-note -->
+ * <p>
+ * You must set the encoding to <code>multipart/form-data</code> in the form 
where the user selects the file to upload.
+ * </p>
+ * <!-- END SNIPPET: multipart-note -->
+ *
+ * <pre>
+ * <!-- START SNIPPET: example-form -->
+ *   &lt;s:form action="doUpload" method="post" 
enctype="multipart/form-data"&gt;
+ *       &lt;s:file name="upload" label="File"/&gt;
+ *       &lt;s:submit/&gt;
+ *   &lt;/s:form&gt;
+ * <!-- END SNIPPET: example-form -->
+ * </pre>
+ * <p>
+ * And then in your action code you'll have access to the File object if you 
provide setters according to the
+ * naming convention documented in the start.
+ * </p>
+ *
+ * <pre>
+ * <!-- START SNIPPET: example-action -->
+ *    package com.example;
+ *
+ *    import java.io.File;
+ *    import com.opensymphony.xwork2.ActionSupport;
+ *
+ *    public UploadAction extends ActionSupport {
+ *       private File file;
+ *       private String contentType;
+ *       private String filename;
+ *
+ *       public void setUpload(File file) {
+ *          this.file = file;
+ *       }
+ *
+ *       public void setUploadContentType(String contentType) {
+ *          this.contentType = contentType;
+ *       }
+ *
+ *       public void setUploadFileName(String filename) {
+ *          this.filename = filename;
+ *       }
+ *
+ *       public String execute() {
+ *          //...
+ *          return SUCCESS;
+ *       }
+ *  }
+ * <!-- END SNIPPET: example-action -->
+ * </pre>
+ */
+public class ActionFileUploadInterceptor extends AbstractFileUploadInterceptor 
{
+
+    protected static final Logger LOG = 
LogManager.getLogger(ActionFileUploadInterceptor.class);
+
+    /* (non-Javadoc)
+     * @see 
com.opensymphony.xwork2.interceptor.Interceptor#intercept(com.opensymphony.xwork2.ActionInvocation)
+     */
+    public String intercept(ActionInvocation invocation) throws Exception {
+        HttpServletRequest request = 
invocation.getInvocationContext().getServletRequest();
+        if (!(request instanceof MultiPartRequestWrapper)) {
+            ActionProxy proxy = invocation.getProxy();
+            LOG.debug(getTextMessage("struts.messages.bypass.request", new 
String[]{proxy.getNamespace(), proxy.getActionName()}));
+            return invocation.invoke();
+        }
+
+        MultiPartRequestWrapper multiWrapper = (MultiPartRequestWrapper) 
request;
+
+        if (!(invocation.getAction() instanceof UploadedFilesAware)) {
+            LOG.debug("Action: {} doesn't implement: {}, ignoring file upload",
+                invocation.getProxy().getActionName(),
+                UploadedFilesAware.class.getSimpleName());
+            return invocation.invoke();
+        }
+        UploadedFilesAware action = (UploadedFilesAware) 
invocation.getAction();
+
+        applyValidation(action, multiWrapper);
+
+        // bind allowed Files
+        Enumeration<String> fileParameterNames = 
multiWrapper.getFileParameterNames();
+        while (fileParameterNames != null && 
fileParameterNames.hasMoreElements()) {
+            // get the value of this input tag
+            String inputName = 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
+                    UploadedFile[] files = multiWrapper.getFiles(inputName);
+                    if (files != null && files.length > 0) {
+                        List<UploadedFile> acceptedFiles = new 
ArrayList<>(files.length);
+
+                        for (int index = 0; index < files.length; index++) {
+                            if (acceptFile(action, files[index], 
fileName[index], contentType[index], inputName)) {
+                                acceptedFiles.add(files[index]);
+                            }
+                        }
+
+                        if (acceptedFiles.isEmpty()) {
+                            LOG.debug("No files have been uploaded/accepted");
+                        } else {
+                            LOG.debug("Passing: {} uploaded file(s) to 
action", acceptedFiles.size());
+                            action.withUploadedFiles(acceptedFiles);
+                        }
+                    }
+                } else {
+                    if (LOG.isWarnEnabled()) {
+                        LOG.warn(getTextMessage(action, 
"struts.messages.invalid.file", new String[]{inputName}));
+                    }
+                }
+            } else {
+                if (LOG.isWarnEnabled()) {
+                    LOG.warn(getTextMessage(action, 
"struts.messages.invalid.content.type", new String[]{inputName}));
+                }
+            }
+        }
+
+        // invoke action
+        return invocation.invoke();
+    }
+
+}
diff --git 
a/core/src/main/java/org/apache/struts2/interceptor/FileUploadInterceptor.java 
b/core/src/main/java/org/apache/struts2/interceptor/FileUploadInterceptor.java
index bb77ea093..4bcdec07b 100644
--- 
a/core/src/main/java/org/apache/struts2/interceptor/FileUploadInterceptor.java
+++ 
b/core/src/main/java/org/apache/struts2/interceptor/FileUploadInterceptor.java
@@ -18,23 +18,22 @@
  */
 package org.apache.struts2.interceptor;
 
-import com.opensymphony.xwork2.*;
-import com.opensymphony.xwork2.inject.Container;
-import com.opensymphony.xwork2.inject.Inject;
-import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.ActionProxy;
 import com.opensymphony.xwork2.interceptor.ValidationAware;
-import com.opensymphony.xwork2.util.TextParseUtil;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
-import org.apache.struts2.dispatcher.LocalizedMessage;
 import org.apache.struts2.dispatcher.Parameter;
 import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper;
 import org.apache.struts2.dispatcher.multipart.UploadedFile;
-import org.apache.struts2.util.ContentTypeMatcher;
 
 import javax.servlet.http.HttpServletRequest;
-import java.text.NumberFormat;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 /**
  * <!-- START SNIPPET: description -->
@@ -77,11 +76,11 @@ import java.util.*;
  * file extensions specified</li>
  *
  * </ul>
- *
+ * <p>
  * <!-- END SNIPPET: description -->
  *
  * <p><u>Interceptor parameters:</u></p>
- *
+ * <p>
  * <!-- START SNIPPET: parameters -->
  *
  * <ul>
@@ -96,14 +95,14 @@ import java.util.*;
  * <li>allowedExtensions (optional) - a comma separated list of file 
extensions (ie: .html) that the interceptor will allow
  * a file reference to be set on the action. If none is specified allow all 
extensions to be uploaded.</li>
  * </ul>
- *
- *
+ * <p>
+ * <p>
  * <!-- END SNIPPET: parameters -->
  *
  * <p><u>Extending the interceptor:</u></p>
- *
- *
- *
+ * <p>
+ * <p>
+ * <p>
  * <!-- START SNIPPET: extending -->
  * <p>
  * You can extend this interceptor and override the acceptFile method to 
provide more control over which files
@@ -122,7 +121,7 @@ import java.util.*;
  * &lt;/action&gt;
  * <!-- END SNIPPET: example-configuration -->
  * </pre>
- *
+ * <p>
  * <!-- START SNIPPET: multipart-note -->
  * <p>
  * You must set the encoding to <code>multipart/form-data</code> in the form 
where the user selects the file to upload.
@@ -174,56 +173,12 @@ import java.util.*;
  * <!-- END SNIPPET: example-action -->
  * </pre>
  */
-public class FileUploadInterceptor extends AbstractInterceptor {
+public class FileUploadInterceptor extends AbstractFileUploadInterceptor {
 
     private static final long serialVersionUID = -4764627478894962478L;
 
     protected static final Logger LOG = 
LogManager.getLogger(FileUploadInterceptor.class);
 
-    protected Long maximumSize;
-    protected Set<String> allowedTypesSet = Collections.emptySet();
-    protected Set<String> allowedExtensionsSet = Collections.emptySet();
-
-    private ContentTypeMatcher matcher;
-    private Container container;
-
-    @Inject
-    public void setMatcher(ContentTypeMatcher matcher) {
-        this.matcher = matcher;
-    }
-
-    @Inject
-    public void setContainer(Container container) {
-        this.container = container;
-    }
-
-    /**
-     * 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)
      */
@@ -242,34 +197,16 @@ public class FileUploadInterceptor extends 
AbstractInterceptor {
             return invocation.invoke();
         }
 
-        ValidationAware validation = null;
-
         Object action = invocation.getAction();
-
-        if (action instanceof ValidationAware) {
-            validation = (ValidationAware) action;
-        }
-
         MultiPartRequestWrapper multiWrapper = (MultiPartRequestWrapper) 
request;
 
-        if (multiWrapper.hasErrors() && validation != null) {
-            TextProvider textProvider = getTextProvider(action);
-            for (LocalizedMessage error : multiWrapper.getErrors()) {
-                String errorMessage;
-                if (textProvider.hasKey(error.getTextKey())) {
-                    errorMessage = textProvider.getText(error.getTextKey(), 
Arrays.asList(error.getArgs()));
-                } else {
-                    errorMessage = 
textProvider.getText("struts.messages.error.uploading", 
error.getDefaultMessage());
-                }
-                validation.addActionError(errorMessage);
-            }
-        }
+        applyValidation(action, multiWrapper);
 
         // bind allowed Files
-        Enumeration fileParameterNames = multiWrapper.getFileParameterNames();
+        Enumeration<String> fileParameterNames = 
multiWrapper.getFileParameterNames();
         while (fileParameterNames != null && 
fileParameterNames.hasMoreElements()) {
             // get the value of this input tag
-            String inputName = (String) fileParameterNames.nextElement();
+            String inputName = fileParameterNames.nextElement();
 
             // get the content type
             String[] contentType = multiWrapper.getContentTypes(inputName);
@@ -289,7 +226,7 @@ public class FileUploadInterceptor extends 
AbstractInterceptor {
                         String fileNameName = inputName + "FileName";
 
                         for (int index = 0; index < files.length; index++) {
-                            if (acceptFile(action, files[index], 
fileName[index], contentType[index], inputName, validation)) {
+                            if (acceptFile(action, files[index], 
fileName[index], contentType[index], inputName)) {
                                 acceptedFiles.add(files[index]);
                                 acceptedContentTypes.add(contentType[index]);
                                 acceptedFileNames.add(fileName[index]);
@@ -298,9 +235,9 @@ public class FileUploadInterceptor extends 
AbstractInterceptor {
 
                         if (!acceptedFiles.isEmpty()) {
                             Map<String, Parameter> newParams = new HashMap<>();
-                            newParams.put(inputName, new 
Parameter.File(inputName, acceptedFiles.toArray(new 
UploadedFile[acceptedFiles.size()])));
-                            newParams.put(contentTypeName, new 
Parameter.File(contentTypeName, acceptedContentTypes.toArray(new 
String[acceptedContentTypes.size()])));
-                            newParams.put(fileNameName, new 
Parameter.File(fileNameName, acceptedFileNames.toArray(new 
String[acceptedFileNames.size()])));
+                            newParams.put(inputName, new 
Parameter.File(inputName, acceptedFiles.toArray(new UploadedFile[0])));
+                            newParams.put(contentTypeName, new 
Parameter.File(contentTypeName, acceptedContentTypes.toArray(new String[0])));
+                            newParams.put(fileNameName, new 
Parameter.File(fileNameName, acceptedFileNames.toArray(new String[0])));
                             ac.getParameters().appendAll(newParams);
                         }
                     }
@@ -320,149 +257,4 @@ public class FileUploadInterceptor extends 
AbstractInterceptor {
         return invocation.invoke();
     }
 
-    /**
-     * 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 filename    - name of the 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.
-     * @return true if the proposed file is acceptable by contentType and size.
-     */
-    protected boolean acceptFile(Object action, UploadedFile file, String 
filename, String contentType, String inputName, ValidationAware validation) {
-        boolean fileIsAcceptable = false;
-
-        // If it's null the upload failed
-        if (file == null) {
-            String errMsg = getTextMessage(action, 
"struts.messages.error.uploading", new String[]{inputName});
-            if (validation != null) {
-                validation.addFieldError(inputName, errMsg);
-            }
-
-            if (LOG.isWarnEnabled()) {
-                LOG.warn(errMsg);
-            }
-        } else if (file.getContent() == null) {
-            String errMsg = getTextMessage(action, 
"struts.messages.error.uploading", new String[]{filename});
-            if (validation != null) {
-                validation.addFieldError(inputName, errMsg);
-            }
-            if (LOG.isWarnEnabled()) {
-                LOG.warn(errMsg);
-            }
-        } else if (maximumSize != null && maximumSize < file.length()) {
-            String errMsg = getTextMessage(action, 
"struts.messages.error.file.too.large", new String[]{inputName, filename, 
file.getName(), "" + file.length(), getMaximumSizeStr(action)});
-            if (validation != null) {
-                validation.addFieldError(inputName, errMsg);
-            }
-
-            if (LOG.isWarnEnabled()) {
-                LOG.warn(errMsg);
-            }
-        } else if ((!allowedTypesSet.isEmpty()) && 
(!containsItem(allowedTypesSet, contentType))) {
-            String errMsg = getTextMessage(action, 
"struts.messages.error.content.type.not.allowed", new String[]{inputName, 
filename, file.getName(), contentType});
-            if (validation != null) {
-                validation.addFieldError(inputName, errMsg);
-            }
-
-            if (LOG.isWarnEnabled()) {
-                LOG.warn(errMsg);
-            }
-        } else if ((!allowedExtensionsSet.isEmpty()) && 
(!hasAllowedExtension(allowedExtensionsSet, filename))) {
-            String errMsg = getTextMessage(action, 
"struts.messages.error.file.extension.not.allowed", new String[]{inputName, 
filename, file.getName(), contentType});
-            if (validation != null) {
-                validation.addFieldError(inputName, errMsg);
-            }
-
-            if (LOG.isWarnEnabled()) {
-                LOG.warn(errMsg);
-            }
-        } else {
-            fileIsAcceptable = true;
-        }
-
-        return fileIsAcceptable;
-    }
-
-    private String getMaximumSizeStr(Object action) {
-        return 
NumberFormat.getNumberInstance(getLocaleProvider(action).getLocale()).format(maximumSize);
-    }
-
-    /**
-     * @param extensionCollection - Collection of extensions (all lowercase).
-     * @param filename            - filename to check.
-     * @return true if the filename has an allowed extension, false otherwise.
-     */
-    private 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 boolean containsItem(Collection<String> itemCollection, String 
item) {
-        for (String pattern : itemCollection)
-            if (matchesWildcard(pattern, item))
-                return true;
-        return false;
-    }
-
-    private boolean matchesWildcard(String pattern, String text) {
-        Object o = matcher.compilePattern(pattern);
-        return matcher.match(new HashMap<String, String>(), text, o);
-    }
-
-    private boolean isNonEmpty(Object[] objArray) {
-        boolean result = false;
-        for (int index = 0; index < objArray.length && !result; index++) {
-            if (objArray[index] != null) {
-                result = true;
-            }
-        }
-        return result;
-    }
-
-    protected String getTextMessage(String messageKey, String[] args) {
-        return getTextMessage(this, messageKey, args);
-    }
-
-    protected String getTextMessage(Object action, String messageKey, String[] 
args) {
-        if (action instanceof TextProvider) {
-            return ((TextProvider) action).getText(messageKey, args);
-        }
-        return getTextProvider(action).getText(messageKey, args);
-    }
-
-    private TextProvider getTextProvider(Object action) {
-        TextProviderFactory tpf = 
container.getInstance(TextProviderFactory.class);
-        return tpf.createInstance(action.getClass());
-    }
-
-    private LocaleProvider getLocaleProvider(Object action) {
-        LocaleProvider localeProvider;
-        if (action instanceof LocaleProvider) {
-            localeProvider = (LocaleProvider) action;
-        } else {
-            LocaleProviderFactory localeProviderFactory = 
container.getInstance(LocaleProviderFactory.class);
-            localeProvider = localeProviderFactory.createLocaleProvider();
-        }
-        return localeProvider;
-    }
-
 }
diff --git a/core/src/main/resources/struts-default.xml 
b/core/src/main/resources/struts-default.xml
index 0fdcc2b37..326477bc4 100644
--- a/core/src/main/resources/struts-default.xml
+++ b/core/src/main/resources/struts-default.xml
@@ -58,6 +58,7 @@
             <interceptor name="execAndWait" 
class="org.apache.struts2.interceptor.ExecuteAndWaitInterceptor"/>
             <interceptor name="exception" 
class="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"/>
             <interceptor name="fileUpload" 
class="org.apache.struts2.interceptor.FileUploadInterceptor"/>
+            <interceptor name="actionFileUpload" 
class="org.apache.struts2.interceptor.ActionFileUploadInterceptor"/>
             <interceptor name="i18n" 
class="org.apache.struts2.interceptor.I18nInterceptor"/>
             <interceptor name="logger" 
class="com.opensymphony.xwork2.interceptor.LoggingInterceptor"/>
             <interceptor name="modelDriven" 
class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>
@@ -121,6 +122,12 @@
                 <interceptor-ref name="basicStack"/>
             </interceptor-stack>
 
+            <!-- Action based file upload stack -->
+            <interceptor-stack name="actionFileUploadStack">
+                <interceptor-ref name="actionFileUpload"/>
+                <interceptor-ref name="basicStack"/>
+            </interceptor-stack>
+
             <!-- Sample model-driven stack  -->
             <interceptor-stack name="modelDrivenStack">
                 <interceptor-ref name="modelDriven"/>
@@ -165,6 +172,7 @@
                 <interceptor-ref name="chain"/>
                 <interceptor-ref name="modelDriven"/>
                 <interceptor-ref name="fileUpload"/>
+                <interceptor-ref name="actionFileUpload"/>
                 <interceptor-ref name="staticParams"/>
                 <interceptor-ref name="actionMappingParams"/>
                 <interceptor-ref name="params"/>
@@ -203,6 +211,7 @@
                 <interceptor-ref name="scopedModelDriven"/>
                 <interceptor-ref name="modelDriven"/>
                 <interceptor-ref name="fileUpload"/>
+                <interceptor-ref name="actionFileUpload"/>
                 <interceptor-ref name="checkbox"/>
                 <interceptor-ref name="datetime"/>
                 <interceptor-ref name="multiselect"/>
diff --git 
a/core/src/test/java/org/apache/struts2/conversion/UploadedFileConverterTest.java
 
b/core/src/test/java/org/apache/struts2/conversion/UploadedFileConverterTest.java
index b88d2b36b..3832a514f 100644
--- 
a/core/src/test/java/org/apache/struts2/conversion/UploadedFileConverterTest.java
+++ 
b/core/src/test/java/org/apache/struts2/conversion/UploadedFileConverterTest.java
@@ -34,10 +34,12 @@ import static org.assertj.core.api.Assertions.assertThat;
 public class UploadedFileConverterTest {
 
     private Map<String, Object> context;
-    private Class target;
+    private Class<?> target;
     private Member member;
     private String propertyName;
     private File tempFile;
+    private String contentType;
+    private String originalName;
 
     @Before
     public void setUp() throws Exception {
@@ -46,6 +48,8 @@ public class UploadedFileConverterTest {
         member = File.class.getMethod("length");
         propertyName = "ignore";
         tempFile = File.createTempFile("struts", "test");
+        contentType = "text/plain";
+        originalName = tempFile.getName();
     }
 
     @After
@@ -54,10 +58,10 @@ public class UploadedFileConverterTest {
     }
 
     @Test
-    public void convertUploadedFileToFile() throws Exception {
+    public void convertUploadedFileToFile() {
         // given
         UploadedFileConverter ufc = new UploadedFileConverter();
-        UploadedFile uploadedFile = new StrutsUploadedFile(tempFile);
+        UploadedFile uploadedFile = 
StrutsUploadedFile.Builder.create(tempFile).withContentType(this.contentType).withOriginalName(this.originalName).build();
 
         // when
         Object result = ufc.convertValue(context, target, member, 
propertyName, uploadedFile, File.class);
@@ -70,10 +74,15 @@ public class UploadedFileConverterTest {
     }
 
     @Test
-    public void convertUploadedFileArrayToFile() throws Exception {
+    public void convertUploadedFileArrayToFile() {
         // given
         UploadedFileConverter ufc = new UploadedFileConverter();
-        UploadedFile[] uploadedFile = new UploadedFile[] { new 
StrutsUploadedFile(tempFile) };
+        UploadedFile[] uploadedFile = new UploadedFile[]{
+            StrutsUploadedFile.Builder.create(tempFile)
+                .withContentType(this.contentType)
+                .withOriginalName(this.originalName)
+                .build()
+        };
 
         // when
         Object result = ufc.convertValue(context, target, member, 
propertyName, uploadedFile, File.class);
diff --git 
a/core/src/test/java/org/apache/struts2/interceptor/FileUploadInterceptorTest.java
 
b/core/src/test/java/org/apache/struts2/interceptor/ActionFileUploadInterceptorTest.java
similarity index 57%
copy from 
core/src/test/java/org/apache/struts2/interceptor/FileUploadInterceptorTest.java
copy to 
core/src/test/java/org/apache/struts2/interceptor/ActionFileUploadInterceptorTest.java
index b9bab88d4..415d4ca67 100644
--- 
a/core/src/test/java/org/apache/struts2/interceptor/FileUploadInterceptorTest.java
+++ 
b/core/src/test/java/org/apache/struts2/interceptor/ActionFileUploadInterceptorTest.java
@@ -23,12 +23,12 @@ import com.opensymphony.xwork2.ActionSupport;
 import com.opensymphony.xwork2.DefaultLocaleProvider;
 import com.opensymphony.xwork2.ValidationAwareSupport;
 import com.opensymphony.xwork2.mock.MockActionInvocation;
+import com.opensymphony.xwork2.mock.MockActionProxy;
 import com.opensymphony.xwork2.util.ClassLoaderUtil;
 import org.apache.commons.fileupload.servlet.ServletFileUpload;
 import org.apache.struts2.ServletActionContext;
 import org.apache.struts2.StrutsInternalTestCase;
-import org.apache.struts2.TestAction;
-import org.apache.struts2.dispatcher.HttpParameters;
+import org.apache.struts2.action.UploadedFilesAware;
 import org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest;
 import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper;
 import org.apache.struts2.dispatcher.multipart.StrutsUploadedFile;
@@ -37,21 +37,17 @@ import org.springframework.mock.web.MockHttpServletRequest;
 
 import javax.servlet.http.HttpServletRequest;
 import java.io.File;
-import java.io.IOException;
 import java.net.URI;
 import java.net.URL;
 import java.nio.charset.StandardCharsets;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
-import java.util.Map;
-
 
 /**
- * Test case for FileUploadInterceptor.
+ * Test case for {@link ActionFileUploadInterceptor}.
  */
-public class FileUploadInterceptorTest extends StrutsInternalTestCase {
+public class ActionFileUploadInterceptorTest extends StrutsInternalTestCase {
 
     public static final UploadedFile EMPTY_FILE = new UploadedFile() {
         @Override
@@ -83,28 +79,42 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
         public byte[] getContent() {
             return new byte[0];
         }
+
+        @Override
+        public String getOriginalName() {
+            return null;
+        }
+
+        @Override
+        public String getContentType() {
+            return null;
+        }
     };
 
-    private FileUploadInterceptor interceptor;
+    private ActionFileUploadInterceptor interceptor;
     private File tempDir;
-    private TestAction action;
 
-    public void testAcceptFileWithEmptyAllowedTypesAndExtensions() throws 
Exception {
+    private final String htmlContent = "<html><head></head><body>html 
content</body></html>";
+    private final String plainContent = "plain content";
+    private final String boundary = "simple boundary";
+    private final String endline = "\r\n";
+
+    public void testAcceptFileWithEmptyAllowedTypesAndExtensions() {
         // when allowed type is empty
         ValidationAwareSupport validation = new ValidationAwareSupport();
-        boolean ok = interceptor.acceptFile(action, EMPTY_FILE, "filename", 
"text/plain", "inputName", validation);
+        boolean ok = interceptor.acceptFile(validation, EMPTY_FILE, 
"filename", "text/plain", "inputName");
 
         assertTrue(ok);
         assertTrue(validation.getFieldErrors().isEmpty());
         assertFalse(validation.hasErrors());
     }
 
-    public void testAcceptFileWithoutEmptyTypes() throws Exception {
+    public void testAcceptFileWithoutEmptyTypes() {
         interceptor.setAllowedTypes("text/plain");
 
         // when file is of allowed types
         ValidationAwareSupport validation = new ValidationAwareSupport();
-        boolean ok = interceptor.acceptFile(action, EMPTY_FILE, 
"filename.txt", "text/plain", "inputName", validation);
+        boolean ok = interceptor.acceptFile(validation, EMPTY_FILE, 
"filename.txt", "text/plain", "inputName");
 
         assertTrue(ok);
         assertTrue(validation.getFieldErrors().isEmpty());
@@ -112,7 +122,7 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
 
         // when file is not of allowed types
         validation = new ValidationAwareSupport();
-        boolean notOk = interceptor.acceptFile(action, EMPTY_FILE, 
"filename.html", "text/html", "inputName", validation);
+        boolean notOk = interceptor.acceptFile(validation, EMPTY_FILE, 
"filename.html", "text/html", "inputName");
 
         assertFalse(notOk);
         assertFalse(validation.getFieldErrors().isEmpty());
@@ -120,11 +130,11 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
     }
 
 
-    public void testAcceptFileWithWildcardContent() throws Exception {
+    public void testAcceptFileWithWildcardContent() {
         interceptor.setAllowedTypes("text/*");
 
         ValidationAwareSupport validation = new ValidationAwareSupport();
-        boolean ok = interceptor.acceptFile(action, EMPTY_FILE, 
"filename.txt", "text/plain", "inputName", validation);
+        boolean ok = interceptor.acceptFile(validation, EMPTY_FILE, 
"filename.txt", "text/plain", "inputName");
 
         assertTrue(ok);
         assertTrue(validation.getFieldErrors().isEmpty());
@@ -132,19 +142,19 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
 
         interceptor.setAllowedTypes("text/h*");
         validation = new ValidationAwareSupport();
-        boolean notOk = interceptor.acceptFile(action, EMPTY_FILE, 
"filename.html", "text/plain", "inputName", validation);
+        boolean notOk = interceptor.acceptFile(validation, EMPTY_FILE, 
"filename.html", "text/plain", "inputName");
 
         assertFalse(notOk);
         assertFalse(validation.getFieldErrors().isEmpty());
         assertTrue(validation.hasErrors());
     }
 
-    public void testAcceptFileWithoutEmptyExtensions() throws Exception {
+    public void testAcceptFileWithoutEmptyExtensions() {
         interceptor.setAllowedExtensions(".txt");
 
         // when file is of allowed extensions
         ValidationAwareSupport validation = new ValidationAwareSupport();
-        boolean ok = interceptor.acceptFile(action, EMPTY_FILE, 
"filename.txt", "text/plain", "inputName", validation);
+        boolean ok = interceptor.acceptFile(validation, EMPTY_FILE, 
"filename.txt", "text/plain", "inputName");
 
         assertTrue(ok);
         assertTrue(validation.getFieldErrors().isEmpty());
@@ -152,7 +162,7 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
 
         // when file is not of allowed extensions
         validation = new ValidationAwareSupport();
-        boolean notOk = interceptor.acceptFile(action, EMPTY_FILE, 
"filename.html", "text/html", "inputName", validation);
+        boolean notOk = interceptor.acceptFile(validation, EMPTY_FILE, 
"filename.html", "text/html", "inputName");
 
         assertFalse(notOk);
         assertFalse(validation.getFieldErrors().isEmpty());
@@ -161,49 +171,52 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
         //test with multiple extensions
         interceptor.setAllowedExtensions(".txt,.lol");
         validation = new ValidationAwareSupport();
-        ok = interceptor.acceptFile(action, EMPTY_FILE, "filename.lol", 
"text/plain", "inputName", validation);
+        ok = interceptor.acceptFile(validation, EMPTY_FILE, "filename.lol", 
"text/plain", "inputName");
 
         assertTrue(ok);
         assertTrue(validation.getFieldErrors().isEmpty());
         assertFalse(validation.hasErrors());
     }
 
-    public void testAcceptFileWithNoFile() throws Exception {
-        FileUploadInterceptor interceptor = new FileUploadInterceptor();
+    public void testAcceptFileWithNoFile() {
+        ActionFileUploadInterceptor interceptor = new 
ActionFileUploadInterceptor();
+        interceptor.setContainer(container);
+
         interceptor.setAllowedTypes("text/plain");
 
         // when file is not of allowed types
         ValidationAwareSupport validation = new ValidationAwareSupport();
-        boolean notOk = interceptor.acceptFile(action, null, "filename.html", 
"text/html", "inputName", validation);
+        boolean notOk = interceptor.acceptFile(validation, null, 
"filename.html", "text/html", "inputName");
 
         assertFalse(notOk);
         assertFalse(validation.getFieldErrors().isEmpty());
         assertTrue(validation.hasErrors());
-        List errors = (List) validation.getFieldErrors().get("inputName");
+        List<String> errors = validation.getFieldErrors().get("inputName");
         assertEquals(1, errors.size());
-        String msg = (String) errors.get(0);
+        String msg = errors.get(0);
         assertTrue(msg.startsWith("Error uploading:"));
         assertTrue(msg.indexOf("inputName") > 0);
     }
 
     public void testAcceptFileWithMaxSize() throws Exception {
         interceptor.setAllowedTypes("text/plain");
-        interceptor.setMaximumSize(new Long(10));
+        interceptor.setMaximumSize(10L);
 
         // when file is not of allowed types
         ValidationAwareSupport validation = new ValidationAwareSupport();
 
-        URL url = ClassLoaderUtil.getResource("log4j2.xml", 
FileUploadInterceptorTest.class);
+        URL url = ClassLoaderUtil.getResource("log4j2.xml", 
ActionFileUploadInterceptorTest.class);
         File file = new File(new URI(url.toString()));
         assertTrue("log4j2.xml should be in src/test folder", file.exists());
-        boolean notOk = interceptor.acceptFile(action, new 
StrutsUploadedFile(file), "filename", "text/html", "inputName", validation);
+        UploadedFile uploadedFile = 
StrutsUploadedFile.Builder.create(file).withContentType("text/html").withOriginalName("filename").build();
+        boolean notOk = interceptor.acceptFile(validation, uploadedFile, 
"filename", "text/html", "inputName");
 
         assertFalse(notOk);
         assertFalse(validation.getFieldErrors().isEmpty());
         assertTrue(validation.hasErrors());
         List<String> errors = validation.getFieldErrors().get("inputName");
         assertEquals(1, errors.size());
-        String msg = (String) errors.get(0);
+        String msg = errors.get(0);
         // the error message should contain at least this test
         assertTrue(msg.startsWith("The file is too large to be uploaded"));
         assertTrue(msg.indexOf("inputName") > 0);
@@ -211,11 +224,15 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
     }
 
     public void testNoMultipartRequest() throws Exception {
-        MyFileupAction action = new MyFileupAction();
+        MyFileUploadAction action = new MyFileUploadAction();
 
         MockActionInvocation mai = new MockActionInvocation();
         mai.setAction(action);
         mai.setResultCode("NoMultipart");
+        MockActionProxy proxy = new MockActionProxy();
+        proxy.setNamespace("/test");
+        proxy.setActionName("myFileUpload");
+        mai.setProxy(proxy);
         mai.setInvocationContext(ActionContext.getContext());
 
         // if no multipart request it will bypass and execute it
@@ -228,13 +245,12 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
         req.setContentType("multipart/form-data"); // not a multipart 
contentype
         req.setMethod("post");
 
-        MyFileupAction action = container.inject(MyFileupAction.class);
+        MyFileUploadAction action = container.inject(MyFileUploadAction.class);
         MockActionInvocation mai = new MockActionInvocation();
         mai.setAction(action);
         mai.setResultCode("success");
         mai.setInvocationContext(ActionContext.getContext());
 
-        
ActionContext.getContext().withParameters(HttpParameters.create().build());
         ActionContext.getContext().put(ServletActionContext.HTTP_REQUEST, 
createMultipartRequestMaxSize(req, 2000));
 
         interceptor.intercept(mai);
@@ -250,13 +266,12 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
         req.addHeader("Content-type", "multipart/form-data");
         req.setContent(null); // there is no content
 
-        MyFileupAction action = container.inject(MyFileupAction.class);
+        MyFileUploadAction action = container.inject(MyFileUploadAction.class);
         MockActionInvocation mai = new MockActionInvocation();
         mai.setAction(action);
         mai.setResultCode("success");
         mai.setInvocationContext(ActionContext.getContext());
 
-        
ActionContext.getContext().withParameters(HttpParameters.create().build());
         ActionContext.getContext().put(ServletActionContext.HTTP_REQUEST, 
createMultipartRequestMaxSize(req, 2000));
 
         interceptor.intercept(mai);
@@ -272,143 +287,106 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
 
         // inspired by the unit tests for jakarta commons fileupload
         String content = ("-----1234\r\n" +
-                "Content-Disposition: form-data; name=\"file\"; 
filename=\"deleteme.txt\"\r\n" +
-                "Content-Type: text/html\r\n" +
-                "\r\n" +
-                "Unit test of FileUploadInterceptor" +
-                "\r\n" +
-                "-----1234--\r\n");
-        req.setContent(content.getBytes("US-ASCII"));
+            "Content-Disposition: form-data; name=\"file\"; 
filename=\"deleteme.txt\"\r\n" +
+            "Content-Type: text/html\r\n" +
+            "\r\n" +
+            "Unit test of ActionFileUploadInterceptor" +
+            "\r\n" +
+            "-----1234--\r\n");
+        req.setContent(content.getBytes(StandardCharsets.US_ASCII));
 
-        MyFileupAction action = new MyFileupAction();
+        MyFileUploadAction action = new MyFileUploadAction();
 
         MockActionInvocation mai = new MockActionInvocation();
         mai.setAction(action);
         mai.setResultCode("success");
         mai.setInvocationContext(ActionContext.getContext());
-        Map<String, Object> param = new HashMap<>();
-        
ActionContext.getContext().withParameters(HttpParameters.create(param).build());
         ActionContext.getContext().put(ServletActionContext.HTTP_REQUEST, 
createMultipartRequestMaxSize(req, 2000));
 
         interceptor.intercept(mai);
 
-        assertTrue(!action.hasErrors());
+        assertFalse(action.hasErrors());
 
-        HttpParameters parameters = mai.getInvocationContext().getParameters();
-        assertTrue(parameters.keySet().size() == 3);
-        UploadedFile[] files = (UploadedFile[]) 
parameters.get("file").getObject();
-        String[] fileContentTypes = 
parameters.get("fileContentType").getMultipleValues();
-        String[] fileRealFilenames = 
parameters.get("fileFileName").getMultipleValues();
+        List<UploadedFile> files = action.getUploadFiles();
 
         assertNotNull(files);
-        assertNotNull(fileContentTypes);
-        assertNotNull(fileRealFilenames);
-        assertTrue(files.length == 1);
-        assertTrue(fileContentTypes.length == 1);
-        assertTrue(fileRealFilenames.length == 1);
-        assertEquals("text/html", fileContentTypes[0]);
-        assertNotNull("deleteme.txt", fileRealFilenames[0]);
+        assertEquals(1, files.size());
+        assertEquals("text/html", files.get(0).getContentType());
+        assertNotNull("deleteme.txt", files.get(0).getOriginalName());
     }
 
     /**
      * tests whether with multiple files sent with the same name, the ones 
with forbiddenTypes (see
-     * FileUploadInterceptor.setAllowedTypes(...) ) are sorted out.
-     *
-     * @throws Exception
+     * ActionFileUploadInterceptor.setAllowedTypes(...) ) are sorted out.
      */
     public void testMultipleAccept() throws Exception {
-        final String htmlContent = "<html><head></head><body>html 
content</body></html>";
-        final String plainContent = "plain content";
-        final String bondary = "simple boundary";
-        final String endline = "\r\n";
-
         MockHttpServletRequest req = new MockHttpServletRequest();
         req.setCharacterEncoding(StandardCharsets.UTF_8.name());
         req.setMethod("POST");
-        req.addHeader("Content-type", "multipart/form-data; boundary=" + 
bondary);
-        StringBuilder content = new StringBuilder(128);
-        content.append(encodeTextFile(bondary, endline, "file", "test.html", 
"text/plain", plainContent));
-        content.append(encodeTextFile(bondary, endline, "file", "test1.html", 
"text/html", htmlContent));
-        content.append(encodeTextFile(bondary, endline, "file", "test2.html", 
"text/html", htmlContent));
-        content.append(endline);
-        content.append(endline);
-        content.append(endline);
-        content.append("--");
-        content.append(bondary);
-        content.append("--");
-        content.append(endline);
-        req.setContent(content.toString().getBytes());
+        req.addHeader("Content-type", "multipart/form-data; boundary=" + 
boundary);
+        String content = encodeTextFile("test.html", "text/plain", 
plainContent) +
+            encodeTextFile("test1.html", "text/html", htmlContent) +
+            encodeTextFile("test2.html", "text/html", htmlContent) +
+            endline +
+            endline +
+            endline +
+            "--" +
+            boundary +
+            "--" +
+            endline;
+        req.setContent(content.getBytes());
 
         assertTrue(ServletFileUpload.isMultipartContent(req));
 
-        MyFileupAction action = new MyFileupAction();
+        MyFileUploadAction action = new MyFileUploadAction();
         container.inject(action);
         MockActionInvocation mai = new MockActionInvocation();
         mai.setAction(action);
         mai.setResultCode("success");
         mai.setInvocationContext(ActionContext.getContext());
-        Map<String, Object> param = new HashMap<String, Object>();
-        
ActionContext.getContext().withParameters(HttpParameters.create(param).build());
         ActionContext.getContext().put(ServletActionContext.HTTP_REQUEST, 
createMultipartRequestMaxSize(req, 2000));
 
         interceptor.setAllowedTypes("text/html");
         interceptor.intercept(mai);
 
-        HttpParameters parameters = mai.getInvocationContext().getParameters();
-        assertEquals(3, parameters.keySet().size());
-        UploadedFile[] files = (UploadedFile[]) 
parameters.get("file").getObject();
-        String[] fileContentTypes = 
parameters.get("fileContentType").getMultipleValues();
-        String[] fileRealFilenames = 
parameters.get("fileFileName").getMultipleValues();
+        List<UploadedFile> files = action.getUploadFiles();
 
         assertNotNull(files);
-        assertNotNull(fileContentTypes);
-        assertNotNull(fileRealFilenames);
-        assertEquals("files accepted ", 2, files.length);
-        assertEquals(2, fileContentTypes.length);
-        assertEquals(2, fileRealFilenames.length);
-        assertEquals("text/html", fileContentTypes[0]);
-        assertNotNull("test1.html", fileRealFilenames[0]);
+        assertEquals("files accepted ", 2, files.size());
+        assertEquals("text/html", files.get(0).getContentType());
+        assertNotNull("test1.html", files.get(0).getOriginalName());
     }
 
     public void testUnacceptedNumberOfFiles() throws Exception {
-        final String htmlContent = "<html><head></head><body>html 
content</body></html>";
-        final String plainContent = "plain content";
-        final String boundary = "simple boundary";
-        final String endline = "\r\n";
-
         MockHttpServletRequest req = new MockHttpServletRequest();
         req.setCharacterEncoding(StandardCharsets.UTF_8.name());
         req.setMethod("POST");
         req.addHeader("Content-type", "multipart/form-data; boundary=" + 
boundary);
-        StringBuilder content = new StringBuilder(128);
-        content.append(encodeTextFile(boundary, endline, "file", "test.html", 
"text/plain", plainContent));
-        content.append(encodeTextFile(boundary, endline, "file", "test1.html", 
"text/html", htmlContent));
-        content.append(encodeTextFile(boundary, endline, "file", "test2.html", 
"text/html", htmlContent));
-        content.append(encodeTextFile(boundary, endline, "file", "test3.html", 
"text/html", htmlContent));
-        content.append(endline);
-        content.append("--");
-        content.append(boundary);
-        content.append("--");
-        content.append(endline);
-        req.setContent(content.toString().getBytes());
+        String content = encodeTextFile("test.html", "text/plain", 
plainContent) +
+            encodeTextFile("test1.html", "text/html", htmlContent) +
+            encodeTextFile("test2.html", "text/html", htmlContent) +
+            encodeTextFile("test3.html", "text/html", htmlContent) +
+            endline +
+            "--" +
+            boundary +
+            "--" +
+            endline;
+        req.setContent(content.getBytes());
 
         assertTrue(ServletFileUpload.isMultipartContent(req));
 
-        MyFileupAction action = new MyFileupAction();
+        MyFileUploadAction action = new MyFileUploadAction();
         container.inject(action);
         MockActionInvocation mai = new MockActionInvocation();
         mai.setAction(action);
         mai.setResultCode("success");
         mai.setInvocationContext(ActionContext.getContext());
-        Map<String, Object> param = new HashMap<>();
-        
ActionContext.getContext().withParameters(HttpParameters.create(param).build());
-        ActionContext.getContext().put(ServletActionContext.HTTP_REQUEST, 
createMultipartRequestMaxFiles(req, 3));
+        
ActionContext.getContext().withServletRequest(createMultipartRequestMaxFiles(req));
 
         interceptor.setAllowedTypes("text/html");
         interceptor.intercept(mai);
 
-        HttpParameters parameters = mai.getInvocationContext().getParameters();
-        assertEquals(0, parameters.keySet().size());
+        assertNull(action.getUploadFiles());
         assertEquals(1, action.getActionErrors().size());
         assertEquals("Request exceeded allowed number of files! Max allowed 
files number is: 3!", action.getActionErrors().iterator().next());
     }
@@ -421,24 +399,22 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
 
         // inspired by the unit tests for jakarta commons fileupload
         String content = ("-----1234\r\n" +
-                "Content-Disposition: form-data; name=\"file\"; 
filename=\"deleteme.txt\"\r\n" +
-                "Content-Type: text/html\r\n" +
-                "\r\n" +
-                "Unit test of FileUploadInterceptor" +
-                "\r\n" +
-                "-----1234--\r\n");
+            "Content-Disposition: form-data; name=\"file\"; 
filename=\"deleteme.txt\"\r\n" +
+            "Content-Type: text/html\r\n" +
+            "\r\n" +
+            "Unit test of ActionFileUploadInterceptor" +
+            "\r\n" +
+            "-----1234--\r\n");
         req.setContent(content.getBytes(StandardCharsets.US_ASCII));
 
-        MyFileupAction action = container.inject(MyFileupAction.class);
+        MyFileUploadAction action = container.inject(MyFileUploadAction.class);
 
         MockActionInvocation mai = new MockActionInvocation();
         mai.setAction(action);
         mai.setResultCode("success");
         mai.setInvocationContext(ActionContext.getContext());
-        Map<String, Object> param = new HashMap<>();
         ActionContext.getContext()
-                .withParameters(HttpParameters.create(param).build())
-                .withServletRequest(createMultipartRequestMaxFileSize(req, 
10));
+            .withServletRequest(createMultipartRequestMaxFileSize(req));
 
         interceptor.intercept(mai);
 
@@ -448,8 +424,8 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
         assertEquals(1, errors.size());
         String msg = errors.iterator().next();
         assertEquals(
-                "File in request exceeded allowed file size limit! Max file 
size allowed is: 10 but file deleteme.txt was: 34!",
-                msg);
+            "File in request exceeded allowed file size limit! Max file size 
allowed is: 10 but file deleteme.txt was: 40!",
+            msg);
     }
 
     public void testMultipartRequestMaxStringLength() throws Exception {
@@ -460,34 +436,32 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
 
         // inspired by the unit tests for jakarta commons fileupload
         String content = ("-----1234\r\n" +
-                "Content-Disposition: form-data; name=\"file\"; 
filename=\"deleteme.txt\"\r\n" +
-                "Content-Type: text/html\r\n" +
-                "\r\n" +
-                "Unit test of FileUploadInterceptor" +
-                "\r\n" +
-                "-----1234\r\n" +
-                "Content-Disposition: form-data; 
name=\"normalFormField1\"\r\n" +
-                "\r\n" +
-                "it works" +
-                "\r\n" +
-                "-----1234\r\n" +
-                "Content-Disposition: form-data; 
name=\"normalFormField2\"\r\n" +
-                "\r\n" +
-                "long string should not work" +
-                "\r\n" +
-                "-----1234--\r\n");
+            "Content-Disposition: form-data; name=\"file\"; 
filename=\"deleteme.txt\"\r\n" +
+            "Content-Type: text/html\r\n" +
+            "\r\n" +
+            "Unit test of ActionFileUploadInterceptor" +
+            "\r\n" +
+            "-----1234\r\n" +
+            "Content-Disposition: form-data; name=\"normalFormField1\"\r\n" +
+            "\r\n" +
+            "it works" +
+            "\r\n" +
+            "-----1234\r\n" +
+            "Content-Disposition: form-data; name=\"normalFormField2\"\r\n" +
+            "\r\n" +
+            "long string should not work" +
+            "\r\n" +
+            "-----1234--\r\n");
         req.setContent(content.getBytes(StandardCharsets.US_ASCII));
 
-        MyFileupAction action = container.inject(MyFileupAction.class);
+        MyFileUploadAction action = container.inject(MyFileUploadAction.class);
 
         MockActionInvocation mai = new MockActionInvocation();
         mai.setAction(action);
         mai.setResultCode("success");
         mai.setInvocationContext(ActionContext.getContext());
-        Map<String, Object> param = new HashMap<>();
         ActionContext.getContext()
-                .withParameters(HttpParameters.create(param).build())
-                .withServletRequest(createMultipartRequestMaxStringLength(req, 
20));
+            .withServletRequest(createMultipartRequestMaxStringLength(req));
 
         interceptor.intercept(mai);
 
@@ -497,8 +471,8 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
         assertEquals(1, errors.size());
         String msg = errors.iterator().next();
         assertEquals(
-                "The request parameter \"normalFormField2\" was too long.  Max 
length allowed is 20, but found 27!",
-                msg);
+            "The request parameter \"normalFormField2\" was too long.  Max 
length allowed is 20, but found 27!",
+            msg);
     }
 
     public void testMultipartRequestLocalizedError() throws Exception {
@@ -509,23 +483,21 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
 
         // inspired by the unit tests for jakarta commons fileupload
         String content = ("-----1234\r\n" +
-                "Content-Disposition: form-data; name=\"file\"; 
filename=\"deleteme.txt\"\r\n" +
-                "Content-Type: text/html\r\n" +
-                "\r\n" +
-                "Unit test of FileUploadInterceptor" +
-                "\r\n" +
-                "-----1234--\r\n");
+            "Content-Disposition: form-data; name=\"file\"; 
filename=\"deleteme.txt\"\r\n" +
+            "Content-Type: text/html\r\n" +
+            "\r\n" +
+            "Unit test of ActionFileUploadInterceptor" +
+            "\r\n" +
+            "-----1234--\r\n");
         req.setContent(content.getBytes(StandardCharsets.US_ASCII));
 
-        MyFileupAction action = container.inject(MyFileupAction.class);
+        MyFileUploadAction action = container.inject(MyFileUploadAction.class);
 
         MockActionInvocation mai = new MockActionInvocation();
         mai.setAction(action);
         mai.setResultCode("success");
         mai.setInvocationContext(ActionContext.getContext());
-        Map<String, Object> param = new HashMap<>();
         ActionContext.getContext()
-            .withParameters(HttpParameters.create(param).build())
             .withLocale(Locale.GERMAN)
             .withServletRequest(createMultipartRequestMaxSize(req, 10));
 
@@ -540,44 +512,40 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
         assertTrue(msg.startsWith("Der Request übertraf die maximal erlaubte 
Größe"));
     }
 
-    private String encodeTextFile(String bondary, String endline, String name, 
String filename, String contentType, String content) {
-        final StringBuilder sb = new StringBuilder(64);
-        sb.append(endline);
-        sb.append("--");
-        sb.append(bondary);
-        sb.append(endline);
-        sb.append("Content-Disposition: form-data; name=\"");
-        sb.append(name);
-        sb.append("\"; filename=\"");
-        sb.append(filename);
-        sb.append(endline);
-        sb.append("Content-Type: ");
-        sb.append(contentType);
-        sb.append(endline);
-        sb.append(endline);
-        sb.append(content);
-
-        return sb.toString();
+    private String encodeTextFile(String filename, String contentType, String 
content) {
+        return "\r\n" +
+            "--" +
+            "simple boundary" +
+            "\r\n" +
+            "Content-Disposition: form-data; name=\"" +
+            "file" +
+            "\"; filename=\"" +
+            filename +
+            "\r\n" +
+            "Content-Type: " +
+            contentType +
+            "\r\n" +
+            "\r\n" +
+            content;
     }
 
-    private MultiPartRequestWrapper 
createMultipartRequestMaxFileSize(HttpServletRequest req, int maxfilesize) 
throws IOException {
-        return createMultipartRequest(req, -1, maxfilesize, -1, -1);
+    private MultiPartRequestWrapper 
createMultipartRequestMaxFileSize(HttpServletRequest req) {
+        return createMultipartRequest(req, -1, 10, -1, -1);
     }
 
-    private MultiPartRequestWrapper 
createMultipartRequestMaxFiles(HttpServletRequest req, int maxfiles) throws 
IOException {
-        return createMultipartRequest(req, -1, -1, maxfiles, -1);
+    private MultiPartRequestWrapper 
createMultipartRequestMaxFiles(HttpServletRequest req) {
+        return createMultipartRequest(req, -1, -1, 3, -1);
     }
 
-    private MultiPartRequestWrapper 
createMultipartRequestMaxSize(HttpServletRequest req, int maxsize) throws 
IOException {
+    private MultiPartRequestWrapper 
createMultipartRequestMaxSize(HttpServletRequest req, int maxsize) {
         return createMultipartRequest(req, maxsize, -1, -1, -1);
     }
 
-    private MultiPartRequestWrapper 
createMultipartRequestMaxStringLength(HttpServletRequest req, int 
maxStringLength) throws IOException {
-        return createMultipartRequest(req, -1, -1, -1, maxStringLength);
+    private MultiPartRequestWrapper 
createMultipartRequestMaxStringLength(HttpServletRequest req) {
+        return createMultipartRequest(req, -1, -1, -1, 20);
     }
 
-    private MultiPartRequestWrapper createMultipartRequest(HttpServletRequest 
req, int maxsize, int maxfilesize,
-                                                           int maxfiles, int 
maxStringLength) throws IOException {
+    private MultiPartRequestWrapper createMultipartRequest(HttpServletRequest 
req, int maxsize, int maxfilesize, int maxfiles, int maxStringLength) {
 
         JakartaMultiPartRequest jak = new JakartaMultiPartRequest();
         jak.setMaxSize(String.valueOf(maxsize));
@@ -589,10 +557,8 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
 
     protected void setUp() throws Exception {
         super.setUp();
-        action = new TestAction();
-        container.inject(action);
 
-        interceptor = new FileUploadInterceptor();
+        interceptor = new ActionFileUploadInterceptor();
         container.inject(interceptor);
         tempDir = File.createTempFile("struts", "fileupload");
         tempDir.delete();
@@ -605,12 +571,17 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
         super.tearDown();
     }
 
-    public static class MyFileupAction extends ActionSupport {
+    public static class MyFileUploadAction extends ActionSupport implements 
UploadedFilesAware {
+        private List<UploadedFile> uploadedFiles;
 
-        private static final long serialVersionUID = 6255238895447968889L;
+        @Override
+        public void withUploadedFiles(List<UploadedFile> uploadedFiles) {
+            this.uploadedFiles = uploadedFiles;
+        }
 
-        // no methods
+        public List<UploadedFile> getUploadFiles() {
+            return this.uploadedFiles;
+        }
     }
 
-
 }
diff --git 
a/core/src/test/java/org/apache/struts2/interceptor/FileUploadInterceptorTest.java
 
b/core/src/test/java/org/apache/struts2/interceptor/FileUploadInterceptorTest.java
index b9bab88d4..18758c422 100644
--- 
a/core/src/test/java/org/apache/struts2/interceptor/FileUploadInterceptorTest.java
+++ 
b/core/src/test/java/org/apache/struts2/interceptor/FileUploadInterceptorTest.java
@@ -83,28 +83,37 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
         public byte[] getContent() {
             return new byte[0];
         }
+
+        @Override
+        public String getOriginalName() {
+            return null;
+        }
+
+        @Override
+        public String getContentType() {
+            return null;
+        }
     };
 
     private FileUploadInterceptor interceptor;
     private File tempDir;
-    private TestAction action;
 
-    public void testAcceptFileWithEmptyAllowedTypesAndExtensions() throws 
Exception {
+    public void testAcceptFileWithEmptyAllowedTypesAndExtensions() {
         // when allowed type is empty
         ValidationAwareSupport validation = new ValidationAwareSupport();
-        boolean ok = interceptor.acceptFile(action, EMPTY_FILE, "filename", 
"text/plain", "inputName", validation);
+        boolean ok = interceptor.acceptFile(validation, EMPTY_FILE, 
"filename", "text/plain", "inputName");
 
         assertTrue(ok);
         assertTrue(validation.getFieldErrors().isEmpty());
         assertFalse(validation.hasErrors());
     }
 
-    public void testAcceptFileWithoutEmptyTypes() throws Exception {
+    public void testAcceptFileWithoutEmptyTypes() {
         interceptor.setAllowedTypes("text/plain");
 
         // when file is of allowed types
         ValidationAwareSupport validation = new ValidationAwareSupport();
-        boolean ok = interceptor.acceptFile(action, EMPTY_FILE, 
"filename.txt", "text/plain", "inputName", validation);
+        boolean ok = interceptor.acceptFile(validation, EMPTY_FILE, 
"filename.txt", "text/plain", "inputName");
 
         assertTrue(ok);
         assertTrue(validation.getFieldErrors().isEmpty());
@@ -112,7 +121,7 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
 
         // when file is not of allowed types
         validation = new ValidationAwareSupport();
-        boolean notOk = interceptor.acceptFile(action, EMPTY_FILE, 
"filename.html", "text/html", "inputName", validation);
+        boolean notOk = interceptor.acceptFile(validation, EMPTY_FILE, 
"filename.html", "text/html", "inputName");
 
         assertFalse(notOk);
         assertFalse(validation.getFieldErrors().isEmpty());
@@ -120,11 +129,11 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
     }
 
 
-    public void testAcceptFileWithWildcardContent() throws Exception {
+    public void testAcceptFileWithWildcardContent() {
         interceptor.setAllowedTypes("text/*");
 
         ValidationAwareSupport validation = new ValidationAwareSupport();
-        boolean ok = interceptor.acceptFile(action, EMPTY_FILE, 
"filename.txt", "text/plain", "inputName", validation);
+        boolean ok = interceptor.acceptFile(validation, EMPTY_FILE, 
"filename.txt", "text/plain", "inputName");
 
         assertTrue(ok);
         assertTrue(validation.getFieldErrors().isEmpty());
@@ -132,19 +141,19 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
 
         interceptor.setAllowedTypes("text/h*");
         validation = new ValidationAwareSupport();
-        boolean notOk = interceptor.acceptFile(action, EMPTY_FILE, 
"filename.html", "text/plain", "inputName", validation);
+        boolean notOk = interceptor.acceptFile(validation, EMPTY_FILE, 
"filename.html", "text/plain", "inputName");
 
         assertFalse(notOk);
         assertFalse(validation.getFieldErrors().isEmpty());
         assertTrue(validation.hasErrors());
     }
 
-    public void testAcceptFileWithoutEmptyExtensions() throws Exception {
+    public void testAcceptFileWithoutEmptyExtensions() {
         interceptor.setAllowedExtensions(".txt");
 
         // when file is of allowed extensions
         ValidationAwareSupport validation = new ValidationAwareSupport();
-        boolean ok = interceptor.acceptFile(action, EMPTY_FILE, 
"filename.txt", "text/plain", "inputName", validation);
+        boolean ok = interceptor.acceptFile(validation, EMPTY_FILE, 
"filename.txt", "text/plain", "inputName");
 
         assertTrue(ok);
         assertTrue(validation.getFieldErrors().isEmpty());
@@ -152,7 +161,7 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
 
         // when file is not of allowed extensions
         validation = new ValidationAwareSupport();
-        boolean notOk = interceptor.acceptFile(action, EMPTY_FILE, 
"filename.html", "text/html", "inputName", validation);
+        boolean notOk = interceptor.acceptFile(validation, EMPTY_FILE, 
"filename.html", "text/html", "inputName");
 
         assertFalse(notOk);
         assertFalse(validation.getFieldErrors().isEmpty());
@@ -161,34 +170,36 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
         //test with multiple extensions
         interceptor.setAllowedExtensions(".txt,.lol");
         validation = new ValidationAwareSupport();
-        ok = interceptor.acceptFile(action, EMPTY_FILE, "filename.lol", 
"text/plain", "inputName", validation);
+        ok = interceptor.acceptFile(validation, EMPTY_FILE, "filename.lol", 
"text/plain", "inputName");
 
         assertTrue(ok);
         assertTrue(validation.getFieldErrors().isEmpty());
         assertFalse(validation.hasErrors());
     }
 
-    public void testAcceptFileWithNoFile() throws Exception {
+    public void testAcceptFileWithNoFile() {
         FileUploadInterceptor interceptor = new FileUploadInterceptor();
+        interceptor.setContainer(container);
+
         interceptor.setAllowedTypes("text/plain");
 
         // when file is not of allowed types
         ValidationAwareSupport validation = new ValidationAwareSupport();
-        boolean notOk = interceptor.acceptFile(action, null, "filename.html", 
"text/html", "inputName", validation);
+        boolean notOk = interceptor.acceptFile(validation, null, 
"filename.html", "text/html", "inputName");
 
         assertFalse(notOk);
         assertFalse(validation.getFieldErrors().isEmpty());
         assertTrue(validation.hasErrors());
-        List errors = (List) validation.getFieldErrors().get("inputName");
+        List<String> errors = validation.getFieldErrors().get("inputName");
         assertEquals(1, errors.size());
-        String msg = (String) errors.get(0);
+        String msg = errors.get(0);
         assertTrue(msg.startsWith("Error uploading:"));
         assertTrue(msg.indexOf("inputName") > 0);
     }
 
     public void testAcceptFileWithMaxSize() throws Exception {
         interceptor.setAllowedTypes("text/plain");
-        interceptor.setMaximumSize(new Long(10));
+        interceptor.setMaximumSize(10L);
 
         // when file is not of allowed types
         ValidationAwareSupport validation = new ValidationAwareSupport();
@@ -196,14 +207,15 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
         URL url = ClassLoaderUtil.getResource("log4j2.xml", 
FileUploadInterceptorTest.class);
         File file = new File(new URI(url.toString()));
         assertTrue("log4j2.xml should be in src/test folder", file.exists());
-        boolean notOk = interceptor.acceptFile(action, new 
StrutsUploadedFile(file), "filename", "text/html", "inputName", validation);
+        UploadedFile uploadedFile = 
StrutsUploadedFile.Builder.create(file).withContentType("text/html").withOriginalName("filename").build();
+        boolean notOk = interceptor.acceptFile(validation, uploadedFile, 
"filename", "text/html", "inputName");
 
         assertFalse(notOk);
         assertFalse(validation.getFieldErrors().isEmpty());
         assertTrue(validation.hasErrors());
         List<String> errors = validation.getFieldErrors().get("inputName");
         assertEquals(1, errors.size());
-        String msg = (String) errors.get(0);
+        String msg = errors.get(0);
         // the error message should contain at least this test
         assertTrue(msg.startsWith("The file is too large to be uploaded"));
         assertTrue(msg.indexOf("inputName") > 0);
@@ -278,7 +290,7 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
                 "Unit test of FileUploadInterceptor" +
                 "\r\n" +
                 "-----1234--\r\n");
-        req.setContent(content.getBytes("US-ASCII"));
+        req.setContent(content.getBytes(StandardCharsets.US_ASCII));
 
         MyFileupAction action = new MyFileupAction();
 
@@ -292,10 +304,10 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
 
         interceptor.intercept(mai);
 
-        assertTrue(!action.hasErrors());
+        assertFalse(action.hasErrors());
 
         HttpParameters parameters = mai.getInvocationContext().getParameters();
-        assertTrue(parameters.keySet().size() == 3);
+        assertEquals(3, parameters.keySet().size());
         UploadedFile[] files = (UploadedFile[]) 
parameters.get("file").getObject();
         String[] fileContentTypes = 
parameters.get("fileContentType").getMultipleValues();
         String[] fileRealFilenames = 
parameters.get("fileFileName").getMultipleValues();
@@ -303,9 +315,9 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
         assertNotNull(files);
         assertNotNull(fileContentTypes);
         assertNotNull(fileRealFilenames);
-        assertTrue(files.length == 1);
-        assertTrue(fileContentTypes.length == 1);
-        assertTrue(fileRealFilenames.length == 1);
+        assertEquals(1, files.length);
+        assertEquals(1, fileContentTypes.length);
+        assertEquals(1, fileRealFilenames.length);
         assertEquals("text/html", fileContentTypes[0]);
         assertNotNull("deleteme.txt", fileRealFilenames[0]);
     }
@@ -313,8 +325,6 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
     /**
      * tests whether with multiple files sent with the same name, the ones 
with forbiddenTypes (see
      * FileUploadInterceptor.setAllowedTypes(...) ) are sorted out.
-     *
-     * @throws Exception
      */
     public void testMultipleAccept() throws Exception {
         final String htmlContent = "<html><head></head><body>html 
content</body></html>";
@@ -326,18 +336,17 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
         req.setCharacterEncoding(StandardCharsets.UTF_8.name());
         req.setMethod("POST");
         req.addHeader("Content-type", "multipart/form-data; boundary=" + 
bondary);
-        StringBuilder content = new StringBuilder(128);
-        content.append(encodeTextFile(bondary, endline, "file", "test.html", 
"text/plain", plainContent));
-        content.append(encodeTextFile(bondary, endline, "file", "test1.html", 
"text/html", htmlContent));
-        content.append(encodeTextFile(bondary, endline, "file", "test2.html", 
"text/html", htmlContent));
-        content.append(endline);
-        content.append(endline);
-        content.append(endline);
-        content.append("--");
-        content.append(bondary);
-        content.append("--");
-        content.append(endline);
-        req.setContent(content.toString().getBytes());
+        String content = encodeTextFile("test.html", "text/plain", 
plainContent) +
+            encodeTextFile("test1.html", "text/html", htmlContent) +
+            encodeTextFile("test2.html", "text/html", htmlContent) +
+            endline +
+            endline +
+            endline +
+            "--" +
+            bondary +
+            "--" +
+            endline;
+        req.setContent(content.getBytes());
 
         assertTrue(ServletFileUpload.isMultipartContent(req));
 
@@ -347,7 +356,7 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
         mai.setAction(action);
         mai.setResultCode("success");
         mai.setInvocationContext(ActionContext.getContext());
-        Map<String, Object> param = new HashMap<String, Object>();
+        Map<String, Object> param = new HashMap<>();
         
ActionContext.getContext().withParameters(HttpParameters.create(param).build());
         ActionContext.getContext().put(ServletActionContext.HTTP_REQUEST, 
createMultipartRequestMaxSize(req, 2000));
 
@@ -380,17 +389,16 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
         req.setCharacterEncoding(StandardCharsets.UTF_8.name());
         req.setMethod("POST");
         req.addHeader("Content-type", "multipart/form-data; boundary=" + 
boundary);
-        StringBuilder content = new StringBuilder(128);
-        content.append(encodeTextFile(boundary, endline, "file", "test.html", 
"text/plain", plainContent));
-        content.append(encodeTextFile(boundary, endline, "file", "test1.html", 
"text/html", htmlContent));
-        content.append(encodeTextFile(boundary, endline, "file", "test2.html", 
"text/html", htmlContent));
-        content.append(encodeTextFile(boundary, endline, "file", "test3.html", 
"text/html", htmlContent));
-        content.append(endline);
-        content.append("--");
-        content.append(boundary);
-        content.append("--");
-        content.append(endline);
-        req.setContent(content.toString().getBytes());
+        String content = encodeTextFile("test.html", "text/plain", 
plainContent) +
+            encodeTextFile("test1.html", "text/html", htmlContent) +
+            encodeTextFile("test2.html", "text/html", htmlContent) +
+            encodeTextFile("test3.html", "text/html", htmlContent) +
+            endline +
+            "--" +
+            boundary +
+            "--" +
+            endline;
+        req.setContent(content.getBytes());
 
         assertTrue(ServletFileUpload.isMultipartContent(req));
 
@@ -402,7 +410,7 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
         mai.setInvocationContext(ActionContext.getContext());
         Map<String, Object> param = new HashMap<>();
         
ActionContext.getContext().withParameters(HttpParameters.create(param).build());
-        ActionContext.getContext().put(ServletActionContext.HTTP_REQUEST, 
createMultipartRequestMaxFiles(req, 3));
+        ActionContext.getContext().put(ServletActionContext.HTTP_REQUEST, 
createMultipartRequestMaxFiles(req));
 
         interceptor.setAllowedTypes("text/html");
         interceptor.intercept(mai);
@@ -438,7 +446,7 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
         Map<String, Object> param = new HashMap<>();
         ActionContext.getContext()
                 .withParameters(HttpParameters.create(param).build())
-                .withServletRequest(createMultipartRequestMaxFileSize(req, 
10));
+                .withServletRequest(createMultipartRequestMaxFileSize(req));
 
         interceptor.intercept(mai);
 
@@ -487,7 +495,7 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
         Map<String, Object> param = new HashMap<>();
         ActionContext.getContext()
                 .withParameters(HttpParameters.create(param).build())
-                .withServletRequest(createMultipartRequestMaxStringLength(req, 
20));
+                
.withServletRequest(createMultipartRequestMaxStringLength(req));
 
         interceptor.intercept(mai);
 
@@ -540,44 +548,40 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
         assertTrue(msg.startsWith("Der Request übertraf die maximal erlaubte 
Größe"));
     }
 
-    private String encodeTextFile(String bondary, String endline, String name, 
String filename, String contentType, String content) {
-        final StringBuilder sb = new StringBuilder(64);
-        sb.append(endline);
-        sb.append("--");
-        sb.append(bondary);
-        sb.append(endline);
-        sb.append("Content-Disposition: form-data; name=\"");
-        sb.append(name);
-        sb.append("\"; filename=\"");
-        sb.append(filename);
-        sb.append(endline);
-        sb.append("Content-Type: ");
-        sb.append(contentType);
-        sb.append(endline);
-        sb.append(endline);
-        sb.append(content);
-
-        return sb.toString();
+    private String encodeTextFile(String filename, String contentType, String 
content) {
+        return "\r\n" +
+            "--" +
+            "simple boundary" +
+            "\r\n" +
+            "Content-Disposition: form-data; name=\"" +
+            "file" +
+            "\"; filename=\"" +
+            filename +
+            "\r\n" +
+            "Content-Type: " +
+            contentType +
+            "\r\n" +
+            "\r\n" +
+            content;
     }
 
-    private MultiPartRequestWrapper 
createMultipartRequestMaxFileSize(HttpServletRequest req, int maxfilesize) 
throws IOException {
-        return createMultipartRequest(req, -1, maxfilesize, -1, -1);
+    private MultiPartRequestWrapper 
createMultipartRequestMaxFileSize(HttpServletRequest req) {
+        return createMultipartRequest(req, -1, 10, -1, -1);
     }
 
-    private MultiPartRequestWrapper 
createMultipartRequestMaxFiles(HttpServletRequest req, int maxfiles) throws 
IOException {
-        return createMultipartRequest(req, -1, -1, maxfiles, -1);
+    private MultiPartRequestWrapper 
createMultipartRequestMaxFiles(HttpServletRequest req) {
+        return createMultipartRequest(req, -1, -1, 3, -1);
     }
 
-    private MultiPartRequestWrapper 
createMultipartRequestMaxSize(HttpServletRequest req, int maxsize) throws 
IOException {
+    private MultiPartRequestWrapper 
createMultipartRequestMaxSize(HttpServletRequest req, int maxsize) {
         return createMultipartRequest(req, maxsize, -1, -1, -1);
     }
 
-    private MultiPartRequestWrapper 
createMultipartRequestMaxStringLength(HttpServletRequest req, int 
maxStringLength) throws IOException {
-        return createMultipartRequest(req, -1, -1, -1, maxStringLength);
+    private MultiPartRequestWrapper 
createMultipartRequestMaxStringLength(HttpServletRequest req) {
+        return createMultipartRequest(req, -1, -1, -1, 20);
     }
 
-    private MultiPartRequestWrapper createMultipartRequest(HttpServletRequest 
req, int maxsize, int maxfilesize,
-                                                           int maxfiles, int 
maxStringLength) throws IOException {
+    private MultiPartRequestWrapper createMultipartRequest(HttpServletRequest 
req, int maxsize, int maxfilesize, int maxfiles, int maxStringLength) {
 
         JakartaMultiPartRequest jak = new JakartaMultiPartRequest();
         jak.setMaxSize(String.valueOf(maxsize));
@@ -589,8 +593,6 @@ public class FileUploadInterceptorTest extends 
StrutsInternalTestCase {
 
     protected void setUp() throws Exception {
         super.setUp();
-        action = new TestAction();
-        container.inject(action);
 
         interceptor = new FileUploadInterceptor();
         container.inject(interceptor);
diff --git 
a/plugins/pell-multipart/src/main/java/org/apache/struts2/dispatcher/multipart/PellMultiPartRequest.java
 
b/plugins/pell-multipart/src/main/java/org/apache/struts2/dispatcher/multipart/PellMultiPartRequest.java
index 9688be65e..d2db975d4 100644
--- 
a/plugins/pell-multipart/src/main/java/org/apache/struts2/dispatcher/multipart/PellMultiPartRequest.java
+++ 
b/plugins/pell-multipart/src/main/java/org/apache/struts2/dispatcher/multipart/PellMultiPartRequest.java
@@ -31,7 +31,6 @@ import java.util.List;
 
 /**
  * Multipart form data request adapter for Jason Pell's multipart utils 
package.
- *
  */
 public class PellMultiPartRequest extends AbstractMultiPartRequest {
 
@@ -69,7 +68,11 @@ public class PellMultiPartRequest extends 
AbstractMultiPartRequest {
     }
 
     public UploadedFile[] getFile(String fieldName) {
-        return new UploadedFile[]{ new 
StrutsUploadedFile(multi.getFile(fieldName)) };
+        return new 
UploadedFile[]{StrutsUploadedFile.Builder.create(multi.getFile(fieldName))
+            .withContentType(multi.getContentType(fieldName))
+            .withOriginalName(multi.getFileSystemName(fieldName))
+            .build()
+        };
     }
 
     public String[] getFileNames(String fieldName) {
@@ -132,7 +135,7 @@ public class PellMultiPartRequest extends 
AbstractMultiPartRequest {
             }
         } catch (IllegalArgumentException e) {
             if (LOG.isInfoEnabled()) {
-                   LOG.info("Could not get encoding property 
'struts.i18n.encoding' for file upload.  Using system default");
+                LOG.info("Could not get encoding property 
'struts.i18n.encoding' for file upload.  Using system default");
             }
         } catch (UnsupportedEncodingException e) {
             LOG.error("Encoding " + encoding + " is not a valid encoding.  
Please check your struts.properties file.");
@@ -140,8 +143,8 @@ public class PellMultiPartRequest extends 
AbstractMultiPartRequest {
     }
 
     /* (non-Javadoc)
-    * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#cleanUp()
-    */
+     * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#cleanUp()
+     */
     public void cleanUp() {
         Enumeration fileParameterNames = multi.getFileParameterNames();
         while (fileParameterNames != null && 
fileParameterNames.hasMoreElements()) {

Reply via email to