netty 中的 http 分块文件传输立即完成
http chunked file transfer in netty finished immediately
我正在编写一个 http 文件服务器,使用 netty
在多线程中下载文件。仅使用 HttpServerCodec()
时,一切正常,但 OOM:直接缓冲内存错误。然后我转向 ChunkedWriteHandler()
处理程序。
但问题是,浏览器(新版 Edge)要么说“无法下载文件”,要么下载大小为零的文件。我对此一无所知,需要帮助。
日志显示传输过程立即完成,没有任何时间成本。
[main] INFO Main - Pick up path C:/Temp
[main] INFO dd.oliver.htp.HtpServer - Server start at 2333
[nioEventLoopGroup-3-1] INFO dd.oliver.htp.RequestHandler - Request C:/Temp/d.test.oliverdd
[nioEventLoopGroup-3-1] INFO dd.oliver.htp.RequestHandler - [id: 0xe5ce2ec6, L:/0:0:0:0:0:0:0:1:2333 - R:/0:0:0:0:0:0:0:1:63040] Transfer complete.
这是我的代码,引用了netty example。
这是 ChannelInitializer:
class HtpChannelInitializer(val basePath: String) : ChannelInitializer<SocketChannel>() {
override fun initChannel(ch: SocketChannel) {
ch.pipeline().addLast("HttpCodec", HttpServerCodec())
ch.pipeline().addLast("HttpAggregator", HttpObjectAggregator(65536))
ch.pipeline().addLast("HttpChunked", ChunkedWriteHandler())
ch.pipeline().addLast("RequestHandle", RequestHandler(basePath))
}
}
这是 RequestHandler:
import io.netty.channel.*
import io.netty.handler.codec.http.*
import io.netty.handler.codec.http.HttpVersion.HTTP_1_0
import io.netty.handler.stream.ChunkedFile
import org.slf4j.LoggerFactory
import java.io.File
import java.io.RandomAccessFile
private val logger = LoggerFactory.getLogger(RequestHandler::class.java)
class RequestHandler_test(val basePath: String) : SimpleChannelInboundHandler<HttpRequest>() {
...
override fun channelReadComplete(ctx: ChannelHandlerContext) {
ctx.flush()
}
override fun channelRead0(ctx: ChannelHandlerContext, msg: HttpRequest) {
val path = basePath + msg.uri() // msg.uri() example: / or /a/b or /a/b/c.txt
logger.info("Request $path")
val file = File(path)
if (file.isFile) {
val rfile = RandomAccessFile(file, "r")
// Line
val response = DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK)
// Headers
response.headers().set("Accept-Ranges", "bytes")
response.headers().set("Content-Disposition", "attachment; filename=\"${file.name}\"")
response.headers().set("Content-Type", "application/octet-stream")
response.headers().set("Content-Length", "${rfile.length()}")
if (!(msg.headers().contains("Connection") && msg.headers().get("Connection") == "keep-alive")) {
response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
} else if (msg.protocolVersion() == HTTP_1_0) {
response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
}
// Content
// response.content().writeBytes(rfile.channel, 0L, rfile.length().toInt())
// ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE)
ctx.write(response)
val sendFileFuture = ctx.write(
HttpChunkedInput(ChunkedFile(rfile, 0, rfile.length(), 8192)),
ctx.newProgressivePromise()
)
sendFileFuture.addListener(object : ChannelProgressiveFutureListener {
override fun operationProgressed(
future: ChannelProgressiveFuture,
progress: Long,
total: Long
) {
if (total < 0) { // total unknown
logger.info(future.channel().toString() + " Transfer progress: " + progress)
} else {
logger.info(
future.channel().toString() + " Transfer progress: " + progress + " / " + total
)
}
}
override fun operationComplete(future: ChannelProgressiveFuture) {
logger.info(future.channel().toString() + " Transfer complete.")
}
})
if (!(msg.headers().contains("Connection") && msg.headers().get("Connection") == "close")) {
sendFileFuture.addListener(ChannelFutureListener.CLOSE)
}
}
}
...
}
知道了! post 遇到了同样的问题。是 DefaultFullHttpResponse
弄错了,改成 DefaultHttpResponse
.
就一切正常了
我正在编写一个 http 文件服务器,使用 netty
在多线程中下载文件。仅使用 HttpServerCodec()
时,一切正常,但 OOM:直接缓冲内存错误。然后我转向 ChunkedWriteHandler()
处理程序。
但问题是,浏览器(新版 Edge)要么说“无法下载文件”,要么下载大小为零的文件。我对此一无所知,需要帮助。
日志显示传输过程立即完成,没有任何时间成本。
[main] INFO Main - Pick up path C:/Temp
[main] INFO dd.oliver.htp.HtpServer - Server start at 2333
[nioEventLoopGroup-3-1] INFO dd.oliver.htp.RequestHandler - Request C:/Temp/d.test.oliverdd
[nioEventLoopGroup-3-1] INFO dd.oliver.htp.RequestHandler - [id: 0xe5ce2ec6, L:/0:0:0:0:0:0:0:1:2333 - R:/0:0:0:0:0:0:0:1:63040] Transfer complete.
这是我的代码,引用了netty example。
这是 ChannelInitializer:
class HtpChannelInitializer(val basePath: String) : ChannelInitializer<SocketChannel>() {
override fun initChannel(ch: SocketChannel) {
ch.pipeline().addLast("HttpCodec", HttpServerCodec())
ch.pipeline().addLast("HttpAggregator", HttpObjectAggregator(65536))
ch.pipeline().addLast("HttpChunked", ChunkedWriteHandler())
ch.pipeline().addLast("RequestHandle", RequestHandler(basePath))
}
}
这是 RequestHandler:
import io.netty.channel.*
import io.netty.handler.codec.http.*
import io.netty.handler.codec.http.HttpVersion.HTTP_1_0
import io.netty.handler.stream.ChunkedFile
import org.slf4j.LoggerFactory
import java.io.File
import java.io.RandomAccessFile
private val logger = LoggerFactory.getLogger(RequestHandler::class.java)
class RequestHandler_test(val basePath: String) : SimpleChannelInboundHandler<HttpRequest>() {
...
override fun channelReadComplete(ctx: ChannelHandlerContext) {
ctx.flush()
}
override fun channelRead0(ctx: ChannelHandlerContext, msg: HttpRequest) {
val path = basePath + msg.uri() // msg.uri() example: / or /a/b or /a/b/c.txt
logger.info("Request $path")
val file = File(path)
if (file.isFile) {
val rfile = RandomAccessFile(file, "r")
// Line
val response = DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK)
// Headers
response.headers().set("Accept-Ranges", "bytes")
response.headers().set("Content-Disposition", "attachment; filename=\"${file.name}\"")
response.headers().set("Content-Type", "application/octet-stream")
response.headers().set("Content-Length", "${rfile.length()}")
if (!(msg.headers().contains("Connection") && msg.headers().get("Connection") == "keep-alive")) {
response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
} else if (msg.protocolVersion() == HTTP_1_0) {
response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
}
// Content
// response.content().writeBytes(rfile.channel, 0L, rfile.length().toInt())
// ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE)
ctx.write(response)
val sendFileFuture = ctx.write(
HttpChunkedInput(ChunkedFile(rfile, 0, rfile.length(), 8192)),
ctx.newProgressivePromise()
)
sendFileFuture.addListener(object : ChannelProgressiveFutureListener {
override fun operationProgressed(
future: ChannelProgressiveFuture,
progress: Long,
total: Long
) {
if (total < 0) { // total unknown
logger.info(future.channel().toString() + " Transfer progress: " + progress)
} else {
logger.info(
future.channel().toString() + " Transfer progress: " + progress + " / " + total
)
}
}
override fun operationComplete(future: ChannelProgressiveFuture) {
logger.info(future.channel().toString() + " Transfer complete.")
}
})
if (!(msg.headers().contains("Connection") && msg.headers().get("Connection") == "close")) {
sendFileFuture.addListener(ChannelFutureListener.CLOSE)
}
}
}
...
}
知道了! DefaultFullHttpResponse
弄错了,改成 DefaultHttpResponse
.