如何正确使用ChunkedStream

时间:2012-01-27 00:02:48

标签: java netty

这是我的用例...我有一个上游服务,通过网络发送我的Netty应用数据,并且该数据需要发布到连接到Netty的多个客户端。推送到客户端的数据必须是HTTP" Transfer-Encoding:chunked。"

我找到了ChunkedStream,虽然也许我可以创建PipedInputStreamPipedOutputStream(连接到PipedInputStream)并将ChunkedStream写入渠道。然后,当我从上游服务收到数据时,我可以将数据写入频道的PipedOutputStream,然后将其发送给客户:

在channelConnected中

PipedInputStream in = new PipedInputStream();
PipedOutputStream out = new PipedOutputStream(in);
ctx.getChannel().write( new PersistentChunkedStream(in) );

单独的线程将数据发布到已连接的频道

ChannelBuffer buff = ChannelBuffers.copiedBuffer("FOO",CharsetUtil.UTF_8);
out.write( buff.array() );
channel.get(ChunkedWriteHandler.class).resumeTransfer();

如果有0个字节可用,我必须从ChunkedStream扩展null以返回nextChunk(对于"暂停"没有线程挂起的写入),所以我在写入相关频道的resumeTransfer后拨打PipedOutputStream。当我调试并逐步执行代码时,我可以看到flush的{​​{1}}被调用,它会调用:

ChunkedWriteHandler

我写入Channels.write(ctx, writeFuture, chunk, currentEvent.getRemoteAddress()); 的字节,但客户从未收到过。

HTTP curl

PipedOutputStream,

有什么想法?也许有更好的方法来实现这个目标吗?

3 个答案:

答案 0 :(得分:4)

我想知道为什么你甚至想要使用PipedInputStream / PipedOutputStream。我认为没有你的数据直接调用Channel.write(..)会更干净/更容易。请注意尽可能多地在Channel.write(..)中提交数据,因为这是一项昂贵的操作。

您可以从任何您想要的线程调用Channel.write(..),因为它是线程安全的。

答案 1 :(得分:3)

只是为诺曼提供的答案添加更多内容。

发送任意分块数据时,必须先发送一个新的DefaultHttpResponse(仅限一次):

HttpResponse res = new DefaultHttpResponse();
res.setChunked(true);
res.setHeader(Names.TRANSFER_ENCODING, Values.CHUNKED);
channel.write(res);

然后,只要您想要使用任意块写入通道,请调用:

HttpChunk chunk = new DefaultHttpChunk(ChannelBuffers.wrappedBuffer(str.getBytes(CharsetUtil.UTF_8)));
channel.write(chunk);

答案 2 :(得分:2)

我知道这是一个老问题,但希望这有助于某人。

ChunkedStream并不意味着HTTP Chunking ......这是一个不幸的命名冲突,我能说清楚。分块流只是为了避免将整个项目加载到内存中,实际上ChunkedWriter会在每个块之后调用ChunkedStream来请求更多数据。

事实证明,您可以使用ChunkedStream范例创建一些可以从标准输入流中为您进行HTTP分块的内容。下面的代码实现了ChunkedInput并获取了一个InputStream。它还会自动附加尾随的http块以指示EOF,但是根据ChunkedInput规范只会这样做一次。

public class HttpChunkStream implements ChunkedInput {

private static final int CHUNK_SIZE = 8192;
boolean eof = false;

InputStream data;

HttpChunkStream (InputStream data) {
    this.data= data;
}

byte[] buf = new byte[CHUNK_SIZE];
@Override
public Object nextChunk() throws Exception {
    if (eof)
        return null;        
    int b = data.read(buf);
    if (b==-1) {
        eof=true;
        return new DefaultHttpChunk(ChannelBuffers.EMPTY_BUFFER);           
    }                   
    DefaultHttpChunk c = new DefaultHttpChunk(ChannelBuffers.wrappedBuffer(buf,0,b));       
    return c;
}

@Override
public boolean isEndOfInput() throws Exception {
    return eof;
}

@Override
public boolean hasNextChunk() throws Exception {
    return isEndOfInput()==false;
}

@Override
public void close() throws Exception {
    Closeables.closeQuietly(data);              
}

}