HTML中chunked解码和gzip解压

article/2025/10/18 10:08:54

chunked编码

chunked编码的的好处

当访问的时动态页面时,服务器则无法预知内容的大小,因此需要一遍产生数据,一边发送数据,将数据分块发送(服务器通过响应头’Transfer-Encoding: chunked’告诉浏览器它将使用chunked编码传输)。浏览器也不需要等到内容字节全部下载完成,只要接收到一个chunked块就可解析页面,并且可以下载html中定义的页面内容,包括js,css,image等。

更多优点如下:

  • 1.允许服务器为动态生成的内容维持HTTP持久链接。
    通常,持久链接需要服务器在开始发送消息体前发送Content-Length消息头字段,但是对于动态生成的内容来说,在内容创建完之前是不可知的。

  • 2.允许服务器在最后发送消息头字段。
    对于那些头字段值在内容被生成之前无法知道的情形非常重要,例如消息的内容要使用散列进行签名,散列的结果通过HTTP消息头字段进行传输。没有分块传输编码时,服务器必须缓冲内容直到完成后计算头字段的值并在发送内容前发送这些头字段的值。

  • 3.HTTP服务器有时使用压缩 (gzip或deflate)以缩短传输花费的时间,分块传输编码可以用来分隔压缩对象的多个部分
    在这种情况下,块不是分别压缩的,而是整个负载进行压缩,压缩的输出使用本文描述的方案进行分块传输。在压缩的情形中,分块编码有利于一边进行压缩一边发送数据,而不是先完成压缩过程以得知压缩后数据的大小

chunked编码的格式

其具体格式如下(BNF文法):

Chunked-Body = chunk //0至多个chunk
last-chunk //最后一个chunk
trailer //尾部
CRLF //结束标记符
chunk = chunk-size [ chunk-extension ] CRLF
chunk-data CRLF
chunk-size = 1
HEX
last-chunk = 1*(“0”) [ chunk-extension ] CRLF
chunk-extension= *( “;” chunk-ext-name [ “=” chunk-ext-val ] )
chunk-ext-name = token
chunk-ext-val = token | quoted-string
chunk-data = chunk-size(OCTET)
trailer = *(entity-header CRLF)
解释:

  • 1 Chunked-Body表示经过chunked编码后的报文体
    报文体可以分为chunk,last-chunk,trailer和结束符四部分。chunk的数量在报文体中最少可以为0,无上限;
  • 2 每个chunk的长度是自指定的
    起始的数据必然是16进制数字的字符串,代表后面chunk-data的长度(字节数)。这个16进制的字符串第一个字符如果是“0”,则表示chunk-size为0,该chunk为last-chunk,无chunk-data部分。
  • 3 可选的chunk-extension由通信双方自行确定,如果接收者不理解它的意义,可以忽略。
  • 4 trailer是附加的在尾部的额外头域,通常包含一些元数据(metadata, meta means “about information”),这些头域可以在解码后附加在现有头域之后

RFC2616中附带的解码流程如下:(伪代码)

length := 0         //长度计数器置0read chunk-size, chunk-extension (if any) and CRLF      //读取chunk-size, chunk-extension和CRLF
while(chunk-size > 0 )  //表明不是last-chunk
{            read chunk-data and CRLF            //读chunk-size大小的chunk-data,skip CRLFappend chunk-data to entity-body     //将此块chunk-data追加到entity-body后length := length + chunk-sizeread chunk-size and CRLF          //读取新chunk的chunk-size 和 CRLF
}
read entity-header      //entity-header的格式为name:valueCRLF,如果为空即只有CRLF
while (entity-header not empty)   //即,不是只有CRLF的空行
{append entity-header to existing header fieldsread entity-header
}
Content-Length:=length      //将整个解码流程结束后计算得到的新报文体length,作为Content-Length域的值写入报文中
Remove "chunked" from Transfer-Encoding  //同时从Transfer-Encoding中域值去除chunked这个标记

chunked解码

解码目的将分块的chunk-data整合恢复成一块作为报文体,同时记录此块体的长度,length最后的值实际为所有chunk的chunk-size之和。
接收端就可以根据长度值读取数据,以及最后一个长度为0的块来判定接收结束

