I would like to know how can I process multipart content using the http4s library.
Imagine a service with the following snippet (the complete gist is here):
case GET -> Root / "form" =>
Ok(
"""|<html>
|<body>
|<form method="post" action="/post" enctype="multipart/form-data">
| <input type="date" name="birthDate" placeholder="birthDate">
| <input type="file" name="dataFile">
| <input type="submit">
|</form></body></html>""".stripMargin).
withContentType(Some(`Content-Type`(`text/html`)))
case req @ POST -> Root / "post" => {
req.decode[Multipart[IO]] { m =>
Ok(
s"""Multipart Data\nParts:${m.parts.length}
|${m.parts.map { case f: Part[IO] => { f.name + ", headers: " + f.headers.mkString(",")} }.mkString("\n")}""".stripMargin)
}
}
If I execute the service and fill the corresponding fields, I obtain an output like the following:
Multipart Data
Parts:2
Some(birthDate), headers: Content-Disposition: form-data; name="birthDate"
Some(dataFile), headers: Content-Disposition: form-data; name="dataFile";
filename="file.pdf",Content-Type: application/pdf
So I know how to obtain information about the parts, which are elements of type Part[IO] and contain headers and body.
What I would like is to know how to process those parts. In this case, for example, I would like to open the file and inform about its length. What is the idiomatic way to do that?
The body of Part[IO] is a Stream[F[_],Byte] which can be processed using methods from the fs2 library.
There are several possibilities, one possibility is to write the stream contents to a file using the io.file.writeAll and io.file.writeAllASync methods.
Another possibility that works for string-based files is to process the stream's contents using the utf8Decode method.
The result could be:
case req @ POST -> Root / "post" => {
req.decode[Multipart[IO]] { m => {
m.parts.find(_.name == Some("dataFile")) match {
case None => BadRequest(s"Not file")
case Some(part) => for {
contents <- part.body.through(utf8Decode).runFoldMonoid
response <- Ok(s"""Multipart Data\nParts:${m.parts.length}
|File contents: ${contents}""".stripMargin)
} yield response
}
}
}
}
}
The previous snippet would return the contents of the file.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With