← Back to list

Handling Multipart File Uploads in JAX-RS 2.1 (on WebSphere Liberty)

In transitioning from JAX-RS 1.x to JAX-RS 2.x, one significant change developers may encounter is how multipart file uploads are handled.

keylearn · 2025-05-07 21:44 · 6 claps · 3.5 min read paywalled
#jax-rs #java #websphere #multipart-form-data #file-upload
Open on Medium ↗

Handling Multipart File Uploads in JAX-RS 2.1 (on WebSphere Liberty)

In transitioning from JAX-RS 1.x to JAX-RS 2.x, one significant change developers may encounter is how multipart file uploads are handled.

With JAX-RS 1, file handling was often done using:

@FormDataParam("file") File file;

However, this approach doesn’t work the same way in JAX-RS 2 environments like WebSphere Liberty, which do not provide native support for @FormDataParam.

Instead, we need to rely on the raw **HttpServletRequest** to access multipart parts manually, which requires more boilerplate but offers flexibility.

This blog is inspired and largely made possible by Jason Lee’s great guide, which helped me understand how to implement file upload support cleanly in a Liberty JAX-RS 2.1 environment.

Let’s walk through a working example step-by-step:

Step 1: pom.xml – Setting up Dependencies

We create a WAR-based Maven project and add JAX-RS 2.1 and Servlet 4.0 dependencies. Since we’re deploying to WebSphere Liberty, these APIs are marked as provided.

<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>jaxrs-was-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- JAX-RS API -->
        <dependency>
            <groupId>javax.ws.rs</groupId>
            <artifactId>javax.ws.rs-api</artifactId>
            <version>2.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>

    </dependencies>

    <build>
        <finalName>jaxrs-was-demo</finalName>
    </build>
</project>

Step 2: server.xml – Liberty Server Configuration

Enable the JAX-RS 2.1 and Servlet 4.0 features and point the server to your deployed WAR.

<server description="JAX-RS 2.1 Application Server">

    <featureManager>
        <feature>jaxrs-2.1</feature>
        <feature>servlet-4.0</feature>
    </featureManager>

    <httpEndpoint id="defaultHttpEndpoint"
                  host="*"
                  httpPort="9080"
                  httpsPort="9443" />
    <application location="/config/jaxrs-was-demo.war" name="jaxrs-was-demo">
        <context-root>/</context-root>
    </application>

</server>

Step 3: web.xml – Register the JAX-RS Servlet

Use the Liberty-specific IBMRestServlet and configure it to load your JAX-RS Application class.

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <display-name>JAX-RS Liberty Demo</display-name>
    <servlet>
        <servlet-name>was</servlet-name>
        <servlet-class>com.ibm.websphere.jaxrs.server.IBMRestServlet</servlet-class>
        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>com.example.rest.ApplicationConfig</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
        <multipart-config />
    </servlet>

</web-app>

You’ll also want to include a proper <multipart-config> block here to enable multipart parsing via the servlet container.

Step 4: ApplicationConfig.java – Register JAX-RS Resources

Set the base path /api and register your REST resource class.

@ApplicationPath("/api")
public class ApplicationConfig extends Application {
    @Override
    public Set<Class<?>> getClasses() {
        Set<Class<?>> classes = new HashSet<>();
        classes.add(HelloResource.class);
        return classes;
    }
}

Step 5: HelloResource.java – Implement the Upload Endpoint

This resource handles two things: a test GET endpoint and a POST endpoint to accept multipart file uploads using raw servlet APIs.

@Path("/hello")
public class HelloResource {

    @GET
    public String sayHello() {
        return "Hello from JAX-RS 2.1 on WebSphere!";
    }

    @POST
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    public String createDocumentPost(@Context HttpServletRequest request) {
        MultipartRequestMap map = new MultipartRequestMap(request);
        File uploadedFile = map.getFileParameter("file");
        return "File size: " + uploadedFile.length() + ", file path: " + uploadedFile.getAbsolutePath();
    }
}

Step 6: MultipartRequestMap.java – Handle Multipart Parsing

Since Liberty doesn’t provide @FormDataParam-style injection, we manually parse the HttpServletRequest to extract file parts and form fields.