代码

# 模拟chunked块数据
chunk1 = b'4\r\nWiki\r\n'
chunk2 = b'6\r\npedia \r\n'
chunk3 = b'E\r\nin \r\n\r\nchunks.\r\n'
last_chunk = b'0\r\n'chunks = chunk1 + chunk2 + chunk3 + last_chunk
# 解码字节串形式的chunked
def decode_chunked(content):# 自定义chunked解码newContent = b''offset = 0while True:try:pos = content.find(b'\r\n', offset)  # 找chunked块的前一个\r\nchunk_size = int(content[offset: pos], 16)if chunk_size > 0:offset = pos + 2newContent += content[offset: offset+chunk_size]pos = content.find(b'\r\n', offset+chunk_size)   # 找chunked块的后一个\r\noffset = pos + 2else: breakexcept BaseException as ret:print(f'没有达到最后一个chunked块!,{ret}')breakreturn newContentprint(decode_chunked(chunks).decode('utf-8'))
# 解码数据流形式的chunked
from io import BytesIOclass chunk(object):'''读取stream,输出解码chunked后的信息'''def __init__(self, chunks_data):self.content = chunks_datadef decode_chunked(self):# chunked解码buffer = b''with BytesIO(self.content) as stream:while True:try:line = self.readline(stream)length = int(line[:-2], 16)if length:buffer += stream.read(length)# 读走数据末尾的'\r\n'stream.read(2)else: # 终止块return bufferexcept BaseException as ret:print(f'没有达到最后一个chunked块!,{ret}')return bufferdef readline(self, input_stream):# 读取一行:以'\r\n'结尾is_end = Falsebuffer = b''while True:byte = input_stream.read(1)if not byte: # EOF返回空字符串return bufferbuffer += byteif byte == b'\r':is_end = Trueelif is_end: if byte == b'\n':return bufferchunk(chunks).decode_chunked()

如何解压gzip格式的响应体?

gzip文件中存储的也是字节串,只不过是经过压缩过。如果直接用vim打开是乱码。
使用python的gzip模块可以将gzip格式的文件解压为txt文本;或者将gzip形式的字节串解压为原始字节串。

