javajax-rsjava-ee-7

JaxRs Client 2.0 file upload


I am looking for a way to upload a file (multipart form data) with the jax-rs client only using the standard apis.

The closest solution I could find was this https://www.adam-bien.com/roller/abien/entry/sending_an_inputstream_to_jax but that does not produce a MultiPart request.

All I have found are specific solutions to a concrete client implementation like RestEASY (https://mkyong.com/webservices/jax-rs/file-upload-example-in-resteasy/) or Jersey (https://howtodoinjava.com/jersey/jersey-file-upload-example/) or skipping JaxRs completely and using HttpClient (FileUpload with JAX-RS), but as the implementation provider will most likely change in the future my best bet would be to stay strictly to the standard API.

Is there something, maybe with JaxRs 2.1 or at least a cross implementation solution? A library providing a MessageBodyWriter or Feature that has just to be registered would be nice too.


Solution

  • As far as I know, there is no direct support for uploading multipart form data in the JAX-RS client. But by using the MultipartEntityBuilder from the Apache HTTP Client within a JAX-RS StreamingOutput, it is possible to do a multipart form data upload from a JAX-RS client with only some lines of code:

    import java.io.File;
    import java.util.UUID;
    
    import javax.ws.rs.client.*;
    import javax.ws.rs.core.*;
    
    import org.apache.http.HttpEntity;
    import org.apache.http.entity.ContentType;
    import org.apache.http.entity.mime.MultipartEntityBuilder;
    
    public class MultipartJaxRsClient {
    
        public static void main(String[] args) {
    
            // prepare MultipartEntityBuilder:
            String boundary = "-----------" + UUID.randomUUID().toString().replace("-", "");
            MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create().setBoundary(boundary);
    
            // add file(s):
            String multipartFieldName = "file1";
            File fileToUpload = new File("test.pdf");
            ContentType contentType = ContentType.create("application/pdf");
            entityBuilder.addBinaryBody(multipartFieldName, fileToUpload, contentType, fileToUpload.getName()).build();
            // ... add more files ...
    
            // wrap HttpEntity in StreamingOutput:
            HttpEntity entity = entityBuilder.build();
            StreamingOutput streamingOutput = out -> entity.writeTo(out);
    
            // use StreamingOutput in JAX-RS post request:
            WebTarget target = ClientBuilder.newClient().target("http://server/path/to/upload");
            String multipartContentType = MediaType.MULTIPART_FORM_DATA + "; boundary=\"" + boundary + "\"";
            Response response = target.request().post(Entity.entity(streamingOutput, multipartContentType));
            System.out.println("HTTP Status: " + response.getStatus());
        }
    }
    

    The above code was tested with Apache HTTP Client 4.5.13 and Apache CXF 3.5.4 JAX-RS client:

    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpmime</artifactId>
        <version>4.5.13</version>
    </dependency>
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-rs-client</artifactId>
        <version>3.5.4</version>
    </dependency>