org.apache.commons.fileupload.FileItemFactory Not Found (Fixed)

ClassNotFoundException for org.apache.commons.fileupload.FileItemFactory means the JAR is missing. Fix it for Spring 6, Jakarta servlets and legacy apps.

Tomcat class loader searching WEB-INF/classes, WEB-INF/lib and the Tomcat lib folder without finding FileItemFactory, with fixes for each stack

The error java.lang.ClassNotFoundException: org.apache.commons.fileupload.FileItemFactory means that the Apache Commons FileUpload 1.x JAR is not on the runtime class path of the web app, although some code needs it to parse a file upload. In most cases the code is the CommonsMultipartResolver bean of a Spring 5 app, and the JAR is missing from WEB-INF/lib of the deployed WAR.

The fix depends on the stack. Spring Framework 6 and 7, Spring Boot 3 and 4, and plain Jakarta servlets do not need Commons FileUpload at all, because the servlet container parses multipart requests itself. Only older javax apps still add commons-fileupload 1.6.0 to the WAR.

The following example asks the class loader for both versions of the class, the same way Tomcat does when a class refers to it.

Class<?> legacy = Class.forName("org.apache.commons.fileupload.FileItemFactory");    // ClassNotFoundException without commons-fileupload 1.x
Class<?> current = Class.forName("org.apache.commons.fileupload2.core.FileItemFactory"); // interface org.apache.commons.fileupload2.core.FileItemFactory

Notice that version 2 of the library uses the package org.apache.commons.fileupload2, so adding the new JAR does not fix code that still imports the old package. We look at where the class loader searches, the servlet multipart fix for current stacks, Commons FileUpload 2 for Jakarta EE, the legacy fix, and a quick way to check the WAR file.

1. What Causes the FileItemFactory ClassNotFoundException

A ClassNotFoundException is thrown at runtime, when the JVM tries to load a class by name and none of its class loaders finds it. The code compiled fine, so the JAR was there at build time. It is missing only from the place where the server looks at runtime.

Tomcat class loader searching WEB-INF/classes, WEB-INF/lib and the Tomcat lib folder without finding FileItemFactory, with fixes for each stack
Tomcat finds FileItemFactory only if commons-fileupload is packed in WEB-INF/lib, so the fix depends on the stack we run

In a Spring 5 app, the trace starts when Spring creates the multipartResolver bean and reflects over its methods. The top of the original log from Tomcat looks like this.

java.lang.ClassNotFoundException: org.apache.commons.fileupload.FileItemFactory
	at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1714)
	at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1559)
	at java.lang.Class.getDeclaredMethods0(Native Method)
	at java.lang.Class.getDeclaredMethods(Unknown Source)
	at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:571)

The JAR goes missing for a few typical reasons.

  • The dependency is declared with provided or test scope, so Maven leaves it out of the WAR.
  • The project was built in the IDE, and the deployment assembly (Eclipse) or the artifact layout (IntelliJ IDEA) does not copy the Maven libraries to WEB-INF/lib.
  • Someone removed commons-fileupload from pom.xml as an unused dependency, but the Spring XML configuration still declares CommonsMultipartResolver.
  • The code was moved to the commons-fileupload2 artifacts, while some classes still import org.apache.commons.fileupload.

A missing class referenced by an already loaded class can also show up as NoClassDefFoundError: org/apache/commons/fileupload/FileItemFactory. The cause and the fix are the same. The exception hierarchy explains why one is an exception and the other an error.

2. The Current Fix With Servlet Multipart Support

Since Servlet 3.0, every servlet container parses multipart/form-data requests itself. Spring Framework 6.0 removed its Commons FileUpload support, including CommonsMultipartResolver, and recommends StandardServletMultipartResolver. So on a current stack, we do not add a JAR. We remove the old resolver and use the built-in support.

2.1. Spring MVC and Spring Boot Applications

In a Spring Boot 3 or 4 application, multipart support is on by default and no bean is needed. In Spring MVC without Spring Boot, we replace the CommonsMultipartResolver bean with a StandardServletMultipartResolver bean named multipartResolver and register a MultipartConfigElement on the DispatcherServlet. The controller code stays the same, because it works with MultipartFile.

@RestController
public class PhotoController {

    @PostMapping("/photos")
    public String upload(@RequestParam("photo") MultipartFile photo) throws IOException {
        String original = Objects.requireNonNullElse(photo.getOriginalFilename(), "photo.bin");
        Path target = Path.of("uploads").resolve(Path.of(original).getFileName());
        photo.transferTo(target);
        return "Saved " + target.getFileName() + ", " + photo.getSize() + " bytes";
    }
}

The call Path.of(original).getFileName() drops any folder names that a client puts into the file name. Spring Boot limits uploads to 1 MB per file and 10 MB per request by default, and we raise the limits with two properties.

spring.servlet.multipart.max-file-size=5MB
spring.servlet.multipart.max-request-size=20MB

For example, a recipe app moves from Spring 5 to Spring Boot 4. The old dispatcher-servlet.xml still declares CommonsMultipartResolver, so the app fails at startup with CannotLoadBeanClassException, caused by a ClassNotFoundException for the resolver class itself. Deleting the bean definition and the commons-fileupload dependency fixes the startup, and the photo upload controller keeps working unchanged. The Spring Boot file upload REST API tutorial shows a complete example.

2.2. Plain Servlets With @MultipartConfig