# 将以gzip压缩的响应体解压为原始字节串
import gzip
gz_bytes = b'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x00\xa5V\xddo\xdb6\x10\x7f\xf7_\xc1r@\xba\x01\xa1\xe4:\x08\xe6\r\x96\x87\xcd[\xb7=u\x0f)\xb0\x01\x03\x02\x8a:Kt(R#)\x7f\x14\xfd\xe3w\x14%EM\xd2-\xa9\x1fl\x91\xc7\xdf}\xdf\x91\xb7z\xf5\xf3\xbb\xcd\xcd_\x7f\xfcB*_\xab\xf5l\x15>Dq]f\x144k\x1d%Bq\xe72j*A\xd73\x04\x00/\xd63BV^z\x05\xebw\x96\x0b\x05\xe47P\r\xd9\x80\xf6`Wi<\x99\x05P\r\x9e\x13Qq\xeb\xc0g\xf4\xfd\xcd[\xb6\xa4\xeb\xf1@\xf3\x1a2\xba\x97ph\x8c\xf5\xa8\xca\xa0\x00\xed\xefIl+}&\xcc\x1e\xec%9\xc8\xc2WY\x01{)\x80u\x9bK"\xb5\xf4\x92+\xe6\x04W\x90\xbd\x99\x88\xae\xbco\x18\xfc\xd3\xca}F\xffd\xef\x7fd\x1bS7\xdc\xcb\\\xc1D\x8f\x84\x0c\x8a\x12\x1e\x99T;\xde4J\nd0\x9a\xddH\x05\x1b\xa3\x8c\x9dp~\xb5\x15\xdb|\xcb\xff\x87\x13\xe1[YN\xd8R\xd7\xdc\nS\xd7F\xa7\xb91w\xccC\xdd(\xee!\xc5\xf0\xb2\xfe@\xd6ej\x98\x14\x01c\xcd\xc1\x81\x8db\x92c\xad\x9ea\xe9\xef5/\xe1\xcbUn\xf9>|\xd9\xe2\xdby\xd2\xe8\xf2\xa5\x0ej\xa3\x81\xa6\xf7L\x8d5\rX\x7f\xcah\x01NX\xd9\x04\xb6\t\xfeW\xf0^\xea\x928\xcf\xad\x87\x82\x94\xadD\xe0%)\x8chkDtj.\x89o\xbd\xb1\x98i<\xe1VT\xd2\x83\xf0\xad\r@\xae\x0bR\x1b\x0b\x83H\xb25\x96\xf4U\x89\xca\x8bVx\xd7\x810\x8e\xa1t\\BI\xb4OI}G,\xa8\x8c\xba\nkM\xb4\x9e\x04\xcf)\xa9,l3*\x9c\xc3\xb8`,\xdd\x10\x93\x04\xff\xe8C\xde\xc8\xe2O\r\xc6\xa6\x83\xa7!j\xc4\xc9\x0f\x80]\xf3\xe6\xbb\xc5\x11\x7f\x83\xcc/\xcc\x05J\xb8\xcf\xc5\xb3U/\x96G\xfc\x9d\xabz\xb1|\xb9\xea\xab\xc5\xf1\xea\\\x9f\xaf\x9et9\xd4\x1e0oZQ\xb1h\xc3\xe8\xed\x1c\xbd\x9d\x9f\xed\xed\xfceZ\xaf1\xbd\xd7g\xa7\xf7\xfa\x85\xbe.\xd1\xd7\xe5\xd9\xbe.\x07_W\xaf\x18\xc3\xabt\x87=e\xec\xf7\xbe\x82\x1a\x08cH\x9ft\x88?)p\x15\x80\x9f\xb6\x07W\x9e\xa7\xcbd\x9e\xcc\xd3\x03\xe4\xdd6\xa9\xa5N\xf0\x8c\x12Yt\xa0\xae\xdb:\r\xa0\x8bAI\'\xfe\xd3\x16|RA\x01\xb5aA,s\xd8\xf2l\x94\x1d\x0b\xcf\xc3\xd1\xa7\x83\x8agH\xc3\x80\x9e\xc3\x8e\xc1\xf5\xce[\xde$\xffe\xc7*\xder\xeb\x83\xd4\x859$\x98\x83R\x99\x9c+\xf2\xf1\xe3x\xa9%\x07\x8b\xee|\xfd\xba\xc7\x12g\x05\xa6\x11tZ\xf4\xe9\xda\xb94r%;G\xd7\x7f\x1f\xaf6i/\xf6\xf57\xaba\xb9J\xe3[<[\xe5\xa68\x8d\xef\xf4\x8ea.X(\x1e~2\xadg\xe10\x96V!\xf7]V\xa2\xe8\x9f\x02\xfd\xb3\\\r\xef\x9fE\xe4\x0bj\xc0\x12k\xf0\x8d\xa59\xd7\x1a\xec\xc8\xd8\xc3#\xa4g@\x16\x83\x8e\xb0v\xbe \xf1\x80u#AF\'c\x02E\xfb\x07T\xaf\'\x8d\xe0~\x17\xac\xad\xb9\xd4\x18|\xbc\xd9q1\xd1\x1a\xe8\x9b\x91:j\xfd\xe4\xd1`\xd1RT\xf3$y\xe0\xc1\x87\x0cJcO]s8D? \x0c\xb8-\xf0\xf0\xe2\x14lxT\x10\xfa\x986\xa0\x9dQm\xd0\xe6\x98\xc3\x82\xc7\x05\xa2\x1f\xd3\xeeeC\x91sq7\x01?"\x8dX\xac\xc21\x19\xfd\x9b\'\xf5\xd6\x8cQ@L\xab%\x8eL\x0e\x07\xa3\x1e=\xd8w\xcb?\xdc\xb6V\xc5b\x8b\xc3\x05W*\tC_\xc8\xc7C\xbeQi:\xddc@\xe5\xbe+\xa8\xb8\x08\xab\xbe\x90\'\xfd\xb0\xe3{\x1e\xa94\xd67\xd6\xb4\x92\xb9Km\x98\xca,\x0c\xdf\xae\xc2\xc7\x9a~\xbe\xacP\x02\x9f\xe5\xe5\xee\xa4\x11\xd6}z\x964\r\xe9\x0c\xcd\xe7m\xeb<$X{\xa96\x1eG\x82\x1f\n\xd3\x15\x9a\xe9\xa6\x86pp!2\x9c0"\xfeb\xe7\xb2<\xbf\x10\x85\xce\xde\\4\x02oX\t\x17\x91\xf1&\x98\x88g\xc1\xc8\x0c\xc5\x86\xb1\xcb\x1a\xe7pT)C\xe1N\x8d\x9b\xad\xd2\xd0\x89\xdd\xa2\x9b\xb5\xff\x05\x00\x88\x9d4{\x0b\x00\x00'
print(gzip.decompress(gz_bytes))
# output
b'<!DOCTYPE html>\n<html lang="en-us" class="ohc">\n\n<head>\n  <title>Oracle Help Center</title>\n\n  <meta charset="UTF-8">\n  <meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1">\n  <meta http-equiv="X-UA-Compatible" content="ie=edge">\n  <meta name="msapplication-TileColor" content="#fcfbfa">\n  <meta name="msapplication-config" content="/sp_common/book-template/ohc-common/img/o-icon/browserconfig.xml">\n  <meta name="msapplication-TileImage" content="/sp_common/book-template/ohc-common/img/o-icon/favicon-270.png">\n  <meta name="msapplication-config" content="none"/>\n  <meta property="description" content="Getting started guides, documentation, tutorials, architectures, and more content for Oracle products and services." />\n  <link rel="shortcut icon" href="css/images/favicon.ico"/>\n  <link rel="icon" type="image/png" sizes="192x192" href="/sp_common/book-template/ohc-common/img/o-icon/favicon-192.png">\n  <link rel="icon" type="image/png" sizes="128x128" href="/sp_common/book-template/ohc-common/img/o-icon/favicon-128.png">\n  <link rel="icon" type="image/png" sizes="32x32" href="/sp_common/book-template/ohc-common/img/o-icon/favicon-32.png">\n  <link rel="apple-touch-icon" sizes="120x120" href="/sp_common/book-template/ohc-common/img/o-icon/favicon-120.png">\n  <link rel="apple-touch-icon" sizes="152x152" href="/sp_common/book-template/ohc-common/img/o-icon/favicon-152.png">\n  <link rel="apple-touch-icon" sizes="180x180" href="/sp_common/book-template/ohc-common/img/o-icon/favicon-180.png">\n<!-- injector:theme -->\n<link rel="stylesheet" href="css/alta/8.0.0/web/alta.min.css" id="css" />\n<!-- endinjector -->\n  <link rel="stylesheet" href="css/demo-alta-site-min.css" type="text/css" />\n  <link rel="stylesheet" href="css/app.css" type="text/css" />\n  <link rel="stylesheet" href="css/bootstrap.min.css" type="text/css" />\n<script>window.ohcglobal || document.write(\'<script src="/en/dcommon/js/global.js">\\x3C/script>\')</script></head>\n\n<body class="oj-web-applayout-body">\n  <div id="globalBody" class="oj-web-applayout-page">\n    <header role="banner" class="layout-header">\n      <ocom-u02 header-title="Help Center"></ocom-u02>\n    </header>\n    <div main="container" class="mainContainer">\n      <documentation-banner></documentation-banner>\n      <category-icons></category-icons>\n      <featured-products></featured-products>\n      <solutions-section></solutions-section>\n      <feedback-section></feedback-section>\n      <footer role="contentinfo">\n        <universal-footer products_az_url="/en/browseall.html"></universal-footer>\n      </footer>\n    </div>\n  </div>\n\n  <script type="text/javascript" src="js/libs/require/require.js"></script>\n  <script type="text/javascript" src="js/main.js"></script>\n  <script async="async" src="//consent.truste.com/notice?domain=oracle.com&c=teconsent&js=bb&cdn=1&pcookie&noticeType=bb&text=true" crossorigin=""></script>\n\n</body>\n\n</html>'

