蓝桉云顶

Good Luck To You!

如何搭建Netty图片服务器?

Netty图片服务器搭建涉及配置ServerHandler、实现自定义编码器、完成ServerBootstrap配置以及启动服务等步骤。

Netty 图片服务器搭建

Netty 是一个基于 Java 的异步事件驱动的网络应用框架,旨在快速开发可维护的高性能协议服务器和客户端,本文将详细介绍如何使用 Netty 搭建一个能够处理图片上传和下载请求的 HTTP 服务器。

一、准备工作

我们需要创建一个 Maven 项目,并在pom.xml 文件中添加 Netty 依赖,这里我们使用 Netty 的稳定版本 4.1.52.Final:

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.52.Final</version>
</dependency>

二、服务端启动类

服务端的启动类主要负责初始化线程模型、配置网络参数并绑定端口,以下是一个简单的示例:

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import java.net.InetSocketAddress;
public class ImageHttpServer {
    public void start(int port) throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup(); // 主线程组
        EventLoopGroup workerGroup = new NioEventLoopGroup(); // 从线程组
        try {
            ServerBootstrap bootstrap = new ServerBootstrap(); // 创建引导器
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class) // 推荐 Netty 服务端采用 NioServerSocketChannel 作为 Channel 的类型
                    .localAddress(new InetSocketAddress(port))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline()
                                    .addLast("codec", new HttpServerCodec()) // HTTP 编解码
                                    .addLast("compressor", new HttpContentCompressor()) // HttpContent 压缩
                                    .addLast("aggregator", new HttpObjectAggregator(65536)) // HTTP 消息聚合
                                    .addLast("handler", new ImageHttpServerHandler()); // 自定义处理器
                        }
                    }).childOption(ChannelOption.SO_KEEPALIVE, true); // 设置为 true 代表启用了 TCP SO_KEEPALIVE 属性,TCP 会主动探测连接状态,即连接保活
            ChannelFuture f = bootstrap.bind().sync(); // 绑定端口并等待同步完成
            System.out.println("Image HTTP Server started, listening on " + port);
            f.channel().closeFuture().sync(); // 关闭通道并释放所有资源
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            workerGroup.shutdownGracefully(); // 优雅地关闭工作线程组
            bossGroup.shutdownGracefully(); // 优雅地关闭主线程组
        }
    }
    public static void main(String[] args) throws Exception {
        new ImageHttpServer().start(8080); // 设置监听端口为 8080
    }
}

三、自定义处理器

在自定义处理器中,我们将处理 HTTP 请求,并根据请求类型(GET 或 POST)来响应相应的图片数据,以下是一个简单的示例:

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.URLDecoder;
import java.nio.channels.FileChannel;
public class ImageHttpServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
    private static final String IMAGE_DIRECTORY = "path/to/your/image/directory"; // 指定图片存储目录
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
        if (HttpUtil.is100ContinueExpected(msg)) {
            send100Continue(ctx);
        }
        // 处理 GET 请求,返回图片文件内容
        if (HttpUtil.isGet(msg)) {
            String uri = msg.uri();
            if (uri.startsWith("/images/")) {
                String imagePath = IMAGE_DIRECTORY + URLDecoder.decode(uri.substring(7), "UTF-8");
                File imageFile = new File(imagePath);
                if (imageFile.exists() && !imageFile.isDirectory()) {
                    sendImage(ctx, imageFile);
                } else {
                    sendError(ctx, NOT_FOUND);
                }
            } else {
                sendError(ctx, FORBIDDEN);
            }
        } else if (HttpUtil.isPost(msg)) {
            // 处理 POST 请求,保存上传的图片文件
            HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(new DefaultHttpDataFactory(false), msg);
            FileUpload fileUpload = null;
            while (decoder.hasNext()) {
                InterfaceHttpData data = decoder.next();
                if (data instanceof FileUpload) {
                    fileUpload = (FileUpload) data;
                    break;
                }
            }
            if (fileUpload != null) {
                String fileName = fileUpload.getFilename();
                File directory = new File(IMAGE_DIRECTORY);
                if (!directory.exists()) {
                    directory.mkdirs();
                }
                File targetFile = new File(directory, fileName);
                try (FileChannel fileChannel = new FileOutputStream(targetFile).getChannel();
                     FileChannel sourceChannel = new RandomAccessFile(fileUpload.getFile(), "r").getChannel()) {
                    sourceChannel.transferTo(0, sourceChannel.size(), fileChannel);
                } catch (IOException e) {
                    e.printStackTrace();
                    sendError(ctx, BAD_REQUEST);
                    return;
                }
                sendResponse(ctx, OK);
            } else {
                sendError(ctx, BAD_REQUEST);
            }
        } else {
            sendError(ctx, METHOD_NOT_ALLOWED);
        }
    }
    private static void send100Continue(ChannelHandlerContext ctx) {
        FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, CONTINUE);
        ctx.write(response);
    }
    private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
        FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, status);
        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    }
    private static void sendResponse(ChannelHandlerContext ctx, HttpResponseStatus status) {
        FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, status);
        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    }
    private static void sendImage(ChannelHandlerContext ctx, File imageFile) throws IOException {
        byte[] content = new byte[(int) imageFile.length()];
        try (FileInputStream fis = new FileInputStream(imageFile)) {
            fis.read(content);
        }
        DefaultFullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK);
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, "image/jpeg");
        response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.length);
        response.content().writeBytes(content);
        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    }
}

四、测试与验证

启动服务器后,可以使用浏览器或 curl 命令来测试图片上传和下载功能。

上传图片:使用表单提交方式上传图片到/images/ 目录。

下载图片:访问http://localhost:8080/images/{imageName} 以下载指定图片。

五、FAQs问答环节

Q1: Netty中的HttpRequest和HttpResponse由哪些部分组成?

A1: Netty中的HttpRequest由请求行、请求头和请求体三部分组成,HttpResponse由状态行、消息报头和响应正文三部分组成,具体如下表所示:

组件 描述
HttpRequest
请求行 以一个方法符开头,以空格分开,后面跟着请求的URI和协议的版本,格式为:Method Request-URI HTTP-Version CRLF,Method表示请求方法,Request-URI是一个统一资源标识符,HTTP-Version表示请求的HTTP协议版本,CRLF表示回车和换行。
请求头 包含多个键值对,用于传递客户端信息和服务器端配置。
请求体 可选部分,包含请求的主体数据,如POST请求中的表单数据。
HttpResponse
状态行 格式为:HTTP-Version Status-Code Reason-Phrase CRLF,其中HTTP-Version表示HTTP协议的版本,Status-Code表示服务器返回的响应状态码。
消息报头 包含多个键值对,用于传递服务器端信息和客户端配置。
响应正文 可选部分,包含响应的主体数据,如HTML页面、图片等。

Q2: Netty如何实现图片的上传和下载?

A2: Netty通过自定义处理器来实现图片的上传和下载,对于上传请求,Netty接收客户端发送的图片数据并保存到指定目录;对于下载请求,Netty读取指定目录下的图片文件并将其内容作为HTTP响应返回给客户端,具体实现细节如上文所述。

  •  王晓光
     发布于 2024-01-12 10:54:43  回复该评论
  • 这篇文章详细介绍了Java封装类的概念和实践,让我对封装类的作用和原理有了更深刻的理解,谢谢作者分享!

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

«    2024年11月    »
123
45678910
11121314151617
18192021222324
252627282930
控制面板
您好,欢迎到访网站!
  查看权限
网站分类
搜索
最新留言
文章归档
网站收藏
友情链接