learn.lianglianglee.com/专栏/透视HTTP协议/15 海纳百川:HTTP的实体数据.md.html
2022-05-11 18:57:05 +08:00

1045 lines
29 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/static/favicon.png">
<title>15 海纳百川HTTP的实体数据.md.html</title>
<!-- Spectre.css framework -->
<link rel="stylesheet" href="/static/index.css">
<!-- theme css & js -->
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
<div class="book-brand">
<a href="/">
<img src="/static/favicon.png">
<span>技术文章摘抄</span>
</a>
</div>
<div class="book-menu uncollapsible">
<ul class="uncollapsible">
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/专栏/透视HTTP协议/00 开篇词To Be a HTTP Hero.md.html">00 开篇词To Be a HTTP Hero.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/01 时势与英雄HTTP的前世今生.md.html">01 时势与英雄HTTP的前世今生.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/02 HTTP是什么HTTP又不是什么.md.html">02 HTTP是什么HTTP又不是什么.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/03 HTTP世界全览与HTTP相关的各种概念.md.html">03 HTTP世界全览与HTTP相关的各种概念.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/04 HTTP世界全览与HTTP相关的各种协议.md.html">04 HTTP世界全览与HTTP相关的各种协议.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/05 常说的“四层”和“七层”到底是什么?“五层”“六层”哪去了?.md.html">05 常说的“四层”和“七层”到底是什么?“五层”“六层”哪去了?.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/06 域名里有哪些门道?.md.html">06 域名里有哪些门道?.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/07 自己动手搭建HTTP实验环境.md.html">07 自己动手搭建HTTP实验环境.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/08 键入网址再按下回车,后面究竟发生了什么?.md.html">08 键入网址再按下回车,后面究竟发生了什么?.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/09 HTTP报文是什么样子的.md.html">09 HTTP报文是什么样子的.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/10 应该如何理解请求方法?.md.html">10 应该如何理解请求方法?.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/11 你能写出正确的网址吗?.md.html">11 你能写出正确的网址吗?.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/12 响应状态码该怎么用?.md.html">12 响应状态码该怎么用?.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/13 HTTP有哪些特点.md.html">13 HTTP有哪些特点.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/14 HTTP有哪些优点又有哪些缺点.md.html">14 HTTP有哪些优点又有哪些缺点.md.html</a>
</li>
<li>
<a class="current-tab" href="/专栏/透视HTTP协议/15 海纳百川HTTP的实体数据.md.html">15 海纳百川HTTP的实体数据.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/16 把大象装进冰箱HTTP传输大文件的方法.md.html">16 把大象装进冰箱HTTP传输大文件的方法.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/17 排队也要讲效率HTTP的连接管理.md.html">17 排队也要讲效率HTTP的连接管理.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/18 四通八达HTTP的重定向和跳转.md.html">18 四通八达HTTP的重定向和跳转.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/19 让我知道你是谁HTTP的Cookie机制.md.html">19 让我知道你是谁HTTP的Cookie机制.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/20 生鲜速递HTTP的缓存控制.md.html">20 生鲜速递HTTP的缓存控制.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/21 良心中间商HTTP的代理服务.md.html">21 良心中间商HTTP的代理服务.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/22 冷链周转HTTP的缓存代理.md.html">22 冷链周转HTTP的缓存代理.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/23 HTTPS是什么SSLTLS又是什么.md.html">23 HTTPS是什么SSLTLS又是什么.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/24 固若金汤的根本(上):对称加密与非对称加密.md.html">24 固若金汤的根本(上):对称加密与非对称加密.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/25 固若金汤的根本(下):数字签名与证书.md.html">25 固若金汤的根本(下):数字签名与证书.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/26 信任始于握手TLS1.2连接过程解析.md.html">26 信任始于握手TLS1.2连接过程解析.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/27 更好更快的握手TLS1.3特性解析.md.html">27 更好更快的握手TLS1.3特性解析.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/28 连接太慢该怎么办HTTPS的优化.md.html">28 连接太慢该怎么办HTTPS的优化.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/29 我应该迁移到HTTPS吗.md.html">29 我应该迁移到HTTPS吗.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/30 时代之风HTTP2特性概览.md.html">30 时代之风HTTP2特性概览.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/31 时代之风HTTP2内核剖析.md.html">31 时代之风HTTP2内核剖析.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/32 未来之路HTTP3展望.md.html">32 未来之路HTTP3展望.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/33 我应该迁移到HTTP2吗.md.html">33 我应该迁移到HTTP2吗.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/34 Nginx高性能的Web服务器.md.html">34 Nginx高性能的Web服务器.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/35 OpenResty更灵活的Web服务器.md.html">35 OpenResty更灵活的Web服务器.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/36 WAF保护我们的网络服务.md.html">36 WAF保护我们的网络服务.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/37 CDN加速我们的网络服务.md.html">37 CDN加速我们的网络服务.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/38 WebSocket沙盒里的TCP.md.html">38 WebSocket沙盒里的TCP.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/39 HTTP性能优化面面观.md.html">39 HTTP性能优化面面观.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/40 HTTP性能优化面面观.md.html">40 HTTP性能优化面面观.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/结束语 做兴趣使然的Hero.md.html">结束语 做兴趣使然的Hero.md.html</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
let sidebar = document.querySelector('.book-sidebar')
let content = document.querySelector('.off-canvas-content')
if (sidebar_toggle.classList.contains('extend')) { // show
sidebar_toggle.classList.remove('extend')
sidebar.classList.remove('hide')
content.classList.remove('extend')
} else { // hide
sidebar_toggle.classList.add('extend')
sidebar.classList.add('hide')
content.classList.add('extend')
}
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.add('show')
overlay.classList.add('show')
}
function hide_canvas() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.remove('show')
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
<div class="column col-12 col-lg-12">
<div class="book-navbar">
<!-- For Responsive Layout -->
<header class="navbar">
<section class="navbar-section">
<a onclick="open_sidebar()">
<i class="icon icon-menu"></i>
</a>
</section>
</header>
</div>
<div class="book-content" style="max-width: 960px; margin: 0 auto;
overflow-x: auto;
overflow-y: hidden;">
<div class="book-post">
<p id="tip" align="center"></p>
<div><h1>15 海纳百川HTTP的实体数据</h1>
<p>你好,我是 Chrono。</p>
<p>今天我要与你分享的话题是“海纳百川HTTP 的实体数据”。</p>
<p>这一讲是“进阶篇”的第一讲,从今天开始,我会用连续的 8 讲的篇幅来详细解析 HTTP 协议里的各种头字段,包括定义、功能、使用方式、注意事项等等。学完了这些课程,你就可以完全掌握 HTTP 协议。</p>
<p>在前面的“基础篇”里我们了解了 HTTP 报文的结构,知道一个 HTTP 报文是由“header+body”组成的。但那时我们主要研究的是 header没有涉及到 body。所以“进阶篇”的第一讲就从 HTTP 的 body 谈起。</p>
<h2>数据类型与编码</h2>
<p>在 TCP/IP 协议栈里传输数据基本上都是“header+body”的格式。但 TCP、UDP 因为是传输层的协议,它们不会关心 body 数据是什么,只要把数据发送到对方就算是完成了任务。</p>
<p>而 HTTP 协议则不同,它是应用层的协议,数据到达之后工作只能说是完成了一半,还必须要告诉上层应用这是什么数据才行,否则上层应用就会“不知所措”。</p>
<p>你可以设想一下,假如 HTTP 没有告知数据类型的功能,服务器把“一大坨”数据发给了浏览器,浏览器看到的是一个“黑盒子”,这时候该怎么办呢?</p>
<p>当然,它可以“猜”。因为很多数据都是有固定格式的,所以通过检查数据的前几个字节也许就能知道这是个 GIF 图片、或者是个 MP3 音乐文件,但这种方式无疑十分低效,而且有很大几率会检查不出来文件类型。</p>
<p>幸运的是,早在 HTTP 协议诞生之前就已经有了针对这种问题的解决方案,不过它是用在电子邮件系统里的,让电子邮件可以发送 ASCII 码以外的任意数据,方案的名字叫做“<strong>多用途互联网邮件扩展</strong>Multipurpose Internet Mail Extensions简称为 MIME。</p>
<p>MIME 是一个很大的标准规范,但 HTTP 只“顺手牵羊”取了其中的一部分,用来标记 body 的数据类型,这就是我们平常总能听到的“<strong>MIME type</strong>”。</p>
<p>MIME 把数据分成了八大类每个大类下再细分出多个子类形式是“type/subtype”的字符串巧得很刚好也符合了 HTTP 明文的特点,所以能够很容易地纳入 HTTP 头字段里。</p>
<p>这里简单列举一下在 HTTP 里经常遇到的几个类别:</p>
<ol>
<li>text即文本格式的可读数据我们最熟悉的应该就是 text/html 了,表示超文本文档,此外还有纯文本 text/plain、样式表 text/css 等。</li>
<li>image即图像文件有 image/gif、image/jpeg、image/png 等。</li>
<li>audio/video音频和视频数据例如 audio/mpeg、video/mp4 等。</li>
<li>application数据格式不固定可能是文本也可能是二进制必须由上层应用程序来解释。常见的有 application/jsonapplication/javascript、application/pdf 等,另外,如果实在是不知道数据是什么类型,像刚才说的“黑盒”,就会是 application/octet-stream即不透明的二进制数据。</li>
</ol>
<p>但仅有 MIME type 还不够,因为 HTTP 在传输时为了节约带宽有时候还会压缩数据为了不要让浏览器继续“猜”还需要有一个“Encoding type”告诉数据是用的什么编码格式这样对方才能正确解压缩还原出原始的数据。</p>
<p>比起 MIME type 来说Encoding type 就少了很多,常用的只有下面三种:</p>
<ol>
<li>gzipGNU zip 压缩格式,也是互联网上最流行的压缩格式;</li>
<li>deflatezlibdeflate压缩格式流行程度仅次于 gzip</li>
<li>br一种专门为 HTTP 优化的新压缩算法Brotli</li>
</ol>
<h2>数据类型使用的头字段</h2>
<p>有了 MIME type 和 Encoding type无论是浏览器还是服务器就都可以轻松识别出 body 的类型,也就能够正确处理数据了。</p>
<p>HTTP 协议为此定义了两个 Accept 请求头字段和两个 Content 实体头字段,用于客户端和服务器进行“<strong>内容协商</strong>”。也就是说,客户端用 Accept 头告诉服务器希望接收什么样的数据,而服务器用 Content 头告诉客户端实际发送了什么样的数据。</p>
<p><img src="assets/5191bce1329efa157a6cc37ab9e789b9.png" alt="img" /></p>
<p><strong>Accept</strong>字段标记的是客户端可理解的 MIME type可以用“,”做分隔符列出多个类型,让服务器有更多的选择余地,例如下面的这个头:</p>
<pre><code>Accept: text/html,application/xml,image/webp,image/png
</code></pre>
<p>这就是告诉服务器:“我能够看懂 HTML、XML 的文本,还有 webp 和 png 的图片,请给我这四类格式的数据”。</p>
<p>相应的,服务器会在响应报文里用头字段<strong>Content-Type</strong>告诉实体数据的真实类型:</p>
<pre><code>Content-Type: text/html
Content-Type: image/png
</code></pre>
<p>这样浏览器看到报文里的类型是“text/html”就知道是 HTML 文件会调用排版引擎渲染出页面看到“image/png”就知道是一个 PNG 文件,就会在页面上显示出图像。</p>
<p><strong>Accept-Encoding</strong>字段标记的是客户端支持的压缩格式,例如上面说的 gzip、deflate 等,同样也可以用“,”列出多个,服务器可以选择其中一种来压缩数据,实际使用的压缩格式放在响应头字段<strong>Content-Encoding</strong>里。</p>
<pre><code>Accept-Encoding: gzip, deflate, br
Content-Encoding: gzip
</code></pre>
<p>不过这两个字段是可以省略的,如果请求报文里没有 Accept-Encoding 字段,就表示客户端不支持压缩数据;如果响应报文里没有 Content-Encoding 字段,就表示响应数据没有被压缩。</p>
<h2>语言类型与编码</h2>
<p>MIME type 和 Encoding type 解决了计算机理解 body 数据的问题,但互联网遍布全球,不同国家不同地区的人使用了很多不同的语言,虽然都是 text/html但如何让浏览器显示出每个人都可理解可阅读的语言文字呢</p>
<p>这实际上就是“国际化”的问题。HTTP 采用了与数据类型相似的解决方案,又引入了两个概念:语言类型与字符集。</p>
<p>所谓的“<strong>语言类型</strong>”就是人类使用的自然语言例如英语、汉语、日语等而这些自然语言可能还有下属的地区性方言所以在需要明确区分的时候也要使用“type-subtype”的形式不过这里的格式与数据类型不同<strong>分隔符不是“/”,而是“-”</strong></p>
<p>举几个例子en 表示任意的英语en-US 表示美式英语en-GB 表示英式英语,而 zh-CN 就表示我们最常使用的汉语。</p>
<p>关于自然语言的计算机处理还有一个更麻烦的东西叫做“字符集”。</p>
<p>在计算机发展的早期,各个国家和地区的人们“各自为政”,发明了许多字符编码方式来处理文字,比如英语世界用的 ASCII、汉语世界用的 GBK、BIG5日语世界用的 Shift_JIS 等。同样的一段文字,用一种编码显示正常,换另一种编码后可能就会变得一团糟。</p>
<p>所以后来就出现了 Unicode 和 UTF-8把世界上所有的语言都容纳在一种编码方案里UTF-8 也成为了互联网上的标准字符集。</p>
<h2>语言类型使用的头字段</h2>
<p>同样的HTTP 协议也使用 Accept 请求头字段和 Content 实体头字段,用于客户端和服务器就语言与编码进行“<strong>内容协商</strong>”。</p>
<p><strong>Accept-Language</strong>字段标记了客户端可理解的自然语言,也允许用“,”做分隔符列出多个类型,例如:</p>
<pre><code>Accept-Language: zh-CN, zh, en
</code></pre>
<p>这个请求头会告诉服务器:“最好给我 zh-CN 的汉语文字,如果没有就用其他的汉语方言,如果还没有就给英文”。</p>
<p>相应的,服务器应该在响应报文里用头字段<strong>Content-Language</strong>告诉客户端实体数据使用的实际语言类型:</p>
<pre><code>Content-Language: zh-CN
</code></pre>
<p>字符集在 HTTP 里使用的请求头字段是<strong>Accept-Charset</strong>,但响应头里却没有对应的 Content-Charset而是在<strong>Content-Type</strong>字段的数据类型后面用“charset=xxx”来表示这点需要特别注意。</p>
<p>例如,浏览器请求 GBK 或 UTF-8 的字符集,然后服务器返回的是 UTF-8 编码,就是下面这样:</p>
<pre><code>Accept-Charset: gbk, utf-8
Content-Type: text/html; charset=utf-8
</code></pre>
<p>不过现在的浏览器都支持多种字符集,通常不会发送 Accept-Charset而服务器也不会发送 Content-Language因为使用的语言完全可以由字符集推断出来所以在请求头里一般只会有 Accept-Language 字段,响应头里只会有 Content-Type 字段。</p>
<p><img src="assets/0e9bcd6922fa8908bdba79d98ae5fa10.png" alt="img" /></p>
<h2>内容协商的质量值</h2>
<p>在 HTTP 协议里用 Accept、Accept-Encoding、Accept-Language 等请求头字段进行内容协商的时候还可以用一种特殊的“q”参数表示权重来设定优先级这里的“q”是“quality factor”的意思。</p>
<p>权重的最大值是 1最小值是 0.01,默认值是 1如果值是 0 就表示拒绝。具体的形式是在数据类型或语言代码后面加一个“;”然后是“q=value”。</p>
<p>这里要提醒的是“;”的用法,在大多数编程语言里“;”的断句语气要强于“,”,而在 HTTP 的内容协商里却恰好反了过来,“;”的意义是小于“,”的。</p>
<p>例如下面的 Accept 字段:</p>
<pre><code>Accept: text/html,application/xml;q=0.9,*/*;q=0.8
</code></pre>
<p>它表示浏览器最希望使用的是 HTML 文件,权重是 1其次是 XML 文件,权重是 0.9,最后是任意数据类型,权重是 0.8。服务器收到请求头后,就会计算权重,再根据自己的实际情况优先输出 HTML 或者 XML。</p>
<h2>内容协商的结果</h2>
<p>内容协商的过程是不透明的,每个 Web 服务器使用的算法都不一样。但有的时候,服务器会在响应头里多加一个<strong>Vary</strong>字段,记录服务器在内容协商时参考的请求头字段,给出一点信息,例如:</p>
<pre><code>Vary: Accept-Encoding,User-Agent,Accept
</code></pre>
<p>这个 Vary 字段表示服务器依据了 Accept-Encoding、User-Agent 和 Accept 这三个头字段,然后决定了发回的响应报文。</p>
<p>Vary 字段可以认为是响应报文的一个特殊的“版本标记”。每当 Accept 等请求头变化时Vary 也会随着响应报文一起变化。也就是说,同一个 URI 可能会有多个不同的“版本”主要用在传输链路中间的代理服务器实现缓存服务这个之后讲“HTTP 缓存”时还会再提到。</p>
<h2>动手实验</h2>
<p>上面讲完了理论部分,接下来就是实际动手操作了。可以用我们的实验环境,在 www 目录下有一个 mime 目录,里面预先存放了几个文件,可以用 URI“/15-1?name=file”的形式访问例如</p>
<pre><code>http://www.chrono.com/15-1?name=a.json
http://www.chrono.com/15-1?name=a.xml
</code></pre>
<p>在 Chrome 里打开开发者工具,就能够看到 Accept 和 Content 头:</p>
<p><img src="assets/b4d1671872876a60ad717821f0fb819e.png" alt="img" /></p>
<p>你也可以把任意的文件拷贝到 mime 目录下比如压缩包、MP3、图片、视频等再用 Chrome 访问,观察更多的 MIME type。</p>
<p>有了这些经验后,你还可以离开实验环境,直接访问各大门户网站,看看真实网络世界里的 HTTP 报文是什么样子的。</p>
<h2>小结</h2>
<p>今天我们学习了 HTTP 里的数据类型和语言类型,在这里为今天的内容做个小结。</p>
<p><img src="assets/b2118315a977969ddfcc7ab9d26cb358.png" alt="img" /></p>
<ol>
<li>数据类型表示实体数据的内容是什么,使用的是 MIME type相关的头字段是 Accept 和 Content-Type</li>
<li>数据编码表示实体数据的压缩方式,相关的头字段是 Accept-Encoding 和 Content-Encoding</li>
<li>语言类型表示实体数据的自然语言,相关的头字段是 Accept-Language 和 Content-Language</li>
<li>字符集表示实体数据的编码方式,相关的头字段是 Accept-Charset 和 Content-Type</li>
<li>客户端需要在请求头里使用 Accept 等头字段与服务器进行“内容协商”,要求服务器返回最合适的数据;</li>
<li>Accept 等头字段可以用“,”顺序列出多个可能的选项,还可以用“;q=”参数来精确指定权重。</li>
</ol>
</div>
</div>
<div>
<div style="float: left">
<a href="/专栏/透视HTTP协议/14 HTTP有哪些优点又有哪些缺点.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/专栏/透视HTTP协议/16 把大象装进冰箱HTTP传输大文件的方法.md.html">下一页</a>
</div>
</div>
</div>
</div>
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"70997cfefd193cfa","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
</body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
var path = window.location.pathname
var cookie = getCookie("lastPath");
console.log(path)
if (path.replace("/", "") === "") {
if (cookie.replace("/", "") !== "") {
console.log(cookie)
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
}
} else {
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
}
return "";
}
</script>
</html>