gzip + chunked处理

该模式下,响应体先用gzip压缩,然后再使用chunked编码分块(每个chunk本身没有压缩); 客户端收到响应体应先chunked解码为完整的gzip文件,再使用gzip解压. 测试文件:13_.htm

# 对gzip+chunked的处理:先使用chunked解码,再使用gzip解压                
with open('testFile/13_.htm', 'rb') as f:cc = f.read()cz = chunk(cc).decode_chunked()print(gzip.decompress(cz))

参考资料

1.Chunked transfer encoding


http://chatgpt.dhexx.cn/article/2m5bUvHv.shtml

相关文章

Android:rxjava简单实现原理(map/flatmap操作符)

rxjava 装饰者模式1、背景2、定义3、特征4、装饰者模式demo rxjava装饰者模式1、rxjava中转换操作符map的简单实现2、rxjava中转换操作符flatmap的简单实现 装饰者模式 1、背景 假设奶茶店有两种茶&#xff0c;果茶&#xff08;fruit tea&#xff09;和奶茶(milky tea)&#…

java dataset flatmap_Spark中map和flatMap的區別詳解

本文介紹了Spark中map(func)和flatMap(func)這兩個函數的區別及具體使用。 函數原型 1.map(func) 將原數據的每個元素傳給函數func進行格式化&#xff0c;返回一個新的分佈式數據集。(原文&#xff1a;Return a new distributed dataset formed by passing each element of the…

