我可以只关闭 fileoutputstream 但在我通过 Netty 发送文件后通道仍然存在吗?
Can I only close fileoutputstream but the channel is living after I send file by Netty?
几天前,我为如何在不杀死 NettyServer 的情况下访问 NettyClient 发送的文件而苦恼。我在 Whosebug 上找到了解决方案,问题的详细信息是 。解决方法是客户端发送文件后关闭通道,服务端在channelInactive方法中关闭fileoutputstream。主要代码如下
客户端处理程序
public class FileClientHandler extends ChannelInboundHandlerAdapter {
private int readLength = 128;
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
sendFile(ctx.channel());
}
private void sendFile(Channel channel) throws IOException {
File file = new File("C:\Users\xxx\Desktop\1.png");
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
ChannelFuture lastFuture = null;
for (;;) {
byte[] bytes = new byte[readLength];
int readNum = bis.read(bytes, 0, readLength);
if (readNum == -1) { // The end of the stream has been reached
bis.close();
fis.close();
lastFuture = sendToServer(bytes, channel, 0);
if(lastFuture == null) { // When our file is 0 bytes long, this is true
channel.close();
} else {
lastFuture.addListener(ChannelFutureListener.CLOSE);
}
return;
}
lastFuture = sendToServer(bytes, channel, readNum);
}
}
private ChannelFuture sendToServer(byte[] bytes, Channel channel, int length)
throws IOException {
return channel.writeAndFlush(Unpooled.copiedBuffer(bytes, 0, length));
}
}
ServerHandler
public class FileServerHandler extends ChannelInboundHandlerAdapter {
private File file = new File("C:\Users\xxx\Desktop\2.png");
private FileOutputStream fos;
public FileServerHandler() {
try {
if (!file.exists()) {
file.createNewFile();
} else {
file.delete();
file.createNewFile();
}
fos = new FileOutputStream(file);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) {
System.out.println("I want to close fileoutputstream!");
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
ByteBuf buf = (ByteBuf) msg;
try {
buf.readBytes(fos, buf.readableBytes());
} catch (Exception e) {
e.printStackTrace();
} finally {
buf.release(); // Should always be done, even if writing to the file fails
}
}
}
如果现在我需要发送10000张图片,但每张图片都小到1KB。我必须经常关闭然后建立频道。这是一件浪费很多资源的事情。如何只关闭 fileoutputstream 但通道还活着?
这只是一个想法,我还没有测试过,但与其在自己的连接中发送每个文件,不如在您发送的地方启动一个流:
- 要发送的文件数(一次)
- 文件信息和内容(每个文件)
- 文件大小
- 文件名大小
- 文件名
- 文件内容(字节)
客户端看起来像这样:
public void sendFiles(Channel channel, File...files) {
ByteBufAllocator allocator = PooledByteBufAllocator.DEFAULT;
int fileCount = files.length;
// Send the file count
channel.write(allocator.buffer(4).writeInt(fileCount));
// For each file
Arrays.stream(files).forEach(f -> {
try {
// Get the file content
byte[] content = Files.readAllBytes(f.toPath());
byte[] fileName = f.getAbsolutePath().getBytes(UTF8);
// Write the content size, filename and the content
channel.write(allocator.buffer(4 + content.length + fileName.length)
.writeInt(content.length)
.writeInt(fileName.length)
.writeBytes(fileName)
.writeBytes(content)
);
} catch (IOException e) {
throw new RuntimeException(e); // perhaps do something better here.
}
});
// Flush the channel
channel.flush();
}
在服务器端,您需要稍微复杂一些的通道处理程序。我在想一个重播解码器。 (Example here)
在该示例中,解码器将读取所有文件,然后转发到下一个处理程序,该处理程序将接收 Upload 个实例的列表,但您可以将每个上传发送到每个接收到的文件之后的管道,这样你就不会分配那么多内存。但目的是将所有文件以一个流的形式发送,而不是每个文件都必须 connect/disconnect。
几天前,我为如何在不杀死 NettyServer 的情况下访问 NettyClient 发送的文件而苦恼。我在 Whosebug 上找到了解决方案,问题的详细信息是
客户端处理程序
public class FileClientHandler extends ChannelInboundHandlerAdapter {
private int readLength = 128;
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
sendFile(ctx.channel());
}
private void sendFile(Channel channel) throws IOException {
File file = new File("C:\Users\xxx\Desktop\1.png");
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
ChannelFuture lastFuture = null;
for (;;) {
byte[] bytes = new byte[readLength];
int readNum = bis.read(bytes, 0, readLength);
if (readNum == -1) { // The end of the stream has been reached
bis.close();
fis.close();
lastFuture = sendToServer(bytes, channel, 0);
if(lastFuture == null) { // When our file is 0 bytes long, this is true
channel.close();
} else {
lastFuture.addListener(ChannelFutureListener.CLOSE);
}
return;
}
lastFuture = sendToServer(bytes, channel, readNum);
}
}
private ChannelFuture sendToServer(byte[] bytes, Channel channel, int length)
throws IOException {
return channel.writeAndFlush(Unpooled.copiedBuffer(bytes, 0, length));
}
}
ServerHandler
public class FileServerHandler extends ChannelInboundHandlerAdapter {
private File file = new File("C:\Users\xxx\Desktop\2.png");
private FileOutputStream fos;
public FileServerHandler() {
try {
if (!file.exists()) {
file.createNewFile();
} else {
file.delete();
file.createNewFile();
}
fos = new FileOutputStream(file);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) {
System.out.println("I want to close fileoutputstream!");
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
ByteBuf buf = (ByteBuf) msg;
try {
buf.readBytes(fos, buf.readableBytes());
} catch (Exception e) {
e.printStackTrace();
} finally {
buf.release(); // Should always be done, even if writing to the file fails
}
}
}
如果现在我需要发送10000张图片,但每张图片都小到1KB。我必须经常关闭然后建立频道。这是一件浪费很多资源的事情。如何只关闭 fileoutputstream 但通道还活着?
这只是一个想法,我还没有测试过,但与其在自己的连接中发送每个文件,不如在您发送的地方启动一个流:
- 要发送的文件数(一次)
- 文件信息和内容(每个文件)
- 文件大小
- 文件名大小
- 文件名
- 文件内容(字节)
客户端看起来像这样:
public void sendFiles(Channel channel, File...files) {
ByteBufAllocator allocator = PooledByteBufAllocator.DEFAULT;
int fileCount = files.length;
// Send the file count
channel.write(allocator.buffer(4).writeInt(fileCount));
// For each file
Arrays.stream(files).forEach(f -> {
try {
// Get the file content
byte[] content = Files.readAllBytes(f.toPath());
byte[] fileName = f.getAbsolutePath().getBytes(UTF8);
// Write the content size, filename and the content
channel.write(allocator.buffer(4 + content.length + fileName.length)
.writeInt(content.length)
.writeInt(fileName.length)
.writeBytes(fileName)
.writeBytes(content)
);
} catch (IOException e) {
throw new RuntimeException(e); // perhaps do something better here.
}
});
// Flush the channel
channel.flush();
}
在服务器端,您需要稍微复杂一些的通道处理程序。我在想一个重播解码器。 (Example here)
在该示例中,解码器将读取所有文件,然后转发到下一个处理程序,该处理程序将接收 Upload 个实例的列表,但您可以将每个上传发送到每个接收到的文件之后的管道,这样你就不会分配那么多内存。但目的是将所有文件以一个流的形式发送,而不是每个文件都必须 connect/disconnect。