Fork me on GitHub

Uploading files

Handling multipart/form-data uploads

Ninja offers direct access to file uploads in your controller method. This is accomplished via the Context object and two of its methods: context.isMultipart() to check if we are dealing with a multipart upload and context.getFileItemIterator() to iterate through potential files.

Usually you render a form that links to the controller that will handle the upload. In the following case we are using a route to /upload.

<form method="post" enctype="multipart/form-data" action="/upload">
    Please specify file to upload: <input type="file" name="upfile"><br />
    <input type="submit" value="submit">
</form>

Note that you'll be able to still get your other form values inside a proper DTO : public Result upload(Context context, MyDto formObject, @Param(“file”) File file) { … }.

The manual default way

The controller at /uploadFinish will then handle the upload:

public Result uploadFinish(Context context) throws Exception {

    // Make sure the context really is a multipart context...
    if (context.isMultipart()) {

        // This is the iterator we can use to iterate over the
        // contents of the request.
        FileItemIterator fileItemIterator = context
                .getFileItemIterator();

        while (fileItemIterator.hasNext()) {

            FileItemStream item = fileItemIterator.next();

            String name = item.getFieldName();
            InputStream stream = item.openStream();

            String contentType = item.getContentType();

            if (item.isFormField()) {

                // do something with the form field

            } else {

                // process file as input stream

            }
        }

    }
    
    // We always return ok. You don't want to do that in production ;)
    return Results.ok();

}

The integrated new way

The controller at /uploadFinish can automatically handle the upload, and return either a FileItem, InputStream or File. Using FileItem allows provides access to additional properties, like getFileName() to get the original file name sent by the browser. NOTE: By default, the file provider is set to NoFileItemProvider - in order to use this new way, you need to configure a file provider as described below.

public Result uploadFinish(Context context, @Param("upfile") FileItem upfile) throws Exception {
}

or

public Result uploadFinish(Context context, @Param("upfile") InputStream upfile) throws Exception {
}

or

public Result uploadFinish(Context context, @Param("upfile") File upfile) throws Exception {
}

or

public Result uploadFinish(Context context) throws Exception {
    FileItem upfile = context.getParameterAsFileItem("upfile");
}

In-memory or disk based file ?

Ninja comes with two providers to choose between in-memory and disk file for storing uploaded content:

  • MemoryFileItemProvider, to stores the file bytes into memory
  • DiskFileItemProvider, to stores the file content to disk in a temporary folder, that can be set using the uploads.temp_folder ninja property

In all case, you can limit the size of each file using uploads.max_file_size and the total size of all files using uploads.max_total_size ninja properties.

When using disk base storage, uploaded files are automatically deleted at the end of the request, to prevent file system exhaustion. Because of this, you must copy (or move) the file somewhere else before the end of the request if you want to keep it fo a later usage.

Configure the file provider to use

Ninja let's you configure the file provider to use at different places:

  • in a module, using a bind to configure a default provider
  • in a controller class, to override the default's provider configured in the module
  • in a controller method, to override the class or module provider

By default, the provider is set to NoFileItemProvider, who simply reverts to the manual way of handling file.

To define a provider in a module, simply use a bind:

bind(FileItemProvider.class).to(MemoryFileItemProvider.class)

To define a provider in a controller class and/or method, use an annotation:

@FileProvider(DiskFileItemProvider.class)
@Singleton
public class MyController {
    @FileProvider(MemoryFileItemProvider.class)
    public Result myRouteMethod() {
        // This will use the MemoryFileItemProvider defined at method level
    }
    public Result myOtherRouteMethod() {
        // This will use the DiskFileItemProvider defined at class level
    }
}

Advanced usage

Ninja uses Apache Commons-upload to implement the upload functionality. Therefore you can also refer to their excellent manual for more information at: http://commons.apache.org/proper/commons-fileupload/