out-of-memorynettyhttpserverlarge-file-upload

Netty HTTP-server, big file upload, OutOfMemoryError


First of all sorry for my crooked English :)

I have some problems during writing Netty http server that sends/receives large files (~2Gb). And I would very appreciate anybody help or explanation.

My client application (web-browser) sends files via XMLHttpRequest like this:

            var sampleFile = document.getElementById("sampleFile").files[0];  //chosen file by <input type="file" .../>
            var xhr = new XMLHttpRequest();       
            xhr.open("POST","http://127.0.0.1:8091/upload/some_file_name.txt", true);        
            xhr.send(sampleFile);

Server side is:

class WebSocketServer:

             ServerBootstrap bootstrapHttp = new ServerBootstrap(
            new NioServerSocketChannelFactory(
                              Executors.newCachedThreadPool(),
                              Executors.newCachedThreadPool()));

    bootstrapHttp.setPipelineFactory(new ChannelPipelineFactory() {
        public ChannelPipeline getPipeline() {
            ChannelPipeline pipeline = pipeline();

              pipeline.addLast("decoder", new HttpRequestDecoder());
              pipeline.addLast("aggregator", new HttpChunkAggregator(1024*1024*1024));
              pipeline.addLast("encoder", new HttpResponseEncoder());          
              pipeline.addLast("deflater", new HttpContentCompressor());               
              pipeline.addLast("handler", new HttpRequestServerHandler());
              return pipeline;
        }
    });     
    bootstrapHttp.bind(new InetSocketAddress(port));

class HttpRequestServerHandler:

public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception    {

             Object msg = e.getMessage();

    if (msg instanceof HttpRequest) {

        HttpRequest req = (HttpRequest)msg;             

        if (req.getMethod() != POST) {          
            return;             
        }                   

        if (decodedURI.startsWith(UPLOAD_FILE_PATH)) {

            HttpRequest request = (HttpRequest) e.getMessage(); 

            RandomAccessFile raf = new RandomAccessFile("foobar.tmp", "rw");                
            ChannelBuffer buf = request.getContent();                   
            FileChannel fChannel = raf.getChannel();
            Channel msgChannel= e.getChannel();             

            fChannel.write( buf.toByteBuffer() );                               
            raf.close();
            fChannel.close();
            msgChannel.close();
        }
    }

When I send middle size file everything works wonderfully. The problem is with large files (>300Mb). After some time processing exception occures:

java.lang.OutOfMemoryError: Java heap space at org.jboss.netty.buffer.HeapChannelBuffer.(HeapChannelBuffer.java:47) at org.jboss.netty.buffer.BigEndianHeapChannelBuffer.(BigEndianHeapChannelBuffer.java:39) at org.jboss.netty.buffer.ChannelBuffers.buffer(ChannelBuffers.java:139) at org.jboss.netty.buffer.HeapChannelBufferFactory.getBuffer(HeapChannelBufferFactory.java:73) at org.jboss.netty.buffer.DynamicChannelBuffer.ensureWritableBytes(DynamicChannelBuffer.java:84) at org.jboss.netty.buffer.DynamicChannelBuffer.writeBytes(DynamicChannelBuffer.java:239) at org.jboss.netty.buffer.AbstractChannelBuffer.writeBytes(AbstractChannelBuffer.java:457) at org.jboss.netty.buffer.AbstractChannelBuffer.writeBytes(AbstractChannelBuffer.java:450) at org.jboss.netty.handler.codec.http.HttpChunkAggregator.messageReceived(HttpChunkAggregator.java:140) at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:302) at org.jboss.netty.handler.codec.replay.ReplayingDecoder.unfoldAndFireMessageReceived(ReplayingDecoder.java:522) at org.jboss.netty.handler.codec.replay.ReplayingDecoder.callDecode(ReplayingDecoder.java:506) at org.jboss.netty.handler.codec.replay.ReplayingDecoder.messageReceived(ReplayingDecoder.java:443) at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:274) at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:261) at org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:351)

It even don't reach my messageReceived handler. As I can assume some inner ChannelBuffer is overflowed. I've tried to increase HttpChunkAggregator(1024*1024*1024) parameter. However, It would not helped. I see just one solution - to split file on the client side (using html5), send these chunks, and paste them together on server. But its seems to quite complex. Is there any easier way to fix it (in Netty scope)?

Thank you! Best regards.


Solution

  • I think you should not use HttpChunkAggregator.

    This means that you will have to manually handle HTTP chunks.

    See Netty file upload example for more details.