Documentation

You are viewing the documentation for the 2.6.0-M4 development release. The latest stable release series is 3.0.x.

§Handling file upload

§Uploading files in a form using multipart/form-data

The standard way to upload files in a web application is to use a form with a special multipart/form-data encoding, which lets you mix standard form data with file attachment data.

Note: The HTTP method used to submit the form must be POST (not GET).

Start by writing an HTML form:

@helper.form(action = routes.ScalaFileUploadController.upload, 'enctype -> "multipart/form-data") {
    
    <input type="file" name="picture">
    
    <p>
        <input type="submit">
    </p>
    
}

Now define the upload action using a multipartFormData body parser:

def upload = Action(parse.multipartFormData) { request =>
  request.body.file("picture").map { picture =>
    val filename = picture.filename
    val contentType = picture.contentType
    picture.ref.moveTo(Paths.get(s"/tmp/picture/$filename"), replace = true)
    Ok("File uploaded")
  }.getOrElse {
    Redirect(routes.ScalaFileUploadController.index).flashing(
      "error" -> "Missing file")
  }
}

The ref attribute give you a reference to a TemporaryFile. This is the default way the multipartFormData parser handles file upload.

Note: As always, you can also use the anyContent body parser and retrieve it as request.body.asMultipartFormData.

At last, add a POST router

POST  /          controllers.ScalaFileUploadController.upload()

§Direct file upload

Another way to send files to the server is to use Ajax to upload the file asynchronously in a form. In this case the request body will not have been encoded as multipart/form-data, but will just contain the plain file content.

In this case we can just use a body parser to store the request body content in a file. For this example, let’s use the temporaryFile body parser:

def upload = Action(parse.temporaryFile) { request =>
  request.body.moveTo(Paths.get("/tmp/picture/uploaded"), replace = true)
  Ok("File uploaded")
}

§Writing your own body parser

If you want to handle the file upload directly without buffering it in a temporary file, you can just write your own BodyParser. In this case, you will receive chunks of data that you are free to push anywhere you want.

If you want to use multipart/form-data encoding, you can still use the default multipartFormData parser by providing a FilePartHandler[A] and using a different Sink to accumulate data. For example, you can use a FilePartHandler[File] rather than a TemporaryFile by specifying an Accumulator(fileSink):

type FilePartHandler[A] = FileInfo => Accumulator[ByteString, FilePart[A]]

def handleFilePartAsFile: FilePartHandler[File] = {
  case FileInfo(partName, filename, contentType) =>
    val perms = java.util.EnumSet.of(OWNER_READ, OWNER_WRITE)
    val attr = PosixFilePermissions.asFileAttribute(perms)
    val path = JFiles.createTempFile("multipartBody", "tempFile", attr)
    val file = path.toFile
    val fileSink = FileIO.toPath(path)
    val accumulator = Accumulator(fileSink)
    accumulator.map { case IOResult(count, status) =>
      FilePart(partName, filename, contentType, file)
    }(ec)
}

def uploadCustom = Action(parse.multipartFormData(handleFilePartAsFile)) { request =>
  val fileOption = request.body.file("name").map {
    case FilePart(key, filename, contentType, file) =>
      file.toPath
  }

  Ok(s"File uploaded: $fileOption")
}

Next: Accessing an SQL database