A servlet without Spring gets the same support with the @MultipartConfig annotation. The container parses the request, and HttpServletRequest.getPart() returns each file as a Part.

@MultipartConfig(maxFileSize = 5 * 1024 * 1024, maxRequestSize = 20 * 1024 * 1024)
public class PhotoUploadServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
        Part photo = req.getPart("photo");
        String fileName = Path.of(photo.getSubmittedFileName()).getFileName().toString();
        try (InputStream in = photo.getInputStream()) {
            Files.copy(in, Path.of("uploads", fileName), StandardCopyOption.REPLACE_EXISTING);
        }
        resp.getWriter().write("Saved " + fileName + ", " + photo.getSize() + " bytes");
    }
}

A file larger than maxFileSize makes getPart() throw IllegalStateException, so the servlet never reads an oversized upload. This code runs on Tomcat 10.1 and 11 without any extra JAR.

3. Commons FileUpload 2 for Jakarta EE

Some apps need the streaming API or the progress listener of Commons FileUpload. For jakarta.servlet, the library has new artifacts and a new package. The current version on Maven Central is 2.0.0-M5, which is still a milestone, so we weigh that before using it in production.

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-fileupload2-jakarta-servlet6</artifactId>
    <version>2.0.0-M5</version>
</dependency>

Maven pulls in commons-fileupload2-core and commons-io as well. The servlet class ServletFileUpload becomes JakartaServletFileUpload, and the factory uses a builder.

public class RecipeImportServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        DiskFileItemFactory factory = DiskFileItemFactory.builder().get();
        JakartaServletFileUpload<DiskFileItem, DiskFileItemFactory> upload = new JakartaServletFileUpload<>(factory);
        upload.setMaxFileSize(5 * 1024 * 1024);
        upload.setMaxFileCount(10);

        List<DiskFileItem> items = upload.parseRequest(req);
        for (DiskFileItem item : items) {
            if (!item.isFormField()) {
                resp.getWriter().write(item.getName() + ": " + item.getSize() + " bytes\n");
            }
        }
    }
}

Always set setMaxFileCount(), because a request with thousands of tiny parts can exhaust the server memory. That attack is CVE-2023-24998, and the file count limit was added to FileUpload 1.5 to stop it.

4. The Legacy Fix for javax Projects

An app that still runs on Spring 5 and Tomcat 9 (javax.servlet) can keep CommonsMultipartResolver. We add the last 1.x version with the default compile scope, so Maven packs it into WEB-INF/lib. Maven adds commons-io as a transitive dependency.

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.6.0</version>
</dependency>

Never downgrade to an old 1.2 or 1.3 version to make the error go away. The version number is not the problem, and those releases have known security issues, such as CVE-2016-1000031 in DiskFileItem. The Spring MVC multiple file upload example shows the resolver configuration for such apps.

5. Checking the WAR for the Missing JAR

When the error appears only on the server, the WAR file is the first thing to inspect. The jar tool from the JDK lists its content, and the libraries we expect must appear under WEB-INF/lib.

jar tf target/photos.war | grep -E "fileupload|commons-io"
WEB-INF/lib/commons-fileupload2-core-2.0.0-M5.jar
WEB-INF/lib/commons-fileupload2-jakarta-servlet6-2.0.0-M5.jar
WEB-INF/lib/commons-io-2.21.0.jar

If the JAR is missing from the WAR, mvn dependency:tree -Dincludes=commons-fileupload shows its scope and which dependency brings it in. If the WAR is correct but the IDE deployment still fails, check the Deployment Assembly page of the Eclipse project properties, which must contain an entry for Maven Dependencies.

6. Commons FileUpload Error FAQs

The class name in the error does not show which Maven artifact or version provides it, so we start with the JAR.

6.1. Which JAR contains org.apache.commons.fileupload.FileItemFactory?

The commons-fileupload:commons-fileupload JAR, version 1.x. Version 2 moved the interface to org.apache.commons.fileupload2.core.FileItemFactory in the commons-fileupload2-core JAR.

6.2. Does Spring Boot need commons-fileupload?

No. Spring Boot 3 and 4 use the multipart support of the embedded Tomcat through StandardServletMultipartResolver, and CommonsMultipartResolver no longer exists in Spring Framework 6 and later.

6.3. Why does the upload work in the IDE but fail on Tomcat?

Because the IDE runs the app with the full Maven class path, while Tomcat sees only the JARs inside the WAR. A provided scope or a broken deployment assembly leaves commons-fileupload out of WEB-INF/lib, as described in section 5.

6.4. What causes ClassNotFoundException for org.apache.commons.io.output.DeferredFileOutputStream?

The commons-io JAR is missing. Commons FileUpload uses it to buffer large uploads on disk. Maven adds it as a transitive dependency, so the error appears when JARs are copied by hand or excluded in pom.xml.

7. Conclusion

The ClassNotFoundException for org.apache.commons.fileupload.FileItemFactory means the Commons FileUpload 1.x JAR is not in the WAR, not that we use a wrong version. On Spring 6 and 7, Spring Boot 3 and 4, and plain Jakarta servlets, we remove the old resolver and let the container parse uploads with MultipartFile or getPart().

Apps that need the Commons API on Jakarta EE use the commons-fileupload2-jakarta-servlet6 milestone with its new package names. Old javax apps add commons-fileupload 1.6.0 with compile scope and confirm with jar tf that it reached WEB-INF/lib.

8. References

Happy Learning !!

Source Code on Github

Leave a Comment

Comments are closed.

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.