Spark中flatMap的操作

Test 1&#xff1a; package test.wyh.wordcountimport org.apache.spark.rdd.RDD import org.apache.spark.{SparkConf, SparkContext}object TestFlatMap {def main(args: Array[String]): Unit {//建立Spark连接val sparkConf new SparkConf().setMaster("local&quo…

flatMap底层实现

上篇&#xff1a;Transformation的map使用 第一种方式重写flatMap方法实现 实现需求&#xff1a;根据字符串在nc -lk 8888的窗口命令下输入的数据&#xff0c;在控制台打印输出发现&#xff1a;在同一行数据输入的单词字符串自动换行&#xff0c;按每个单词独立换行并且若输入…

java7 flatmap_flink学习之七-map、fliter、flatmap

看完了Flink的datasource、sink&#xff0c;也就把一头一尾给看完了&#xff0c;从数据流入到数据流出&#xff0c;缺少了中间的处理环节。 而flink的大头恰恰是只在这个中间环节&#xff0c;如下图&#xff1a; source-transform-sink-update.png 中间的处理环节比较复杂&…

Stream之flatMap

一、flatMap简介 flatMap:将小Stream转换为大Stream 二、示例转换要求 目标&#xff1a;将如下对象中的分类category提取出来&#xff0c;去重。其中如"哲学,爱情",需要解析为两个分类&#xff0c;["哲学","爱情"] [{"age":33,"…

flatmap使用

使用场景 适用于嵌套list数据结构&#xff0c;想把内部的list数据合并成一个list.。 举例如下&#xff1a; public class Test {public static void main(String[] args) {List<List<String>> list new ArrayList<>();List<String> list1 new Arr…

Java8中map与flatMap用法

目录 1 概述 2 map与flatMap 3 常用写法 1 概述 Java8中一些新特性在平时工作中经常会用到&#xff0c;但有时候总感觉不是很熟练&#xff0c;今天特意将这个Java8中的映射记录一下。 2 map与flatMap map---对集合中的元素逐个进行函数操作映射成另外一个 flatMap---接收一…

如何使用flatMap

1. 什么情况下用到flatMap 当使用map&#xff08;&#xff09;操作时&#xff0c;不是返回一个值&#xff0c;而是返回一个集合或者一个数组的时候&#xff0c;这时候就可以使用flatMap解决这个问题。举个例子&#xff0c;你有一个列表 [21,23,42]&#xff0c;然后你调用getPr…

【JavaScript中数组的flatMap方法的详细介绍】

