Search code examples
javarestjersey-2.0multipart

Jersey throws ParseException when uploading file in multipart/form-data request


I am trying to send an mp3 file along with some metadata to my Jersey REST service. For this I am trying to use multipart/form-data content type, however I always get java.text.ParseException: Next event is not a Separator when I send the request. The whole response:

{"restResponse":{
"responseCode":"INVALID_PARAMETER",
"i18nMessage":"Invalid Parameter: Next event is not a Separator",
"responseDescription":"The request contains an invalid parameter"}}

If I remove the mp3 file from the request, it is working properly so I assume there is a problem with how my request is constructed. I am using Paw, which generates request like this:

POST /rest/myapi/recording/multipart HTTP/1.1
Accept: application/json
Accept-Language: en-US
Content-Type: multipart/form-data; boundary=EI6FArOacJKf5JCY5BAA2sbl2IAfN8ty
Cookie: JSESSIONID=a5f5cfa7329142158766a6182645; JSESSIONIDSSO=BAFA3371F6D14A179B0BA6216DD6C119
Host: localhost:8181
Connection: close
User-Agent: Paw/2.2.2 (Macintosh; OS X/10.10.5) GCDHTTPRequest
Content-Length: 44504

--EI6FArOacJKf5JCY5BAA2sbl2IAfN8ty
Content-Disposition: form-data; name="queue"

qMultipart
--EI6FArOacJKf5JCY5BAA2sbl2IAfN8ty
Content-Disposition: form-data; name="datetime"

20151029-021807
--EI6FArOacJKf5JCY5BAA2sbl2IAfN8ty
Content-Disposition: form-data; name="recording"; filename="test4_multiform.mp3"
Content-Type: audio/mpeg

ID3ETT2test4_multiformCOMengiTunPGAP0TENiTunes 12.3.0.44COMhengiTunNORM 00000152 00000152 000013BE 000013BE 0000023E 0000023E 00003F10 00003F10 000002A7 000002A7COMengiTunSMPB 00000000 00000210 00000A30 0000000000015BC0 00000000 0
--- the rest of the recording ---

The serverside handler:

    @Path(value = "multipart")
    @POST
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @Produces(MediaType.APPLICATION_JSON)
    public Response receiveFileAndMetadataAsMultipart(
            @FormDataParam(RECORDING) InputStream recordingIinputStream,
            @FormDataParam(RECORDING) FormDataContentDisposition callRecordingDispositionHeader,
            @FormDataParam(QUEUE) String queue,
            @FormDataParam(DATETIME) String datetime) throws JSONException, ParseException, IOException {

        //do stuff with my recording and metadata
        //...

        JSONObject response = new JSONObject();
        return Response.status(Response.Status.OK).entity(response).build();
    }

When I try to send just the file and the content type is not multipart/form-data but application/octet-stream, then it works fine. (I mean not with this handler but one which accepts octet-stream) although the request is very similar. I'll post the request and code for octet stream as well, maybe it helps:

PUT /rest/myapi/recording HTTP/1.1
Accept: application/json
Accept-Language: en-US
Content-Type: application/octet-stream
Cookie: JSESSIONID=a5f5cfa7329142158766a6182645; JSESSIONIDSSO=BAFA3371F6D14A179B0BA6216DD6C119
Host: localhost:8181
Connection: close
User-Agent: Paw/2.2.2 (Macintosh; OS X/10.10.5) GCDHTTPRequest
Content-Length: 55414

ID3BTT2test3_2callsCOMengiTunPGAP0TENiTunes 12.3.0.44COMhengiTunNORM 0000038A 0000038A 00001B3D 00001B3D
--- the rest of the recording ---

Method:

    @Path(value = "recording")
    @PUT
    @Consumes(MediaType.APPLICATION_OCTET_STREAM)
    @Produces(MediaType.APPLICATION_JSON)
    public Response receiveRecording(InputStream callRecordingInputStream) throws IOException, JSONException {

        //do stuff with my recording
        //...

        JSONObject response = new JSONObject();              
        return Response.status(Response.Status.OK).entity(response).build();
    }

Does anyone have any idea what is wrong? I found this approach with multipart on several places online so I guess it should work doing it like that.

Btw, I also tried removing all of the metadata and just send the recording but the problem was the same.


Solution

  • I have found the issue thanks to @peeskillet's comment, so just in case this exact same thing happens to someone else:

    I had a bad import for FormDataContentDisposition.

    Wrong:

    import com.sun.jersey.core.header.FormDataContentDisposition;
    

    Correct:

    import org.glassfish.jersey.media.multipart.FormDataContentDisposition;