Netty:如何确定文件是否已完全发送?

时间:2015-10-06 14:40:45

标签: java netty file-transfer

我正在尝试将文件(pdf / bpm / jpg / png)从java服务器应用程序发送到java客户端应用程序。 该文件以块的形式发送。但是,如何知道文件何时完全发送?

我的第一个想法是使用channelReadComplete来通知文件是否已完全发送。但是因为当文件仍在发送时它也会被触发,所以它是无用的。有关如何进行的任何建议吗?

客户端管道:

ChannelPipeline p = ch.pipeline();
p.addLast( "FileChunkHandler", new FileChunkHandler());

服务器管道:

p.addLast( "Encoder", new ObjectEncoder());
p.addLast( "Decoder", new ObjectDecoder( Integer.MAX_VALUE, ClassResolvers.cacheDisabled( null ) ));
p.addLast( "chunkedWriteHandler", new ChunkedWriteHandler());
p.addLast( "FileSenderHandler", new FileSenderHandler());

客户端上的FileChunkHandler:

public class FileChunkHandler extends SimpleChannelInboundHandler<ChunkedFile>{
    private ObjectOutputStream oout = null;

    @Override protected void channelRead0(ChannelHandlerContext ctx, ChunkedFile msg) throws Exception{
        System.out.println( "channelRead0");

        if ( oout == null){
            FileOutputStream out = new FileOutputStream( "/Users/user/Documents/tmp/test/bla.txt" );
            oout = new ObjectOutputStream( out );
        }

        ByteBuf buf = (ByteBuf)msg;
        int numberOfReadableBytes = buf.readableBytes();
        byte[] bytes = new byte[numberOfReadableBytes];
        buf.readBytes( bytes );

        oout.write( bytes, 0, bytes.length);
    }

    @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception{
        System.out.println( "channelReadComplete");
        ctx.fireChannelReadComplete();
    }
}

服务器上的FileSenderHandler:

公共类FileSenderHandler扩展了ChannelInboundHandlerAdapter {

@Override public void channelRegistered(ChannelHandlerContext ctx) throws Exception{

    File file = new File( "/Users/user/Documents/tmp/test/test.txt" );

    RandomAccessFile raf = new RandomAccessFile(file, "r");
    ChannelFuture sendFileFuture = null;

    if (ctx.pipeline().get(SslHandler.class) == null) {
        sendFileFuture = ctx.write(new DefaultFileRegion(raf.getChannel(), 0, raf.length()), ctx.newProgressivePromise());
        ctx.flush();
    } else {
        sendFileFuture = ctx.writeAndFlush( file, ctx.newProgressivePromise());
    }

    sendFileFuture.addListener(new ChannelProgressiveFutureListener() {
        @Override
        public void operationProgressed(ChannelProgressiveFuture future, long progress, long total) {
            if (total < 0) { // total unknown
                System.err.println(future.channel() + " Transfer progress: " + progress);
            } else {
                System.err.println(future.channel() + " Transfer progress: " + progress + " / " + total);
            }
        }

        @Override
        public void operationComplete(ChannelProgressiveFuture future) {
            System.err.println(future.channel() + " Transfer complete.");
        }
    });

    ctx.fireChannelRegistered();
}

2 个答案:

答案 0 :(得分:0)

我解决了这个问题(但我对此并不完全满意)。欢迎进一步的反馈!

我正在创建一个FileStatusHandler,它接收包含长度和名称等文件信息的模型。这些信息作为参数传递给FileDecoder,后者被添加到管道中。

public class FileStatusHandler extends SimpleChannelInboundHandler<FileInformationModel>{

@Override protected void channelRead0(ChannelHandlerContext ctx, FileInformationModel msg) throws Exception{
    String name = "out_" +msg.getName();
    long length = msg.getLength();

    FileDecoder fileDecoder = new FileDecoder( length, name);
    ctx.pipeline().addFirst( CommonClientDefines.Handler.FILE_DECODER, fileDecoder );

    ctx.writeAndFlush( CommonClientDefines.READY_FOR_CONTENT);
}
}

FileDecoder本身负责接收文件内容,并在收到文件后删除处理程序。

public class FileDecoder extends MessageToMessageDecoder<ByteBuf>{

private final String name;
private final long length;
private FileChannel fileChannel;
private long receivedLength = 0;

public FileDecoder(long length, String name) throws IOException{
    this.length = length;
    this.name = name;

    Path directoryPath = Paths.get( System.getProperty( "user.home" ), "Download", "tmp", "test");
    Path filePath = directoryPath.resolve( name);
    fileChannel = FileChannel.open( filePath, StandardOpenOption.CREATE, StandardOpenOption.WRITE );
}

@Override protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception{
    receivedLength += msg.readableBytes();
    fileChannel.write( msg.nioBuffer());

    if( receivedLength >= length){
        fileChannel.close();
        ctx.pipeline().remove( CommonClientDefines.Handler.FILE_DECODER);
    }
}
}

答案 1 :(得分:-1)

让您的客户端和服务器通过套接字进行通信。当您的客户端将文件发送到服务器时,服务器应将“文件已接收消息”发送给客户端。

谷歌“用Java发送套接字消息”,有很多例子。

希望它有所帮助。