预下载视频网络流量消耗问题分析

简介

最近得到一个问题,之前业务上为了视频在启动APP后能秒播,对第一个视频做了预加载逻辑。但是最新发现,这个文件的预下载消耗用户的流量远远多于应该的预下载大小。

开始抓包看,没有看出什么问题,因为在下载后读输入流的逻辑中是有只写入1/10流数据到缓存文件的逻辑的,但是在后来通过HttpCanary抓包的过程,真的接口响应的大小远远大于1/10的写文件大小(比如缓存文件是1M,但抓包服务端响应了4M,也就是消耗了用户4M的流量),但去看缓存的预下载视频文件,大小又是吻合读1/10的文件大小逻辑的。

分析

简单分析

下载逻辑中,header中并没有添加Range,也就是说是请求的完整的视频文件,但是在输入流读取的时候,做了判断,如果大于文件大小的1/10,则终止读取,并关闭输入流。
输入流是请求服务端文件下载后,和服务端建立起来的一个数据通道,服务器和客户端之间通过这个管道做数据传输,但是问题来了,并不是客户端读取多少数据,服务端就刚好传输这么多数据。可以想见,服务端传输的数据必定是大于客户端接受的数据,否则,这个管道就没水了。

问题思考

如果是完整的请求一个视频地址文件的下载(不添加Range字段请求文件片段的话),InputStream从服务器接收数据的输入流读取的字节大小和最终网络请求消耗的网络大小是无关的。
也就是说,InputStream你可能只读取了1MB的数据,但是整个下载网络请求消耗的流量可能已经4MB了。可以这样理解这个问题:

InputStream是用于从服务器接收数据的输入流。当你向服务器发起请求时,服务器会处理请求并返回相应的数据。这些数据被封装在响应中,你通过InputStream从响应中读取数据。

InputStream和服务器发送数据之间的关系可以类比为“水管”和“水流”的关系。服务器是“水源”,它产生数据并通过网络发送给你的应用。而InputStream是你的应用内部的“水管”,它负责接收并传输这些数据,使你的应用能够获取服务器返回的信息。

当你开始读取InputStream时,数据会从网络通过“水管”传送到你的应用中,然后你的应用可以对数据进行处理,比如写入文件、解析JSON等操作。这个过程是由网络协议(如HTTP、HTTPS等)负责的,而数据的传输则会消耗网络流量

当你使用 InputStream 读取网络响应数据时,实际读取的数据量并不会影响服务器响应的大小。服务器的响应是提前生成的,不会因为你读取的数据量而改变。

总的来说,服务器的响应大小与你实际读取的数据量是独立的。服务器发送完整的响应,而你从中读取数据时只是逐步接收和处理响应体的内容。服务器响应大小可能会受到网络传输协议、头部信息、数据压缩等因素的影响,这些因素导致服务器的响应大小可能比你实际读取的数据量大。

解决方案

在预下载视频前,先通过HEAD请求得到完整文件的大小。再计算得到预下载的视频大小,比如1/10,假如是一个10MB的文件,这里计算得到的下载文件片段大小就是1MB=110241024=1048575字节。
再进行文件下载请求,HEAD中添加Range字段,加上视频片段的区间,比如这里就应该是
Range: bytes=0-1048575

此时服务端返回的就只会包含这个区间的文件片段,消耗的流量也只会是文件片段大小的流量。所以问题就解决了。

其他注意

业务逻辑上需要使用HEAD请求得到的文件大小,来做后续的计算,而不能再是文件GET请求中返回的片段的Content-Length,因为此时也是片段的Content-Length,即1/10的文件大小。

HEAD请求获取原始文件大小

Call headRequestCall = HttpConnect.getInstance().buildHeadCall(downloadUrl);
Response headResponse = headRequestCall.execute();
long fileSize = 0;
if (headResponse != null && headResponse.isSuccessful()) {
    String contentLength = headResponse.header("Content-Length");
    if (!TextUtils.isEmpty(contentLength)) {
        fileSize = Long.parseLong(contentLength);
    }
}

if (headResponse != null) {
    headResponse.close();
}
return fileSize;

计算预下载片段文件大小

比如按照文件的1/10来计算,则计算后的大小为

long limit = fileSize / 10;

请求片段文件

这里注意rangeEnd的值,这里采用数组下标的规则,所以rangeEnd=rangeStart+limit-1。

requestBuilder.addHeader("Range", "bytes=" + rangeStart + "-" + rangeEnd);

结尾

最终,通过HttpCanary抓包,可以看到,服务端响应的大小和客户端请求的Range长度是完全一致的,不会再存在消耗用户多余流量的问题。

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MY7y2utm' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片