Netty编写一个静态资源服务器

Netty编写一个静态资源服务器

上一篇文章我们了解了如何使用Netty编写一个极简的Http服务器 ,这一篇文章在上一篇文章的基础上,进一步编写一个静态资源服务器。

首先,HttpServer类与之前相同,不需要作修改,具体代码请见使用Netty编写一个极简的Http服务器 。我们修改HttpRequestHandler,来实现对静态资源请求的响应。

为了方便对Http请求的处理,我们先作如下规定:

  • 如果请求URI为“/”或“/index.html”,则直接返回index.html文件;
  • 如果请求URI以“/static”开头,则从/src/main/resources/static目录下根据URI中的文件名查找对应文件并返回给浏览器;
  • 如果请求URI不符合以上规则,则直接返回404未找到的Http响应。

首先我们在/src/main/resources目录下创建static文件夹,并添加几个测试文件,目录结构大概如下:

src
└── main
    ├── java
    │   └── ...
    └── resources
        ├── index.html
        └── static
            ├── test-image.jpg
            ├── test.txt
            └── 测试.txt

HttpRequestHandlerchannelRead0方法修改如下:

public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
        System.out.println("request uri: " + msg.uri());
        if ("/".equals(msg.uri()) || "/index.html".equals(msg.uri())) {
            handleResource(ctx, msg, "index.html");
        } else if (msg.uri().startsWith("/static")) {
            handleResource(ctx, msg, msg.uri().substring(1));
        } else {
                        //处理请求链接不存在的情况
            handleNotFound(ctx, msg);
        }
    }

        //其他方法
}

handleNotFound方法用于请求链接不存在时,返回404未找到响应,主要代码如下:

private void handleNotFound(ChannelHandlerContext ctx, FullHttpRequest msg) {
    ByteBuf content = Unpooled.copiedBuffer("URL not found", CharsetUtil.UTF_8);
    HttpResponse response = new DefaultFullHttpResponse(msg.protocolVersion(), HttpResponseStatus.NOT_FOUND, content);
    ChannelFuture future = ctx.writeAndFlush(response);
    future.addListener(ChannelFutureListener.CLOSE);
}

handleResource方法用于处理静态资源请求,也包括处理对主页index.html访问的处理:

private void handleResource(ChannelHandlerContext ctx, FullHttpRequest msg, String resource) throws IOException {
        String url = this.getClass().getResource("/").getPath() + resource;
        File file = new File(url);
        if (!file.exists()) {
            handleNotFound(ctx, msg);
            return;
        }
        if (file.isDirectory()) {
            handleDirectory(ctx, msg, file);
            return;
        }
        handleFile(ctx, msg, file);
    }

handleResource方法首先根据请求的文件名,拼装文件磁盘路径,获取对应的文件。如果文件不存在,则返回404的Http响应;如果对应路径是文件夹,则列出文件夹下的子文件;如果是文件,则返回对应的文件内容。

handleDirectory代码如下,此处只是简单地列出文件夹下的文件名称,还有很多细节需要完善。

private void handleDirectory(ChannelHandlerContext ctx, FullHttpRequest msg, File file) {
    StringBuilder sb = new StringBuilder();
    File[] files = file.listFiles();
    if (files != null) {
        for (File f : files) {
            if (f.isHidden() || !f.canRead()) {
                continue;
            }
            String name = f.getName();
            sb.append(name).append("<br/>");
        }
    }
    ByteBuf buffer = ctx.alloc().buffer(sb.length());
    buffer.writeCharSequence(sb.toString(), CharsetUtil.UTF_8);
    FullHttpResponse response = new DefaultFullHttpResponse(msg.protocolVersion(), HttpResponseStatus.OK, buffer);
    response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=UTF-8");
    ChannelFuture future = ctx.writeAndFlush(response);
    future.addListener(ChannelFutureListener.CLOSE);
}

handleFile方法如下:

private void handleFile(ChannelHandlerContext ctx, FullHttpRequest msg, File file) throws IOException {
    RandomAccessFile raf = new RandomAccessFile(file, "r");
    HttpHeaders headers = getContentTypeHeader(file);
    HttpResponse response = new DefaultHttpResponse(msg.protocolVersion(), HttpResponseStatus.OK, headers);
    ctx.write(response);
    ctx.write(new DefaultFileRegion(raf.getChannel(), 0, raf.length()));
    ChannelFuture future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
    future.addListener(ChannelFutureListener.CLOSE);
}

getContentTypeHeader方法:

private HttpHeaders getContentTypeHeader(File file) {
    MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap();
    HttpHeaders headers = new DefaultHttpHeaders();
    String contentType = mimeTypesMap.getContentType(file);
    if (contentType.equals("text/plain")) {
        //由于文本在浏览器中会显示乱码,此处指定为utf-8编码
        contentType = "text/plain;charset=utf-8";
    }
    headers.set(HttpHeaderNames.CONTENT_TYPE, contentType);
    return headers;
}

通过Java的MimetypesFileTypeMap工具类获取要传给浏览器的contentType,并将contentType设置到header中。测试过程中发现utf-8编码的txt文件在浏览器中会显示成乱码,因此如果获取的是文本文件,则将contentTypetext/plain改为text/plain;charset=utf-8

最终效果如下:

文件列表

https://i.loli.net/2021/01/14/hKUfXHL75Trnv6c.png

txt文件内容

https://i.loli.net/2021/01/14/l1nqQWaY5iLgokZ.png

获取图片文件

https://i.loli.net/2021/01/15/h597pMyrDNinRjP.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352

推荐阅读更多精彩内容