Key highlights:

  • Parses Part objects from the request.
  • Distinguishes between file uploads and text fields.
  • Saves uploaded files temporarily and cleans them up.

public class MultipartRequestMap extends HashMap<String, List<Object>> {

    private static final String DEFAULT_ENCODING = "UTF-8";
    private final String encoding;
    private final String tempLocation;

    public MultipartRequestMap(HttpServletRequest request) {
        this(request, System.getProperty("java.io.tmpdir"));
    }

    public MultipartRequestMap(HttpServletRequest request, String tempLocation) {
        super();
        this.tempLocation = tempLocation;

        String tempEncoding = request.getCharacterEncoding();
        if (tempEncoding == null) {
            try {
                request.setCharacterEncoding(DEFAULT_ENCODING);
                tempEncoding = DEFAULT_ENCODING;
            } catch (UnsupportedEncodingException ex) {
                Logger.getLogger(MultipartRequestMap.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        this.encoding = tempEncoding;

        try {
            for (Part part : request.getParts()) {
                String fileName = part.getSubmittedFileName();
                if (fileName == null) {
                    putMulti(part.getName(), getValue(part));
                } else {
                    processFilePart(part, fileName);
                }
            }
        } catch (IOException | ServletException ex) {
            Logger.getLogger(MultipartRequestMap.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public String getStringParameter(String name) {
        List<Object> list = get(name);
        return (list != null) ? (String) list.get(0) : null;
    }

    public File getFileParameter(String name) {
        List<Object> list = get(name);
        return (list != null) ? (File) list.get(0) : null;
    }

    private void processFilePart(Part part, String fileName) throws IOException {
        File tempFile = new File(tempLocation, fileName);
        tempFile.createNewFile();
        tempFile.deleteOnExit();

        try (
                BufferedInputStream input = new BufferedInputStream(part.getInputStream(), 8192);
                BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(tempFile), 8192)
        ) {
            byte[] buffer = new byte[8192];
            int length;
            while ((length = input.read(buffer)) > 0) {
                output.write(buffer, 0, length);
            }
        } catch (Exception e) {
            Logger.getLogger(MultipartRequestMap.class.getName()).log(Level.SEVERE, null, e);
        }

        part.delete();
        putMulti(part.getName(), tempFile);
    }

    private String getValue(Part part) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(part.getInputStream(), encoding));
        StringBuilder value = new StringBuilder();
        char[] buffer = new char[8192];
        int length;
        while ((length = reader.read(buffer)) > 0) {
            value.append(buffer, 0, length);
        }
        return value.toString();
    }

    private <T> void putMulti(final String key, final T value) {
        List<Object> values = get(key);
        if (values == null) {
            values = new ArrayList<>();
            put(key, values);
        }
        values.add(value);
    }
}

This is a clean, reusable utility for multipart handling without relying on external libraries.

Step 7: Dockerfile – Build and Deploy with Liberty

Here’s a simple Dockerfile to containerize your app with Open Liberty:

FROM open-liberty:24.0.0.9-full-java8-openj9
WORKDIR /config
RUN configure.sh
COPY server.xml /config/server.xml
COPY target/jaxrs-was-demo.war /config/jaxrs-was-demo.war

Build and run:

docker build -t jaxrs-upload-demo .
docker run -p 9080:9080 jaxrs-upload-demo

Summary

The jump from JAX-RS 1.x to 2.x simplifies many things, but file uploads in some runtimes (like Liberty) still require a bit of custom plumbing. This example illustrates:

  • How to configure Liberty for JAX-RS 2.1 and multipart handling
  • Manual parsing of multipart data using servlet APIs
  • Avoiding external libraries and working with native Java EE features

Thank you!


메타데이터
post_id
b83dbc88be2c
slug
handling-multipart-file-uploads-in-jax-rs-2-1-on-websphere-liberty-b83dbc88be2c
url
https://medium.com/@keylearn/handling-multipart-file-uploads-in-jax-rs-2-1-on-websphere-liberty-b83dbc88be2c
canonical_url
https://medium.com/@keylearn/handling-multipart-file-uploads-in-jax-rs-2-1-on-websphere-liberty-b83dbc88be2c
author_url
https://medium.com/@keylearn
status
ok
fetched_at
2026-07-20 00:12:32