Combinator for specifying a multipart/form-data request
body, typically (but not always) issued from an HTML <form>.
multipart/form-data can't be made into an ordinary content
type for now in servant because it doesn't just decode the
request body from some format but also performs IO in the case
of writing the uploaded files to disk, e.g in /tmp, which is
not compatible with servant's vision of a content type as things
stand now. This also means that MultipartForm can't be used in
conjunction with ReqBody in an endpoint.
The tag type parameter instructs the function to handle data
either as data to be saved to temporary storage (Tmp) or saved to
memory (Mem).
The a type parameter represents the Haskell type to which
you are going to decode the multipart data to, where the
multipart data consists in all the usual form inputs along
with the files sent along through <input type="file">
fields in the form.
One option provided out of the box by this library is to decode to MultipartData.
Example:
type API = MultipartForm Tmp (MultipartData Tmp) :> Post '[PlainText] String
api :: Proxy API
api = Proxy
server :: MultipartData Tmp -> Handler String
server multipartData = return str
where str = "The form was submitted with "
++ show nInputs ++ " textual inputs and "
++ show nFiles ++ " files."
nInputs = length (inputs multipartData)
nFiles = length (files multipartData)
You can alternatively provide a FromMultipart instance for some type of yours, allowing you to regroup data into a structured form and potentially selecting a subset of the entire form data that was submitted.
Example, where we only look extract one input, username, and one file, where the corresponding input field's name attribute was set to pic:
data User = User { username :: Text, pic :: FilePath }
instance FromMultipart Tmp User where
fromMultipart multipartData =
User <$> lookupInput "username" multipartData
<*> fmap fdPayload (lookupFile "pic" multipartData)
type API = MultipartForm Tmp User :> Post '[PlainText] String
server :: User -> Handler String
server usr = return str
where str = username usr ++ "'s profile picture"
++ " got temporarily uploaded to "
++ pic usr ++ " and will be removed from there "
++ " after this handler has run."
Note that the behavior of this combinator is configurable,
by using serveWith from servant-server instead of serve,
which takes an additional Context argument. It simply is an
heterogeneous list where you can for example store
a value of type MultipartOptions that has the configuration that
you want, which would then get picked up by servant-multipart.
Important: as mentionned in the example above, the file paths point to temporary files which get removed after your handler has run, if they are still there. It is therefore recommended to move or copy them somewhere in your handler code if you need to keep the content around.