File Upload with Swagger using RESTEasy in Java

3,167 views
Skip to first unread message

Max Magee

unread,
Mar 12, 2014, 11:40:42 AM3/12/14
to swagger-sw...@googlegroups.com
From the group questions and response, I can only find examples of file uploads in Scala and Jersey. It appears as though both depend on the Jersey com.sun.jersey.multipart.FormDataParam annotation. RESTEasy allows you to deserialize a form submission directly to a POJO by annotating the property in the model object itself like so:

@ApiModel(value="An InformaCast Message object")
public class RestMessage {

    private static final Logger log = Logger.getLogger(RestMessage.class);

    private Integer messageId;

    @FormParam("mainAudio")
    @PartType("application/octet-stream")
    @ApiModelProperty(value="Main Audio", required=true, allowableValues="null, 8-bit uLaw audio file")
    private File mainAudio;
...

I don't think these annotations are being recognized, since in the api-docs json, that field is described as:
                "mainAudio": {
                    "$ref": "File",
                    "description": "Main Audio",
                    "enum": [
                        "null",
                        " 8-bit uLaw audio file"
                    ]
                },
Whereas, in the petshop example, its file uploads contained type: "File"
  • {
    • name"file",
    • description"file to upload",
    • requiredfalse,
    • type"File",
    • paramType"body"
    }

Some additional annotations might be helpful for defining specific components of objects that should be files.

A little bit more detail about how the submission can work: Using the PostMan REST Client I can specify that the POST submission is mime-type "multipart/form-data".  This then gives me the ability to POST Key/Value pairs of type Text or of type File.  In the event that it's text, I enter the field name that corresponds to the POJO's property name for the key, and I enter the value that I want to submit (like message text, for example) in the value field.  For files, I do exactly the same thing, only submitting a file object as the value corresponding to a field name in the POJO that can receive an Octet-Stream.  


So I guess for full RESTEasy specification, Swagger-core needs to inspect the model objects deserialized by RESTEasy targets to collect any PartType annotations OR add something like a "type" parameter to the ApiModelProperty (that results in api-doc output similar to the output from the Jersey FormDataParams).

The second piece to this solution, though, requires a bit of sophistication that I think is missing currently from Swagger-UI, and that is the ability to upload multiple parts in a POST or PUT request- including Files and text both- all in one request, as PostMan does:

I haven't worked with CoffeeScript before, but I'd be willing to give it a shot if this functionality doesn't currently exist (and I haven't seen any evidence of it anywhere, but it's pretty tough to find all of the features that Swagger encompasses, so I may have missed it).

tony tam

unread,
Mar 12, 2014, 1:03:17 PM3/12/14
to swagger-sw...@googlegroups.com
Hi, I don't believe you'll be able to have a file inside an object.  For swagger, you'll have to accept the multipart-form post, and get just the file + additional form params.

Max Magee

unread,
Mar 24, 2014, 12:07:33 PM3/24/14
to swagger-sw...@googlegroups.com
Hi Tony,

Thanks for your help with this.

I have tried to implement it the way you suggested (handling the multipart within the method itself)- it looks like this:
    @PUT
    @Path("/{messageId}/icons")
    @ApiOperation(value="Update display icon")
    @RolesAllowed({"messageEditor", "messageAdmin"})
    @Consumes("multipart/form-data")
    @Produces("application/json")
    public RestMessage updateIcons(@ApiParam(value="Icon file upload", required=true) MultipartFormDataInput formInput, @PathParam("messageId") int messageId) throws Exception {
        log.debug("in updateIcon(Multipart) for messageId " + messageId);
        Map<String, List<InputPart>> uploadForm = formInput.getFormDataMap();
        List<InputPart> inputParts = uploadForm.get("uploadFile");
        String fileName = "";
        byte[] bytes = new byte[0];
        for (InputPart inputPart: inputParts) {
            MultivaluedMap<String, String> header = inputPart.getHeaders();
            fileName = getFileName(header);
            InputStream inputStream = inputPart.getBody(InputStream.class, null);
            bytes = IOUtils.toByteArray(inputStream);
        }

        return helper.updateIcons(messageId, bytes, fileName, user);
}


Unfortunately, this still doesn't produce the desired file upload input (it works for retrieving the data, but doesn't work for swagger).  It would be really helpful for the @APIParam annotation to have something like a paramType option in order to override the default (body):
  • {
    • name"body"
    • description"Icon file upload"
    • requiredtrue
    • allowMultiplefalse
    • type"FileUploadForm"
    • paramType"body"
    }

As a work-around, I can get an additional file selector to show up if I use the following annotation:
@ApiImplicitParams(@ApiImplicitParam(name = "uploadFile", value="", dataType = "file", paramType = "file"))

From that, I can retrieve the uploadFile that is submitted through the Swagger UI just as I would like to, but the default body input field still shows up.  At this point, we're just setting the description in the body field to "*** DO NOT USE THIS ***" so that the user isn't quite as confused, but we'd love to be able to hide that field- we've tried the access="internal" as referenced on https://github.com/wordnik/swagger-core/issues/127, but it looks like the location of the source has changed, and that it hasn't been working for other users either.

Because we're using RESTEasy, swagger has been a little more difficult to implement fully- this is the last hurdle before we are able to say we've got it working the way we want it to, but we haven't been able to find a good solution to it.

-Max
Reply all
Reply to author
Forward
0 new messages