Testing File Uploading error: Missing boundary header

26 Nov 2017

Here's a reference Stackoverflow question.

It happens when I try to test a controller with FakeRequest(POST, "/").withBody(MultipartFormData[File](Map.empty, Seq.empty, Seq.empty)). The error is: Missing boundary header.

I remember I encountered this same error in 2015.

Long story short: Writable for MultipartFormData is broken. It doesn't define a boundary. Here's the Writable that works:

import java.nio.file.{Files, Paths} import play.api.http.{HeaderNames, Writeable} import play.api.libs.Files.TemporaryFile import play.api.mvc.MultipartFormData.FilePart import play.api.mvc.{Codec, MultipartFormData} object MultipartFormDataWritable { val boundary = "--------ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" def formatDataParts(data: Map[String, Seq[String]]) = { val dataParts = data.flatMap { case (key, values) => values.map { value => val name = s""""$key"""" s"--$boundary\r\n${HeaderNames.CONTENT_DISPOSITION}: form-data; name=$name\r\n\r\n$value\r\n" } }.mkString("") Codec.utf_8.encode(dataParts) } def filePartHeader(file: FilePart[TemporaryFile]) = { val name = s""""${file.key}"""" val filename = s""""${file.filename}"""" val contentType = file.contentType.map { ct => s"${HeaderNames.CONTENT_TYPE}: $ct\r\n" }.getOrElse("") Codec.utf_8.encode(s"--$boundary\r\n${HeaderNames.CONTENT_DISPOSITION}: form-data; name=$name; filename=$filename\r\n$contentType\r\n") } val singleton = Writeable[MultipartFormData[TemporaryFile]]( transform = { form: MultipartFormData[TemporaryFile] => formatDataParts(form.dataParts) ++ form.files.flatMap { file => val fileBytes = Files.readAllBytes(Paths.get(file.ref.file.getAbsolutePath)) filePartHeader(file) ++ fileBytes ++ Codec.utf_8.encode("\r\n") } ++ Codec.utf_8.encode(s"--$boundary--") }, contentType = Some(s"multipart/form-data; boundary=$boundary") ) }

Update: this isn't true anymore.

The reason I got the error was because I didn't initialize the FakeRequest correctly. I was an idiot.

Here's the correct version:

val filePart = FilePart[TemporaryFile]("image", "name.jpg", None, null) val req = FakeRequest().withBody( MultipartFormData[TemporaryFile]( dataParts = Map.empty, files = Seq(filePart), badParts = Seq.empty))

The Writable for MultipartFormData has been added a while ago.

My faith in Playframework has been restored. The bugs are getting fixed. Yay!