Create a multipart/form-data body parser of type Source[ByteString, _]

119 views
Skip to first unread message

Juan Miguel Cejuela

unread,
Oct 27, 2017, 5:26:59 AM10/27/17
to Play Framework
I'm struggling with the following, I want to create a body parser that accepts multipart/form-data and outputs `Source[ByteString, _]`, that is, I want to create a body parser with this signature:

BodyParser[MultipartFormData[Source[ByteString, _]]

The reason is that I want to stream each file (source) later, pretty much as done in https://www.playframework.com/documentation/2.5.x/ScalaBodyParsers#Directing-the-body-elsewhere 

Using Play 2.5.18, I've achieved streaming the whole body as given in the example but I'm struggling with streaming file parts. 

I'm trying something like this:

def handleFilePartAsFile: FilePartHandler[Source[ByteString, _]] = {
   
case FileInfo(partName, filename, contentType) =>
     
Accumulator
       
.source[ByteString]
       
.map { source =>
          println
("Hey, I'm here")
         
FilePart(partName, filename, contentType, source)
       
}
}


def experimental = Action(parse.multipartFormData(handleFilePartAsFile)) { implicit req =>
    val retString
= req.body.files
     
.map { filePart =>
       
//stream source encapsulated in filePart...
        filePart
.toString
     
}
     
.mkString(" -- ")


   
Ok(retString)
}


However, the call never ends. I can see the `println`, but then, somehow the Source or Accumulator's Future never ends.

What am I doing wrong? 

Juan Miguel Cejuela

unread,
Oct 27, 2017, 6:18:13 AM10/27/17
to Play Framework
Update:

when I consume the Source right in the `FilePartHandler` (for example with a WSClient), then handler ends, and my actual goal of streaming the source is achieved.

Either way, I cannot seem to return back to the `Action` controller a `MultipartFormData` of `[Source[ByteString, _]]` -- I did achieve return a simpler `[Source[ByteString, _]]` (without prior consuming) and then process it however I want in the action controller.

Greg Methvin

unread,
Oct 28, 2017, 12:00:18 AM10/28/17
to play-framework
In your original example, you're not consuming the source at all. Per the documentation for Accumulator.source:
Extreme care must be taken when using this accumulator - the source must always be materialized and consumed. If it isn't, this could lead to resource leaks and deadlocks upstream.

Did you try consuming the source or connecting it to a sink inside your action? What exactly are you trying to do with the source?

--
You received this message because you are subscribed to the Google Groups "Play Framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to play-framework+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/play-framework/16fea9d7-64aa-4f37-8cae-b8fc231625ce%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--
Greg Methvin
Tech Lead - Play Framework

Juan Miguel Cejuela

unread,
Oct 29, 2017, 11:28:50 AM10/29/17
to play-fr...@googlegroups.com

Thanks Greg,

Yes, in the second example I do consume the source and works. Concretely, I want to stream files elsewhere upon files uploads.

However, more generally, ideally I would be able to obtain the Source[ByteString, _] of each FilePart as a library-like utility (in a BodyParser), so that later different controllers’ Actions can do different things with those sources. What I would like is a general BodyParser that returns a Source[ByteString, _] and another parser that returns a MultipartFormData of type Source[ByteString, _]

I tested making a BodyParser that returns the whole Source[ByteString, _] of the body, and works (pretty much as in in this doc, but without making a WSClient call, just returning the Source.

I also saw that Play 2.6.x implemented a new BodyParser that returns a ByteString.

Makes sense?


To unsubscribe from this group and stop receiving emails from it, send an email to play-framewor...@googlegroups.com.



--
Greg Methvin
Tech Lead - Play Framework

--
You received this message because you are subscribed to a topic in the Google Groups "Play Framework" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/play-framework/bw3MEedbFhM/unsubscribe.
To unsubscribe from this group and all its topics, send an email to play-framewor...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/play-framework/CAA%3D11HxbQc2zBsXav12vWQ%2B2%2BYO5_4R-i-U3ORO0up_Q%3Dx%2B8MA%40mail.gmail.com.
Reply all
Reply to author
Forward
0 new messages