在我们平时对数组进行操作的时候&#xff0c;通常map、forEach和filter方法比较常用。而flatMap方法用得相对少一些。当你掌握了flatMap方法的使用之后&#xff0c;我相信你一定会喜欢上它的&#xff01; 下面我们会通过以下三个问题展开对flatMap方法的讲解&#xff1a; 1. f…

Java8 - Streams flatMap()

文章目录 官方文档What is flatMap()?Why flat a Stream?Demo需求1&#xff1a;Find all books需求2&#xff1a;Order and LineItems需求3&#xff1a;Splits the line by spaces需求4&#xff1a; flatMap and primitive type 官方文档 https://docs.oracle.com/javase/8/…

JAVA8 中的flatmap

构建对象 class User{private String addr } 将多个User集合中的addr按照;分割合并成一个字符串list List<User> uList Lists.newArrayList();User u1 new User();u1.setAddr("a1;a2;a3;a4;a5");User u2 new User();u2.setAddr("b1;b2;b3;b4;b5&qu…

Unity resource style/Theme.AppCompat.Dialog (aka xxx:style/Theme.AppCompat.Dialog) not found

关于Unity 打包报错"resource style/Theme.AppCompat.Dialog (aka com.game.chipsmerge:style/Theme.AppCompat.Dialog) not found."的问题 解决方法: 在mainTemplate文件中添加依赖: implementation ‘com.android.support:appcompat-v7:28.0.0’ 或者自己去下载其…

android最新v7包下载,support v7 appcompat.jar包下载

android support v7 appcompat.jar包是一款非常实用的jar文件,是android开发中必备的一份文件,能够在低版本Android平台上开发一个应用程序,兼容性极强。感兴趣的朋友欢迎前来IT猫扑下载体验吧! android support v7 appcompat.jar包介绍 android-support-v7-appcompat.jar包…

解决 appcompat 1.1.0 导致 webview crash 的问题

Android SDK 太不让人省心了&#xff0c;正式版本居然也埋雷。 前段时间把 support 升级到了 androidx&#xff0c;appcompat 自动升级了新版本 androidx.appcompat:appcompat:1.1.0。 简单回归了下功能就发上线了&#xff0c;结果在在 5.1 的系统上发生了大规模的 crash&…

从AppCompat切换到MaterialComponents一些主题属性介绍

文章目录 前言主题属性颜色排版字体形状小部件ButtonsText FieldsCardsBottom Navigation 后话 前言 絮叨两句&#xff0c;感觉Component这个库有点傲娇&#xff0c;我碰到一个情景&#xff0c;使用Button&#xff0c;设置了background属性&#xff0c;当使用样式是AppCompat时…

Gradle编译问题(appcompat和material相关)

在使用Android Studio编译项目时&#xff0c;发现的编译问题。已解决&#xff0c;在此记录一下 问题1 Cant determine type for tag <macro name"m3_comp_bottom_app_bar_container_color">?attr/colorSurface</macro> 原因是androidx.appcompat:app…

Android报错之You need to use a Theme.AppCompat theme (or descendant) with this activity.

[TOC](Android报错之You need to use a Theme.AppCompat theme (or descendant) with this activity.) 一、报错如下 原因为&#xff1a;Activty继承自android.support.v7.app.AppCompatActivty,而不是android.app.Activty。 二、解决方法 看一下提示&#xff0c;就是要用Th…

appcompat_v7项目说明

一、appcompat_v7项目说明 今天来说一下appcompat_v7项目的问题&#xff0c;使用eclipse创建Android项目时&#xff0c;发现project列表中会多创建出一个appcompat_v7项目&#xff0c;这是我搭建最新的Android开发环境创建第一个Android测试项目后发现的&#xff0c;我在创建An…

Android Studio报错Could not find any version that matches com.android.support:appcompat-v7:33.+.

今天用AndroidStudio新建了一个项目&#xff0c;没想到新建项目就爆红了 而且Java代码有标红&#xff0c;cannot reslove symbol"v7" 解决方案&#xff1a; 1.打开build.gradle文件&#xff0c;找到dependencies下 implementation com.android.support:appcompat-…