使用http的Accept-Ranges实现json报文分段传输

背景

因为公司业务需求,对接一款终端设备,需要下载黑名单。可是设备的性能较差,每次下载的报文大小有限制。所以需要采用报文分段下载来实现。

当然其实这种需求最好是用查询分页来达到分段下载是最好的,可是因为终端那边实现问题还有以及其他各种原因,不得已采用这种分段下载方式。

找了很多帖子比较少有关于这种分段返回报文的内容,可是有发现可以利用断点下载的思想,去实现这个分段报文返回。有断点下载经验的同学,应该很快能理解本文介绍的内容,其实都是很皮毛的东西。

正题

分段下载我们主要用到两个http头信息:

  • http 响应头 Accept-Ranges
  • http 请求头 Range

服务器使用 HTTP 响应头Accept-Ranges标识自身支持范围请求(partial requests)。字段的具体值用于定义范围请求的单位。当浏览器/客户端发现 Accept-Ranges 头时,可以尝试继续中断了的下载,而不是重新开始。用法:Accept-Ranges: bytes

Range用来标志本次请求,获取数据的范围。如:Range: bytes=100-200

步骤

  • 客户端请求头都带上Range,第一个请求Range:bytes=0-99,第二个Range:bytes=100-199,以此类推。
  • 服务端除了响应头带上Accept-Ranges: bytes,还需要根据请求中的Range来切割报文,返回Range范围内的数据。
  • http响应码设置为206
  • http响应头新增Content-Range,标识本次响应的内容的范围,和整个完整报文的长度
  • 需要注意一点的是,Range单位是byte而不是字符数,所以计算长度的时候要要将字符串换算成byte计算。

接下来展示controller层部分代码:

		// 省略业务代码
		//rsp为黑名单的响应dto对象,全量的黑名单
        String returnJson = JsonUtil.Object2Json(rsp);
        httpResp.setContentType("application/json");
        //无缓存
        httpResp.setHeader("Cache-Control", "no-cache");
        //设置UTF-8编码
        httpResp.setCharacterEncoding(Constant.CHARACTER_ENCODING);
        //可以断点下载
        httpResp.setHeader("Accept-Ranges", "bytes");
        //设置http响应码为206
        httpResp.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);

        //获取头信息Range,并且去掉bytes=
        String range = httpReq.getHeader("Range").replaceAll("bytes=", "");
        String[] rangeSplit = range.split("-");

        //整段报文的总长度,要用utf-8的编码字节长度计算
        int returnJsonLength = returnJson.getBytes("UTF-8").length;
        //offset 第一次请求默认从0开始
        int offset = Integer.parseInt(rangeSplit[0]);
        // 初始化 length 和 endIndex
        // length:本次报文返回的长度;endIndex:本次报文结束的下标位置,默认是报文的总长度所以是returnJsonLength - 1
        int length = returnJsonLength - offset;
        int endIndex = returnJsonLength - 1;
        //设置 length 和 endIndex 值
        if (rangeSplit.length > 1) {
            //strSplit数组长度大于1,说明Range是一个范围,而不是一个固定值
            endIndex = Integer.parseInt(rangeSplit[1]);
            //endIndex不允许大于等于报文长度
            if (endIndex >= returnJsonLength) {
                endIndex = returnJsonLength - 1;
            }
            //因为endIndex最大值比报文长度小于1的,而length是报文返回长度,所以要+1
            length = endIndex - offset + 1;
        }

        httpResp.setHeader("Content-Length", Long.toString(returnJsonLength));
        //Content-Range:bytes[json的开始字节]-[json的结束字节]/[报文的总大小]
        httpResp.setHeader("Content-Range", "bytes " + offset + "-" + endIndex + "/" + returnJsonLength);
        
        // 获得写出流
        try {
            OutputStream outputStream = httpResp.getOutputStream();
            //重点来了,输出报文,只返回原完整报文的offset至offset + length的字节内容
            outputStream.write(returnJson.getBytes("utf-8"), offset, length);
            outputStream.flush();
            outputStream.close();
        } catch (Exception e) {
            LogUtil.addProclog(procid, "返回报文错误:" + CommonUtil.getTrace(e));
        }

缺点:

  • 每次请求只返回部分的报文,可是每次都是获取全量的黑名单,数据库查询多次。可以用redis缓存黑名单,减少数据库查询。

仅以记录本次的解决方案,如果有更好的思路不妨一起交流一下,不才献丑了。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
accept-ranges是HTTP协议中的一个响应头部,用于指示服务器是否支持分块传输(chunked transfer encoding)和范围请求(range requests)。 在HTTP/1.1协议中,范围请求允许客户端请求部分资源的内容,而不是整个资源。例如,如果一个大的视频文件被分成多个部分,一个客户端可以只请求其中的一个部分而不是整个文件。支持范围请求可以提高性能和降低网络带宽的消耗。 accept-ranges头部的值可以是"none"、"bytes"或其他自定义值。当值为"none"时,表示服务器不支持范围请求。当值为"bytes"时,表示服务器支持范围请求,并且只接受字节范围请求。其他自定义值可以用于指示服务器支持其他类型的范围请求,但这种情况比较少见。 例如,一个支持范围请求的响应头部可以如下所示: HTTP/1.1 200 OK Accept-Ranges: bytes Content-Length: 1024 在这个响应中,Accept-Ranges头部的值为"bytes",表示服务器支持字节范围请求。Content-Length头部指示响应正文的总长度为1024个字节。如果客户端想要请求其中的一部分,可以使用Range头部来指定范围,例如: Range: bytes=0-511 这个请求表示客户端只需要请求响应正文的前512个字节。服务器将返回一个206 Partial Content响应,并在Content-Range头部中指定返回的内容范围,例如: HTTP/1.1 206 Partial Content Accept-Ranges: bytes Content-Length: 512 Content-Range: bytes 0-511/1024 在这个响应中,Content-Range头部指示返回的内容范围是0到511字节,总共有1024个字节。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值