Is there a way to handle file upload ?

classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

Is there a way to handle file upload ?

fblanvil
Hi,

Is there a way to handle file uploads with ratpack with an <input type="file" ?

Also related, is there a clean way to bypass the 65536 bytes post size limitation ? (found this limit hardcoded in RatpackChannelInitializer).

Thanks a lot.
Reply | Threaded
Open this post in threaded view
|

Re: Is there a way to handle file upload ?

Luke Daley
Administrator
Hi,

There's currently no built in support for file handling unfortunately. I've raised an issue for this.

https://github.com/ratpack/ratpack/issues/54

Would you be interested in helping to add it?

Reply | Threaded
Open this post in threaded view
|

Re: Is there a way to handle file upload ?

fblanvil
Thanks for your response.

I'm currently trying to hack something like :

        handler('publish-image') {
            methods.get {
                get(TemplateRenderer).render "upload-image.html"
            }.post {
                DefaultFullHttpRequest nettyRequest = request.nettyRequest
                HttpDataFactory factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE)
                HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(factory, nettyRequest)
                HttpContent chunk = (HttpContent) nettyRequest;
                decoder.offer(chunk)
                String resp = ""
                while (decoder.hasNext()) {
                    InterfaceHttpData data = decoder.next()
                    switch (data.httpDataType) {
                        case InterfaceHttpData.HttpDataType.Attribute:
                            Attribute attribute = (Attribute) data
                            resp += "${attribute.name}: ${attribute.value}\n"
                            break
                        case InterfaceHttpData.HttpDataType.FileUpload:
                            FileUpload fileUpload = (FileUpload) data
                            if (fileUpload.completed) {
                                resp += "file : ${data.name} (${fileUpload.length()} bytes)\n"
                            }
                    }
                }
                response.send("post")
            }.send()
        }
from https://github.com/netty/netty/blob/master/example/src/main/java/io/netty/example/http/upload/HttpUploadServerHandler.java

But it doesn't work very well for now.

I'm absolutely not experienced in web servers, except to drop my war in; However I would be interested to try to help on this (with no guarantee!).

I imagine that org.ratpackframework.bootstrap.internal.NettyHandlerAdapter would be a place to start, but I would be grateful to have some directions and hints here. Also I don't really understand how Guice ties the components up.

Cheers
Reply | Threaded
Open this post in threaded view
|

Re: Is there a way to handle file upload ?

fblanvil
For the hacky records, this works:

        handler('publish-image') {
            methods.get {
                get(TemplateRenderer).render "upload-image.html"
            }.post {
                DefaultFullHttpRequest nettyRequest = request.nettyRequest
                HttpDataFactory factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE)
                HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(factory, nettyRequest)
                HttpContent chunk = (HttpContent) nettyRequest;
                decoder.offer(chunk)
                Map <String, Object> multipartForm = [:]
                try {
                    while (decoder.hasNext()) {
                        InterfaceHttpData data = decoder.next()
                        switch (data.httpDataType) {
                            case InterfaceHttpData.HttpDataType.Attribute:
                                Attribute attribute = (Attribute) data
                                multipartForm."${attribute.name}" = attribute.value
                                break
                            case InterfaceHttpData.HttpDataType.FileUpload:
                                FileUpload fileUpload = (FileUpload) data
                                if (fileUpload.completed) { // which is right because of the HttpObjectAggregator
                                    File file = fileUpload.file
                                    multipartForm."${data.name} (${file.name})" = file
                                }
                        }
                    }
                } catch (HttpPostRequestDecoder.EndOfDataDecoderException e) {
                    // This ugly empty catch is hit everytime and is therefore necessary
                }
                response.send(multipartForm.toString())
            }.send()
        }
For this hack to work for files larger than 65536 bytes, this ratpack code chunk needs to be changed accordingly:
(org.ratpackframework.bootstrap.internal.RatpackChannelInitializer)
    pipeline.addLast("decoder", new HttpRequestDecoder());
    pipeline.addLast("aggregator", new HttpObjectAggregator(65536));
    pipeline.addLast("encoder", new HttpResponseEncoder());
    pipeline.addLast("chunkedWriter", new ChunkedWriteHandler());
Reply | Threaded
Open this post in threaded view
|

Re: Is there a way to handle file upload ?

Luke Daley
Administrator
That's great stuff :)

We should put this behind some kind of method on the request object. What kind of API do you suggest?

I'm not quite sure how to make that max size configurable at this minute. I'll work something out soon though.
Reply | Threaded
Open this post in threaded view
|

Re: Is there a way to handle file upload ?

fblanvil
Without much thoughts, I was thinking for the ease of use to integrate the files directly in the form object.
But the signature would have to change from
Map<String, List<String>> getForm();
to
Map<String, List<Object>> getForm(); // to accept File objects.
which might be not pleasant for Java users.

I didn't investigate about that but I suppose this approach has some problems since it doesn't seems to be the usual way to do. What do you think ?

Another way would be to add a
Map<String, List<File>> getFiles(); // method to the Request interface.

I'll be pretty busy by the end of the week, but I should be able to work on this during next week :-)
Reply | Threaded
Open this post in threaded view
|

Re: Is there a way to handle file upload ?

Luke Daley
Administrator
Really sorry, I missed the notification about your response.

I think we want a separate method, like…

RequestBody getBody()

Where RequestBody has a method…

FileUpload file(String name)

And FileUpload is something like…

interface FileUpload {
  String getContentType()
  File getFile()
}