<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>编程生活 · Wasabi</title><link>https://sh1luo.github.io/posts/chore/</link><description>开发工具、问题排查、博客建设与个人经历记录。</description><generator>Hugo</generator><language>zh-Hans</language><copyright>文章采用知识共享署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议。</copyright><lastBuildDate>Tue, 01 Sep 2026 06:42:28 +0000</lastBuildDate><atom:link href="https://sh1luo.github.io/posts/chore/index.xml" rel="self" type="application/rss+xml"/><item><title>最近两个月及远程实习半个月小记</title><link>https://sh1luo.github.io/posts/chore/2021-06-19-recent-life/</link><pubDate>Sat, 19 Jun 2021 15:00:13 +0000</pubDate><guid>https://sh1luo.github.io/posts/chore/2021-06-19-recent-life/</guid><description>&lt;p&gt;自 3 月末完成许多轮线上面试，4 月初拿到 offer，到现在因疫情远程实习，已经过去2个多月了，时间真的一晃就过去了，虽说还没拿到正式的秋招 offer，但也距离面试不远了，有必要记录一下这段时间的生活，顺便总结反思。&lt;/p&gt;</description><content:encoded><![CDATA[<p>自 3 月末完成许多轮线上面试，4 月初拿到 offer，到现在因疫情远程实习，已经过去2个多月了，时间真的一晃就过去了，虽说还没拿到正式的秋招 offer，但也距离面试不远了，有必要记录一下这段时间的生活，顺便总结反思。</p>
<p><img class="article-image"
     src="https://blogimagee.oss-cn-beijing.aliyuncs.com/images/image-20210619155644748.png"
     alt="2021 年实习与校招阶段记录"
     loading="lazy"
     decoding="async">
</p>
<h2 id="前段时间">前段时间<a href="#%e5%89%8d%e6%ae%b5%e6%97%b6%e9%97%b4" class="anchor" aria-label="链接到“前段时间”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<p>在拿到实习 offer 后，和之前学习状态相比有点佛系，一方面是因为阶段性的目标没了，短时间内没有下一个目标；另一方面是把重心从看/背知识点转移到了深入思考，理解上来了。因为在准备时有一些知识难免是死记硬背的，或者理解不透彻，需要复现并弄清楚。</p>
<p>针对第一方面，其实我之前已经有了另一个目标的雏形，也是针对我的简历项目比较薄弱。就是寻找一个  “亮眼” 的项目来丰富简历上的项目部分，思前想后主要有四大块方向，<strong>竞赛类项目</strong>，<strong>开源贡献</strong>，<strong>造轮子</strong>，还有 <strong>秒杀/CRUD 类</strong>。</p>
<p>竞赛类项目是一些参加比赛的项目，比如一年一次的微信小程序开发大赛，用小程序解决一个实际问题或者开发一个趣味游戏。这个我现在来不及了，不适合。开源贡献这块我一直在关注 GSoC，奈何要求确实略高，另外需要提前至少 1-2 个月进行准备，为目标社区的项目贡献，联系导师等，之前加入了国内 GSoC 的 Gitter 社区讨论群，在 5 月恰巧看到群里有人说国内也搞了个开源夏令营，模仿谷歌的，是第二年举办，我就参加了，贡献了半个多月，累计 PR 10 个左右 ，也是常常肝到凌晨，目前结果怎么样还未知，不过学习到的东西真的很多，例如更多的 Git 用法，开源规范，还有具体项目的知识。这个如果成功了我觉得也应该比单纯的 CRUD 好（希望&#x1f604;</p>
<p><img class="article-image"
     src="https://blogimagee.oss-cn-beijing.aliyuncs.com/images/image-20210703211531387.png"
     alt="开源夏令营贡献记录"
     loading="lazy"
     decoding="async">
</p>
<p>参加这类夏令营，大多都不看重 proposal，也就是申请书/简历一类的，强调在实际贡献中体现代码及工程能力，所以要提前一段时间准备，了解项目。</p>
<p><img class="article-image"
     src="https://blogimagee.oss-cn-beijing.aliyuncs.com/images/image-20210619163352964.png"
     alt="开源项目申请与贡献记录"
     loading="lazy"
     decoding="async">
</p>
<p>造轮子就是创造一些小工具，这个需要有一定的代码能力，做得好了也很加分，但是我觉得写 用 tcp 实现 http，json 解析器之类的可以自己写，就没必要写简历上了，一来没啥问的，二来没啥实际用途。我个人是用 Go 写了个 rpc 框架的小轮子，不过难点也很多，很容易被怼。</p>
<p>秒杀/CRUD 类就是常见的 xx管理系统，多做一些 CRUD 的操作，不推荐，除非吃透不怕问，不过我觉得面试官比你更懂秒杀（​逃&#x1f606;</p>
<hr>
<p>除了简历，我也在思考一些常见问题如何回答的更深入，例如常见的 https，select/poll/epoll，memory，file system等等，这些问题想要答的透彻，基本都要看源码，看底层设计实现，比较耗时，但是能将清楚非常加分，也是拿到  sp/ssp 的关键一环。</p>
<h2 id="近期实习">近期实习<a href="#%e8%bf%91%e6%9c%9f%e5%ae%9e%e4%b9%a0" class="anchor" aria-label="链接到“近期实习”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<p>由于疫情远程参与了实习，前期也比较闲，主要是熟悉产品，在内网逛逛&#x1f604;，不得不说，来大厂实习的一大好处就是可以体验一下大厂内部的环境。好多都是自研，确实方便。</p>
<p>很多资料，格式/规范，除非一部分实习生没权限之外（比如腾讯的乐问），技术资料都公开。因为可能是出售的产品，所以有的代码是不能访问，需要开权限才能访问。当然内部开源的工具的源码还是随便看的。</p>
<p><img class="article-image"
     src="https://blogimagee.oss-cn-beijing.aliyuncs.com/images/image-20210703211945974.png"
     alt="远程实习期间的工作记录"
     loading="lazy"
     decoding="async">
</p>
<p>等疫情过去，7 月多我就过去线下实习体验一下，还是挺期待现场实习的。</p>
<blockquote>
<p>2021.6.30 16:02更新，已入选~</p>
<p>顺便吐槽一下，入选有时候真的看运气，有的项目4、5个人，卷的飞起，10 几个PR不一定能进，有的没人，可能写个申请书随便交点PR就进了 &#x1f602;</p>
</blockquote>
<p><img class="article-image"
     src="https://blogimagee.oss-cn-beijing.aliyuncs.com/images/image-20210630160301943.png"
     alt="开源夏令营入选结果"
     loading="lazy"
     decoding="async">
</p>
]]></content:encoded></item><item><title>记 MySQL 5.7.5 及以上实现的功能检测依赖报错分析</title><link>https://sh1luo.github.io/posts/chore/2021-02-24-%E8%AE%B0-mysql-5.7.5-%E5%8F%8A%E4%BB%A5%E4%B8%8A%E5%AE%9E%E7%8E%B0%E7%9A%84%E5%8A%9F%E8%83%BD%E6%A3%80%E6%B5%8B%E4%BE%9D%E8%B5%96/</link><pubDate>Wed, 24 Feb 2021 15:15:13 +0000</pubDate><guid>https://sh1luo.github.io/posts/chore/2021-02-24-%E8%AE%B0-mysql-5.7.5-%E5%8F%8A%E4%BB%A5%E4%B8%8A%E5%AE%9E%E7%8E%B0%E7%9A%84%E5%8A%9F%E8%83%BD%E6%A3%80%E6%B5%8B%E4%BE%9D%E8%B5%96/</guid><description>&lt;p&gt;秉承着 “软件均使用最新版本，学习均从文档入手” 的理念，学习使用 MySQL 我也是在使用最新的大版本 MySQL 8，而在技术发展过程中，一个不可忽视的因素就是兼容性，在保证新特性无误的同时必须确保以前的功能不受影响，如果不能保证这一点，那么许多使用者在迁移的时候就需要考虑很多改动成本，除非不是大改动，一般都会考虑到这一点，比如 HTTP/2 、TLS/1.3 等等。&lt;/p&gt;</description><content:encoded><![CDATA[<p>秉承着 “软件均使用最新版本，学习均从文档入手” 的理念，学习使用 MySQL 我也是在使用最新的大版本 MySQL 8，而在技术发展过程中，一个不可忽视的因素就是兼容性，在保证新特性无误的同时必须确保以前的功能不受影响，如果不能保证这一点，那么许多使用者在迁移的时候就需要考虑很多改动成本，除非不是大改动，一般都会考虑到这一点，比如 HTTP/2 、TLS/1.3 等等。</p>
<h2 id="错误描述及分析">错误描述及分析<a href="#%e9%94%99%e8%af%af%e6%8f%8f%e8%bf%b0%e5%8f%8a%e5%88%86%e6%9e%90" class="anchor" aria-label="链接到“错误描述及分析”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<p>最近为学校写一个选课平台时关系型数据库使用的是 MySQL 8+，使用 Group by 时报了这个错误：</p>
<pre tabindex="0"><code>ERROR 1055 (42000): Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column &#39;mydb.t.address&#39; which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
</code></pre><p>意思是说 select 列表中包含了非聚合列，其并不函数依赖于 Group by 子句中的列。并且和 <code>sql_mode</code> 这个配置项的  <code>only_full_group_by</code> 值冲突。不过这篇文章并不只是单纯说解决方法，如果想要解决方法请直接移步文末。</p>
<p>可以确定的是同样的使用了 Group by 的 SQL 查询语句在低版本中是正常的但是高版本却不行，猜测可能是版本差异造成的，而且还是因为这个配置项导致的。</p>
<p>所以虽然我们一开始可能看不懂这个报错信息，但是我们通过一些分析已经定位了问题，并且报错的错误码明显（ERROR 1055(42000)），报错提示信息清楚，并不难找到解决方法。</p>
<h2 id="追根溯源">追根溯源<a href="#%e8%bf%bd%e6%a0%b9%e6%ba%af%e6%ba%90" class="anchor" aria-label="链接到“追根溯源”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<p>为了更好理解错误原因，举个例子，假定你有这个查询语句：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-mysql" data-lang="mysql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="n">address</span><span class="p">,</span><span class="w"> </span><span class="nf">MAX</span><span class="p">(</span><span class="n">age</span><span class="p">)</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="k">GROUP</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">name</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><p>看起来很正常，但是如果 name 列不是主键，并且表中数据是这样子呢：</p>
<pre tabindex="0"><code>name 	address 	age
wasabi	   a         2
wasabi	   b         2
</code></pre><p>分组以后 MAX(age) 还相同怎么办，是显示 a 地址还是 b 地址呢？MySQL 这就确定不了了，所以在 <strong>MySQL5.7.5 及以上</strong> 引入了函数依赖检测，具体描述是：如果 SELECT 列表，HAVING 条件，ORDER BY 列表应用了聚合列，但是并没有在 Group by 子句中命名，就会拒绝查询，如果启用了 ONLY_FULL_GROUP_BY 这个配置项也可以引用非聚合列，可以有多列，但是必须确保每列值唯一，不然会引起冲突。</p>
<p>不过你也可以通过允许任意值来让 MySQL 为你随便选一个返回，比如这样：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-mysql" data-lang="mysql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="nf">ANY_VALUE</span><span class="p">(</span><span class="n">address</span><span class="p">),</span><span class="w"> </span><span class="nf">MAX</span><span class="p">(</span><span class="n">age</span><span class="p">)</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="k">GROUP</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">name</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><p>再举个例子，你有这些数据，使用如下查询语句：</p>
<pre tabindex="0"><code>c1 c2 c3
1  2  A
3  4  B
1  2  C

SELECT DISTINCT c1, c2 FROM t ORDER BY c3;
</code></pre><p>你应该返回哪行呢，第一行还是第三行？无法确定，这就是 MySQL 解决的问题。</p>
<h2 id="总结">总结<a href="#%e6%80%bb%e7%bb%93" class="anchor" aria-label="链接到“总结”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<p>MySQL 在 5.7.5+ 版本实现了函数依赖检测，如果某些列中不存在函数依赖关系，还没有被引用使用的话，就认为可能存在冲突，所以为 sql_mode 配置项增加了一个列表值 <code>ONLY_FULL_GROUP_BY</code> 你可以通过这些命令查看这个配置项的值：</p>
<pre tabindex="0"><code>mysql&gt; select @@global.sql_mode;
mysql&gt; select @@session.sql_mode;

+---------------------------------------------------------------+
| @@session.sql_mode                                            |
+---------------------------------------------------------------+
| ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION |
+---------------------------------------------------------------+
</code></pre><p>然后通过 set 来重新设置这个值，把这个列表项去掉即可：</p>
<pre tabindex="0"><code>set @@global.sql_mode=&#39;STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION&#39;;
</code></pre><h2 id="扩展阅读">扩展阅读<a href="#%e6%89%a9%e5%b1%95%e9%98%85%e8%af%bb" class="anchor" aria-label="链接到“扩展阅读”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<ul>
<li><a href="https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html">MySQL5.7 版本对于GroupBy的处理</a></li>
<li><a href="https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_only_full_group_by">sql_mode配置中的ONLY_FULL_GROUP_BY配置项</a></li>
<li><a href="https://dev.mysql.com/doc/refman/5.7/en/group-by-functional-dependence.html">函数依赖</a></li>
</ul>
]]></content:encoded></item><item><title>在线博客的图床解决方案</title><link>https://sh1luo.github.io/posts/chore/blog_imgs/</link><pubDate>Wed, 20 Jan 2021 15:45:13 +0000</pubDate><guid>https://sh1luo.github.io/posts/chore/blog_imgs/</guid><description>&lt;p&gt;很多人应该都听说过来人说过要写博客进行总结记录，一是说自己学习过程的复盘，二是在某些时候向不认识自己的人间接显示自己的大致水平。不过如果没有人“手把手”教的话我相信还会遇到各种各样的问题的，这篇文章就介绍一下在博客中必不可少的图片网络地址解决方法，也就是图床。它的目标是&lt;strong&gt;在你写文章的过程中可以一键插入目标图片并自动上传到网络，就像你使用本地图片一样快捷。&lt;/strong&gt;&lt;/p&gt;</description><content:encoded><![CDATA[<p>很多人应该都听说过来人说过要写博客进行总结记录，一是说自己学习过程的复盘，二是在某些时候向不认识自己的人间接显示自己的大致水平。不过如果没有人“手把手”教的话我相信还会遇到各种各样的问题的，这篇文章就介绍一下在博客中必不可少的图片网络地址解决方法，也就是图床。它的目标是<strong>在你写文章的过程中可以一键插入目标图片并自动上传到网络，就像你使用本地图片一样快捷。</strong></p>
<p>图床的目的就是把你的本地图片上传上去，从而能获得它的网络地址。当然了，只使用图床也是可以的，但是需要配合对应的工具上传拿 URL（比如 OSS）或者纯手动（GitHub/Gitee），<del>非常不方便</del> 一点也不极客，所以需要配合其他工具一键快速拿到它对应的网络地址（URL），所以我在下面介绍了我使用到的工具，仅供参考。</p>
<blockquote>
<p>评估用的稳定性指你上传的图片会不会在某个不确定的时间突然挂掉，让你的文章出现大量图片 404 丢失的情况。</p>
</blockquote>
<h1 id="图床">图床<a href="#%e5%9b%be%e5%ba%8a" class="anchor" aria-label="链接到“图床”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h1>
<p>这里我介绍三种图床，列出优缺点。可以根据需要进行选择。</p>
<h2 id="githubgitee">GitHub/Gitee<a href="#githubgitee" class="anchor" aria-label="链接到“GitHub/Gitee”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<p><img class="article-image"
     src="https://gitee.com/sh1luo/imgs/raw/master/imgs/123.png"
     alt="Gitee"
     loading="lazy"
     decoding="async">
</p>
<p><a href="https://github.com/">GitHub</a> 和 <a href="https://gitee.com/">Gitee</a> 是干什么的我就不多说了，它们也是可以作为图床使用的，前者境内访问较慢，后者较快。没有账号可以免费注册一个，然后随便创建一个公开仓库，把图片上传上去就可以获取它的 URL 了，比较简单粗暴。</p>
<p>优点：稳定性较好，空间无限，上手难度较简单</p>
<p>缺点：无法阻止盗链，无法管理（原本目的就不是作为图床，没有统计流量等功能）。</p>
<p>总结，如果你是新手，不担心自己的图片被别人<strong>轻易获取</strong> ，随缘使用图床，这个是首选。</p>
<h2 id="oss">OSS<a href="#oss" class="anchor" aria-label="链接到“OSS”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<p><img class="article-image"
     src="https://gitee.com/sh1luo/imgs/raw/master/imgs/image-20210120131946016.png"
     alt="aliyun OSS"
     loading="lazy"
     decoding="async">
</p>
<p>OSS （Object Storage Service）即对象存储服务，用来做个人博客的图床有点小题大做，腾讯云、阿里云等等提供 OSS 的平台都可以，一般需要续费，一年 50G 左右话 10 块钱左右。</p>
<p>优点：可控性比较强，提供了专业的平台，你能想到的各种需求基本都有，包括防盗链，流量统计与分析等。</p>
<p>缺点：对新手不友好，根据流量付费。</p>
<p>总结，如果你未来博客流量会不断增大，出于可控考虑选这个，流量较小的新人不考虑。</p>
<h2 id="不稳定第三方">不稳定第三方<a href="#%e4%b8%8d%e7%a8%b3%e5%ae%9a%e7%ac%ac%e4%b8%89%e6%96%b9" class="anchor" aria-label="链接到“不稳定第三方”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<p><img class="article-image"
     src="https://gitee.com/sh1luo/imgs/raw/master/imgs/image-20210120132728890.png"
     alt="SM.MS"
     loading="lazy"
     decoding="async">
</p>
<p>也可以使用这种第三方的图床，没有任何操作步骤，上传完就拿到 URL，当然事物都有两面性，这种极致快捷带来的就是不稳定，就跟当年的新浪图床一样，你不知道什么时候它就没了。。</p>
<p>优点：简单快捷打开一键就用，免费</p>
<p>缺点：不知道什么时候你的图片就被人家清了。。然后你的文章里的图片就全部 404 了。。</p>
<p>总结，随缘党首选不解释，懂得都懂。</p>
<h1 id="工具">工具<a href="#%e5%b7%a5%e5%85%b7" class="anchor" aria-label="链接到“工具”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h1>
<p>这里就介绍我使用的两个配合工具，其他的可以自行搜索。</p>
<h2 id="typora">typora<a href="#typora" class="anchor" aria-label="链接到“typora”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<p><a href="https://typora.io/">typora</a> 是一款 Markdown 文本编辑软件，我相信写过 Markdown 的都听说过，它的特点是真正的实时显示，不左右分栏，扩展性强，灵活可配置。我个人平时写 Markdown 文本都是使用这个软件，具体可以去它的官网首页看，十分推荐。</p>
<p><img class="article-image"
     src="https://gitee.com/sh1luo/imgs/raw/master/imgs/GIF%202021-1-20%2018-44-01.gif"
     alt="实时转换"
     loading="lazy"
     decoding="async">
</p>
<h2 id="picgo">PicGo<a href="#picgo" class="anchor" aria-label="链接到“PicGo”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<p><img class="article-image"
     src="https://gitee.com/sh1luo/imgs/raw/master/imgs/image-20210120134735352.png"
     alt="PicGo 图片上传界面"
     loading="lazy"
     decoding="async">
</p>
<p><a href="https://github.com/Molunerfinn/PicGo">PicGo</a> 是一款使用 Node + Electron 开发的跨平台可视化图片上传开源软件，支持 N 种图床平台，配置灵活简单，最重要的是它支持插件功能，理论上支持任意图床平台的使用（需要提供相应上传下载的 API），可以自行开发插件。操作也比较简单，把图片拖到框里就可以拿到 URL。这张图是 gitee 相关配置，其他平台类似，只需若干个必要参数即可：</p>
<p><img class="article-image"
     src="https://gitee.com/sh1luo/imgs/raw/master/imgs/image-20210120135009612.png"
     alt="PicGo 的 Gitee 图床配置界面"
     loading="lazy"
     decoding="async">
</p>
<h2 id="配合使用">配合使用<a href="#%e9%85%8d%e5%90%88%e4%bd%bf%e7%94%a8" class="anchor" aria-label="链接到“配合使用”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<p><img class="article-image"
     src="https://blogimagee.oss-cn-beijing.aliyuncs.com/images/test.png"
     alt="PicGo配置"
     loading="lazy"
     decoding="async">
</p>
<p>前面说的这两种工具可以结合使用快速进行写作，在 typora 主界面上方，文件-&gt;偏好设置-&gt;图像，修改 PicGo 路径为你安装的路径下的 exe 地址。这样就可以做到你在 typora 里编辑时直接粘贴图片到文档里一键点击上传，非常方便。当然了，如果你不需要 typora，或者使用的是别的 Markdown 编辑器，也可以只使用 PicGo。</p>
<p>而且它是开源的，如果你有任何建议可以提 Issue 或者自己为它增加特性 :)</p>
<p>我这里就有一个相关需求，自定义重命名上传文件，我想将所有图片按照我自己的规则自动重命名后再上传，比如统一按照格式 <code>year-month-day-imgID</code> 这种格式，方便后续整理图片。当然了，这里只是抛砖引玉，更多的留给大家去探索 :)</p>
<p>最后配一个最终的成品 GIF 图给大家看一下效果：</p>
<p><img class="article-image"
     src="https://blogimagee.oss-cn-beijing.aliyuncs.com/images/dddd.gif"
     alt="PicGo 与 Markdown 编辑器联动上传演示"
     loading="lazy"
     decoding="async">
</p>
<p>如果你有什么问题欢迎来找我玩，一起交流。</p>
]]></content:encoded></item><item><title>几经周折的博客变迁史</title><link>https://sh1luo.github.io/posts/chore/blog_journey/</link><pubDate>Wed, 06 Jan 2021 21:13:13 +0000</pubDate><guid>https://sh1luo.github.io/posts/chore/blog_journey/</guid><description>&lt;p&gt;仔细想起来我的博客旅程也有好多年了，兜兜转转地走了不少弯路，也确实在这个过程中学到了很多东西，大致有这些时间节点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;尝试购买了国内域名和学生机服务器，看到自己写的内容能在公网上让全世界的人看到就高兴了好久，内容很随意，自己管理。&lt;/li&gt;
&lt;li&gt;使用 &lt;a href="https://github.com/hexojs/hexo"&gt;Hexo&lt;/a&gt; 框架系统化内容，并部署在 GitHub Pages 交给 GitHub 管理。&lt;/li&gt;
&lt;li&gt;转用 &lt;a href="https://github.com/gohugoio/hugo"&gt;Hugo&lt;/a&gt; 框架，利用 GitHub Actions 自动化构建，依然在 GitHub Pages。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img class="article-image"
src="https://blogimagee.oss-cn-beijing.aliyuncs.com/images/image-20210118174633253.png"
alt="现在的博客"
loading="lazy"
decoding="async"&gt;
&lt;/p&gt;</description><content:encoded><![CDATA[<p>仔细想起来我的博客旅程也有好多年了，兜兜转转地走了不少弯路，也确实在这个过程中学到了很多东西，大致有这些时间节点：</p>
<ol>
<li>尝试购买了国内域名和学生机服务器，看到自己写的内容能在公网上让全世界的人看到就高兴了好久，内容很随意，自己管理。</li>
<li>使用 <a href="https://github.com/hexojs/hexo">Hexo</a> 框架系统化内容，并部署在 GitHub Pages 交给 GitHub 管理。</li>
<li>转用 <a href="https://github.com/gohugoio/hugo">Hugo</a> 框架，利用 GitHub Actions 自动化构建，依然在 GitHub Pages。</li>
</ol>
<p><img class="article-image"
     src="https://blogimagee.oss-cn-beijing.aliyuncs.com/images/image-20210118174633253.png"
     alt="现在的博客"
     loading="lazy"
     decoding="async">
</p>
<p>前前后后也折腾了好几年，技术上的提升我倒觉得不太大，因为折腾的挺多但是都是应用层面上的，对我来说更多的是 <strong>动手能力</strong> 的提高，有好多东西不经过自己亲手尝试是根本理解不了的，我前面很多东西就是别人远程或者一步一步告诉我让我去点这个点那个就可以了，但是回过头来问你，你根本不知道发生了什么，甚至隔天就忘，这是典型的不理解、死记硬背式学习方法。</p>
<p>我之所以最终选择将博客托管在 GitHub Pages，也是考虑都未来可能自己不会坚持购买服务器，说不定哪天没有服务器但是还写博客，那样就没地方在线看了。</p>
<p>最终采用的方案是：Hugo + GitHub Actions + GitHub Pages + 自定义域名。最终的效果是，只需要本地编写博客文件后，直接推送即可自动化构建 + 自动更新推送。</p>
<p>唯一的缺点是我的域名是在国外的平台 <a href="https://www.namesilo.com/">Namesilo</a> 购买的，无法使用 CDN 加速，导致国内用户时常访问异常，现象是无预期的卡顿。。我也没办法，想使用国内 CDN 加速，域名必须备案。</p>
<p><img class="article-image"
     src="https://blogimagee.oss-cn-beijing.aliyuncs.com/images/image-20211203152728270.png"
     alt="域名"
     loading="lazy"
     decoding="async">
</p>
<p>当时配置那个 A 记录，CNAME 记录啥的根本不知道是什么东西，也不知道 DNS 是什么，就是配，成功了就行，不成功就猜，一个个试呗。现在想想，真的是走了不少弯路，如果能系统地学一下多好。不过这些问题也都在我后来的实践以及学习过程中逐一解决了，现在遇到问题也不用像没头苍蝇一样乱撞了，这种感觉很不错 :)</p>
<p>这件事也让我大致理解了一句老话，大致是这么说的：真正厉害的技术人员不是说他写程序一定要多么多么厉害，而是他有非常强悍的问题定位能力（trouble shooting），能解决别人解决不了的问题，而这一切都源自于无数经验的积累以及深度的思考。</p>
<p>下面说我的最后一次迁移：)</p>
<blockquote>
<p>之所以有两个部分是因为买了域名后才发现必须要国内域名并且备案，才能加速国内。我买的是国外域名所以没办法，最后就用 GitHub Page + 自定义域名了。</p>
</blockquote>
<h2 id="github-page--自定义域名">GitHub Page + 自定义域名<a href="#github-page--%e8%87%aa%e5%ae%9a%e4%b9%89%e5%9f%9f%e5%90%8d" class="anchor" aria-label="链接到“GitHub Page &#43; 自定义域名”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<p>这种方案比较简单（因为GitHub 都帮我们做了），这也是我现在的方式，只不过我的域名是国外的，所以没办法使用 CDN 加速。</p>
<p>你首先需要创建一个名为 <code>&lt;YourGitHubName&gt;.github.io</code> 的仓库，比如 <code>sh1luo.github.io</code>，然后购买一个域名（国内国外都行，国内要备案），因为我们是要使用 GitHub 的自定义域名，所以需要使用 <a href="https://baike.baidu.com/item/CNAME/9845877?fr=aladdin">CNAME</a> 记录，在域名服务器处添加两条CNAME 记录 ，就像这样</p>
<p><img class="article-image"
     src="https://gitee.com/sh1luo/imgs/raw/master/imgs/image-20210126230446099.png"
     alt="DNS记录"
     loading="lazy"
     decoding="async">
</p>
<p>我是国外的域名，不能使用国内 CDN 加速，正好 CloudFlare 提供免费全球 CDN，虽然效果一般，但是它提供的免费功能也是非常的多，包括各种流量统计分析，也是很良心了。想使用 CloudFlare 当 DNS 服务器做法也非常简单。注册一个账号填上域名信息，然后会提供给我们两个 DNS 服务器域名，我们把原域名服务器商的 NameServers 改成新解析服务器提供的就好了。</p>
<p><img class="article-image"
     src="https://gitee.com/sh1luo/imgs/raw/master/imgs/image-20210126231932450.png"
     alt="Cloudflare DNS 服务器配置"
     loading="lazy"
     decoding="async">
</p>
<p>当然，如果你本来就是国内域名，就不用这么麻烦了，直接添加两条 CNAME 记录，然后在 GitHub Pages 仓库的发布分支放一个 CNAME 文件，文件内容就是你的 CNAME 地址就可以了，它会自动帮你设置为这个域名并搞定一切的:)</p>
<p>最终应该是这样：</p>
<p><img class="article-image"
     src="https://gitee.com/sh1luo/imgs/raw/master/imgs/image-20210126232019604.png"
     alt="GitHub Pages 自定义域名配置结果"
     loading="lazy"
     decoding="async">
</p>
<h2 id="cdn加速">CDN加速<a href="#cdn%e5%8a%a0%e9%80%9f" class="anchor" aria-label="链接到“CDN加速”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<p>如果你是国内的域名但是是国外的空间，就可以使用 CDN 加速了，加速区域选国内。这里需要注意源站地址由于是 GitHub Pages ，而 GitHub Pages 有 4 个 IP。都写上一行一个，回源 HOST 写你自己地址就可以了。这个回源 HOST 实际上就是 HTTP 头里的 host 字段，如果同一台机器上有多个服务的话，用来标识是哪一个服务，比较常见就是一个 IP 地址上配置了多个域名，就是通过这种给 host 首部字段的方式区分的不同 Web 站点。</p>
<p>配置完源站信息后，会生成一个 CDN 域名，比如我的是 <code>kcode.icu.cdn.dnsv1.com</code> ，然后再添加一条 CNAME 记录指向这个域名就搞定。这样配置过，你的网站经过缓存后国内访问速度能达到非常恐怖的速度，可以试着 ping 一下哦。</p>
<p>不过我这里是尝试了一下，发现国内都是不支持加速国外域名，所以放弃了。</p>
<p><img class="article-image"
     src="https://blogimagee.oss-cn-beijing.aliyuncs.com/images/image-20210118184423991.png"
     alt="CDN 配置尝试结果"
     loading="lazy"
     decoding="async">
</p>
<h2 id="自动化构建">自动化构建<a href="#%e8%87%aa%e5%8a%a8%e5%8c%96%e6%9e%84%e5%bb%ba" class="anchor" aria-label="链接到“自动化构建”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<p>自动化构建意思是说你只需要完成博客内容部分，其他生成静态页面，打包，发布部署等功能都交给一套脚本去完成。这里介绍我曾经体验过的几种。</p>
<h3 id="github-actions">Github Actions<a href="#github-actions" class="anchor" aria-label="链接到“Github Actions”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h3>
<p>这是 Github 提供的功能较为全面的自动化部署解决方案，使用方法有两种，一是在 <code>.git</code> 文件的同级目录下创建一个 <code>.github/workflow</code> 目录，然后新建 <code>gh-pages.yaml</code> 文件，另一个是在 Web端登录进入你 <code>yourname.github.io</code> 仓库， <code>Code</code> 栏右侧的 <code>Action</code> ，点进去图形化创建并编辑文件即可，这里展示我的 Action 脚本：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">GitHub Pages</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">push</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">branches</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">master</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pull_request</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">deploy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-20.04</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">concurrency</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">group</span><span class="p">:</span><span class="w"> </span><span class="l">${{ github.workflow }}-${{ github.ref }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">submodules</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">  </span><span class="c"># Fetch Hugo themes (true OR recursive)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="c"># fetch-depth: 0    # Fetch all history for .GitInfo and .Lastmod</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Setup Hugo</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">peaceiris/actions-hugo@v2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">hugo-version</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;0.80.0&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">extended</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">hugo --minify</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Deploy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">peaceiris/actions-gh-pages@v3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">if</span><span class="p">:</span><span class="w"> </span><span class="l">${{ github.ref == &#39;refs/heads/master&#39; }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">github_token</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.GITHUB_TOKEN }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">cname</span><span class="p">:</span><span class="w"> </span><span class="l">kcode.icu</span><span class="w">
</span></span></span></code></pre></div><p>也比较好理解，就是执行 <code>hugo</code> 命令然后创建一个 CNAME 文件用于自动设置一个自定义域名，然后推到 <code>gh-pages</code> 分支。</p>
<p>这种方案的优点是自定义程度高，缺点相对就是需要自己全权控制写一个控制脚本，有一定学习成本。</p>
<p>这是我的自动化构建执行结果：</p>
<p><img class="article-image"
     src="https://blogimagee.oss-cn-beijing.aliyuncs.com/images/image-20210331160251498.png"
     alt="GitHub Actions 自动构建成功记录"
     loading="lazy"
     decoding="async">
</p>
<h3 id="gitee-page">Gitee Page<a href="#gitee-page" class="anchor" aria-label="链接到“Gitee Page”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h3>
<p>Gitee 提供了常见的几个博客框架的自动化部署功能，Hugo，Hexo 和 Jekyll。也就是说，只要你根目录下有对应框架的 <code>yaml</code> 配置文件，在你每次将最新内容推至仓库时就会自动执行自动化构建，并把最新页面推到前端页面上，比较省心。</p>
<p>优点是国内速度比 Github 快，毕竟服务器近，缺点是每次推完还要手动点更新，自动的一年 99RMB。</p>
<p><img class="article-image"
     src="https://blogimagee.oss-cn-beijing.aliyuncs.com/images/image-20210331155953211.png"
     alt="Gitee Pages 手动更新入口"
     loading="lazy"
     decoding="async">
</p>
<h2 id="画图工具">画图工具<a href="#%e7%94%bb%e5%9b%be%e5%b7%a5%e5%85%b7" class="anchor" aria-label="链接到“画图工具”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<p>这里主要介绍我使用的两个画图软件。</p>
<blockquote>
<p>2021-06-19 16:44:13 更新</p>
</blockquote>
<h3 id="diagram">Diagram<a href="#diagram" class="anchor" aria-label="链接到“Diagram”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h3>
<p>原来这个软件叫 draw.io，后来改名为 <a href="https://www.diagrams.net/">Diagram</a>。</p>
<p>我之前一直使用这个工具画图，优点是免费，功能丰富，暂无发现缺点 &#x1f602; 推荐给要求较高的同学使用，画彩色图，花里胡哨图。</p>
<p><img class="article-image"
     src="https://blogimagee.oss-cn-beijing.aliyuncs.com/images/image-20210619170357266.png"
     alt="diagrams.net 绘图界面示例"
     loading="lazy"
     decoding="async">
</p>
<h3 id="ascii">Ascii<a href="#ascii" class="anchor" aria-label="链接到“Ascii”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h3>
<p>Ascii 图是我看 <a href="https://xargin.com/diagram-tools-intro/">曹大博客</a> 了解到的，可以说是一见面就夺走了我的所有抵抗力，我曾经还觉得在画编程方面的图这一块，Diagram 完全够用了，就好比 “任何比 C语言复杂的语言都能用 C语言实现” &#x1f60e;</p>
<p>但是当我看到这种风格的图我再也不想用其他工具了，因为它完全够用了（装逼完美</p>
<p>毕竟曹大在讲 plan9 汇编时说过，学习的最大动力就是装逼，不服可以去线下面基他 &#x1f604;，这种风格的图真的 geek 风拉满，“程序员味”十足。</p>
<p><img class="article-image"
     src="https://blogimagee.oss-cn-beijing.aliyuncs.com/images/image-20210619170049500.png"
     alt="ASCII 文本图示例"
     loading="lazy"
     decoding="async">
</p>
<p>说点正经的，我喜欢这种图最大的优点是 <strong>简洁</strong>，无他。我不用操心颜色搭配，字体大小，迁移存储等一切问题，只专心图所能表达出的含义就 OK 了。这里推荐两个站点：</p>
<ul>
<li>asciiflow（<a href="https://asciiflow.com/">https://asciiflow.com/</a>）</li>
<li>Textik（<a href="https://textik.com/">https://textik.com/</a>）</li>
</ul>
<h3 id="sketch">Sketch<a href="#sketch" class="anchor" aria-label="链接到“Sketch”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h3>
<p>这个软件是比较专业的设计师使用的，也可以用来画一般的文章插图，缺点是收费，只能在 Mac 下使用。我没有使用过，可以自行搜索。</p>
<h2 id="说在最后">说在最后<a href="#%e8%af%b4%e5%9c%a8%e6%9c%80%e5%90%8e" class="anchor" aria-label="链接到“说在最后”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<p>这里说几点我个人看法。</p>
<ul>
<li>博客要<strong>注重内容</strong>，我看好多人把界面弄的 <strong>过于花哨</strong> 不注重实际内容，我觉得如果你内容很丰富的情况下可以适当美化，但是弄个 BGM 或者动态换图什么的还是不要了。</li>
<li>多踩坑不一定是坏事，在大学这个试错成本很低的环境里就要多尝试，在踩坑的过程中把基础打牢，锻炼问题定位能力，多思考，多总结，日积月累肯定有所提升。</li>
<li>写博客给别人看的同时要多去看别人的成果，尤其是大佬们的，进步很快。</li>
<li>最后一点也是最重要的，不要主要依靠别人的博客学习，大部分博客不系统，甚至有的博主不理解就生搬硬套，很容易给新人带来误解。努力学习一手资料，官方文档、书籍都是不错的选择，其次才是较为系统阐述的博客、零散的博客。</li>
</ul>
]]></content:encoded></item><item><title>Linux du/df 命令总结</title><link>https://sh1luo.github.io/posts/chore/linux_df_du/</link><pubDate>Wed, 04 Nov 2020 18:15:13 +0000</pubDate><guid>https://sh1luo.github.io/posts/chore/linux_df_du/</guid><description>&lt;p&gt;linux 在我们日常学习中用的非常多，连接远程 linux 服务器，使用各种 linux 命令，或者物理机就是 linux 系统或者 MacOS。如果你想要学习进阶知识，linux 已经是必不可少的一个组件了。这篇博客记录一下 linux 下常被混淆的 du/df 命令。&lt;/p&gt;</description><content:encoded><![CDATA[<p>linux 在我们日常学习中用的非常多，连接远程 linux 服务器，使用各种 linux 命令，或者物理机就是 linux 系统或者 MacOS。如果你想要学习进阶知识，linux 已经是必不可少的一个组件了。这篇博客记录一下 linux 下常被混淆的 du/df 命令。</p>
<h2 id="df">df<a href="#df" class="anchor" aria-label="链接到“df”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<p>df 就是 “disk free” 的缩写，一般是用来快速浏览你的文件系统，会显示所有磁盘的大小，使用空间，剩余空间，使用百分比还有磁盘挂载到哪个分区上了。使用的时候一般要带上 <code>-h</code> 参数来增加可读性。这些数据都是从挂载点或者文件系统级别计算出来的，比较 “宏观”。</p>
<p><img class="article-image"
     src="https://gitee.com/sh1luo/imgs/raw/master/imgs/sh.png"
     alt="df -h"
     loading="lazy"
     decoding="async">
</p>
<p>而且，<code>-h</code> 标记会使你的数据四舍五入更方便显示，比如它显示 3G 的大小，实际上可能是 2.9G 或者  3.1G，并不确定。</p>
<h2 id="du">du<a href="#du" class="anchor" aria-label="链接到“du”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<p>du 就是 “disk usage” 的缩写，如果你需要计算某个特定目录大小的时候非常好用。一般搭配 <code>-sh</code> 参数让结果更易读，比如这样：</p>
<p><img class="article-image"
     src="https://gitee.com/sh1luo/imgs/raw/master/imgs/du.png"
     alt="du"
     loading="lazy"
     decoding="async">
</p>
<h2 id="df-vs-du">df VS. du<a href="#df-vs-du" class="anchor" aria-label="链接到“df VS. du”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<p>看了这两个命令大致用法后，它们在使用场景是什么呢，众所周知不结合使用场景的技术都是耍流氓。大致可以这样总结：<code>df</code> 命令提供一个关于你整个文件系统的概览，比如你多少空间正在使用等等，<code>du</code> 命令更倾向于计算某个目录的精确快照。</p>
<p>如果非要说哪个的结果更有用一点，我觉得是 <code>du</code>。举个例子，我在 <code>/var</code> 目录下安装一个第三方包时遇到了一个目录空间不足的错误，那么我就可以先跑一下 <code>df</code> 命令去看看这个目录的空间来确认一下这是不是真的。我如果看到 <code>/var</code> 有足够空间，那我会接着跑一下 <code>du /var</code> 去看看是它的哪个子目录因为空间不够报的错。这样我就可以一直通过 <code>df </code> 去缩小出错目录的范围，直到找到问题。</p>
<p>理解这些命令的区别能让我们串联使用这些工具，从而更快的定位并解决我们所面临的绝大多数的问题。</p>
]]></content:encoded></item><item><title>StarUML3.x 版本解包分析</title><link>https://sh1luo.github.io/posts/chore/staruml-3.2.x/</link><pubDate>Wed, 20 May 2020 22:11:13 +0000</pubDate><guid>https://sh1luo.github.io/posts/chore/staruml-3.2.x/</guid><description>&lt;p&gt;由于软件工程基础课需要，搜索发现这个工具还算毕竟好用的，下载了这个工具发现需要注册码，遂进行一番查找，最后确定如下方法。之所以写博客记录下来是因为&lt;strong&gt;有两个坑点&lt;/strong&gt;。&lt;/p&gt;</description><content:encoded><![CDATA[<p>由于软件工程基础课需要，搜索发现这个工具还算毕竟好用的，下载了这个工具发现需要注册码，遂进行一番查找，最后确定如下方法。之所以写博客记录下来是因为<strong>有两个坑点</strong>。</p>
<blockquote>
<p>OS：Windows 10 专业版。</p>
<p>更新时间：2020/5/17</p>
<p>StarUML版本：3.2.2</p>
</blockquote>
<h2 id="破解思路">破解思路<a href="#%e7%a0%b4%e8%a7%a3%e6%80%9d%e8%b7%af" class="anchor" aria-label="链接到“破解思路”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<p>该工具是用 Electron 框架所写，代码是通过 asar 工具打包而成，我们只需要利用该工具反编译出源代码，修改相关判断逻辑即可。</p>
<h2 id="具体做法">具体做法<a href="#%e5%85%b7%e4%bd%93%e5%81%9a%e6%b3%95" class="anchor" aria-label="链接到“具体做法”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<h3 id="下载安装-node">下载安装 node<a href="#%e4%b8%8b%e8%bd%bd%e5%ae%89%e8%a3%85-node" class="anchor" aria-label="链接到“下载安装 node”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h3>
<p>如果不会，自（bie）行（lai）解（zhao）决（wo）。</p>
<h3 id="使用-cnpm-包管理工具下载-asar">使用 cnpm 包管理工具下载 asar<a href="#%e4%bd%bf%e7%94%a8-cnpm-%e5%8c%85%e7%ae%a1%e7%90%86%e5%b7%a5%e5%85%b7%e4%b8%8b%e8%bd%bd-asar" class="anchor" aria-label="链接到“使用 cnpm 包管理工具下载 asar”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h3>
<p>由于众所周知的原因，用 cnpm 下载，先安装 cnpm并使用淘宝镜像源：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">npm install -g cnpm --registry<span class="o">=</span>https://registry.npm.taobao.org	// 下载 asar
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">cnpm install -g asar // 安装 asar
</span></span></code></pre></div><h3 id="解压-asar-文件修改源代码">解压 asar 文件，修改源代码<a href="#%e8%a7%a3%e5%8e%8b-asar-%e6%96%87%e4%bb%b6%e4%bf%ae%e6%94%b9%e6%ba%90%e4%bb%a3%e7%a0%81" class="anchor" aria-label="链接到“解压 asar 文件，修改源代码”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h3>
<p>找到 StarUML 安装目录里 resource 文件夹下的 app.asar 文件，执行解压：</p>
<pre tabindex="0"><code>asar extract app.asar app
</code></pre><h4 id="坑点一解压盘符问题">坑点一、解压盘符问题<a href="#%e5%9d%91%e7%82%b9%e4%b8%80%e8%a7%a3%e5%8e%8b%e7%9b%98%e7%ac%a6%e9%97%ae%e9%a2%98" class="anchor" aria-label="链接到“坑点一、解压盘符问题”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h4>
<p>由于 StarUML 默认安装位置是 C 盘，直接在 StarUML 的安装目录下执行会有权限问题，会报类似这种错误：</p>
<pre tabindex="0"><code>$ asar extract app.asar app
internal/fs/utils.js:230
 throw err;
 ^
   
   Error: EPERM: operation not permitted, open &#39;app\package.json&#39;
 at Object.openSync (fs.js:458:3)
 at Object.writeFileSync (fs.js:1279:35)
    at Object.module.exports.extractAll (C:\Users\shiluo\AppData\Roaming\npm\node_modules\asar\lib\asar.js:201:10)
    at Command.&lt;anonymous&gt; (C:\Users\shiluo\AppData\Roaming\npm\node_modules\asar\bin\asar.js:72:10)
    at Command.listener [as _actionHandler] (C:\Users\shiluo\AppData\Roaming\npm\node_modules\asar\node_modules\_commander@5.1.0@commander\index.js:413:31)
    at Command._parseCommand (C:\Users\shiluo\AppData\Roaming\npm\node_modules\asar\node_modules\_commander@5.1.0@commander\index.js:914:14)
    at Command._dispatchSubcommand (C:\Users\shiluo\AppData\Roaming\npm\node_modules\asar\node_modules\_commander@5.1.0@commander\index.js:865:18)
    at Command._parseCommand (C:\Users\shiluo\AppData\Roaming\npm\node_modules\asar\node_modules\_commander@5.1.0@commander\index.js:882:12)
    at Command.parse (C:\Users\shiluo\AppData\Roaming\npm\node_modules\asar\node_modules\_commander@5.1.0@commander\index.js:717:10)
    at Object.&lt;anonymous&gt; (C:\Users\shiluo\AppData\Roaming\npm\node_modules\asar\bin\asar.js:80:9) {
   errno: -4048,
   syscall: &#39;open&#39;,
  code: &#39;EPERM&#39;,
  path: &#39;app\\package.json&#39;
  }
</code></pre><p>解决方法：只需要把 app.asar 单独拿出来放其他盘比如你放 D 盘（不要在根目录，下面有解释），然后再次执行上面这个命令即可。</p>
<p>然后找到解压出来的源代码的 app/src/engine/license-manager.js 文件，大概在 120 行：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">checkLicenseValidity</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">this</span><span class="p">.</span><span class="nx">validate</span><span class="p">().</span><span class="nx">then</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">setStatus</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// setStatus(this, false)
</span></span></span><span class="line"><span class="cl">    <span class="c1">// UnregisteredDialog.showDialog()
</span></span></span><span class="line"><span class="cl">    <span class="nx">setStatus</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>可以很明显看到是一个 Promise ，成功了将注册状态设置为 true，否则设置为 false，并且弹窗提示。所以嘛。。就在这里下手，把逻辑改掉，如上图所示。</p>
<h3 id="重新打包运行">重新打包运行<a href="#%e9%87%8d%e6%96%b0%e6%89%93%e5%8c%85%e8%bf%90%e8%a1%8c" class="anchor" aria-label="链接到“重新打包运行”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">asar pack app app.asar
</span></span></code></pre></div><h4 id="坑点二打包路径问题">坑点二、打包路径问题<a href="#%e5%9d%91%e7%82%b9%e4%ba%8c%e6%89%93%e5%8c%85%e8%b7%af%e5%be%84%e9%97%ae%e9%a2%98" class="anchor" aria-label="链接到“坑点二、打包路径问题”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h4>
<p>上面说到不要把解压得到的 app 目录放在根目录下，否则执行打包命令后会报这个错误，这里我拿 D 盘举个栗子：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ asar pack app app.asar
</span></span><span class="line"><span class="cl"><span class="o">(</span>node:9504<span class="o">)</span> UnhandledPromiseRejectionWarning: Error: EPERM: operation not permitted, mkdir <span class="s1">&#39;D:\&#39;</span>
</span></span><span class="line"><span class="cl"><span class="o">(</span>node:9504<span class="o">)</span> UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async <span class="k">function</span> without a catch block, or by rejecting a promise which was not handled with .catch<span class="o">()</span>. To terminate the node process on unhandled promise rejection, use the CLI flag <span class="sb">`</span>--unhandled-rejections<span class="o">=</span>strict<span class="sb">`</span> <span class="o">(</span>see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode<span class="o">)</span>. <span class="o">(</span>rejection id: 1<span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="o">(</span>node:9504<span class="o">)</span> <span class="o">[</span>DEP0018<span class="o">]</span> DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero <span class="nb">exit</span> code.
</span></span></code></pre></div><p>执行完后会得到一个 80 MB 左右的 asar 文件，拿它覆盖原安装包内的 asar 文件，运行 StarUML。</p>
<p>搞定。</p>
]]></content:encoded></item><item><title>OBS/Bilibili 直播姬的显示器捕获黑屏解决方案</title><link>https://sh1luo.github.io/posts/chore/obs/</link><pubDate>Sun, 26 Apr 2020 11:34:13 +0000</pubDate><guid>https://sh1luo.github.io/posts/chore/obs/</guid><description>&lt;blockquote&gt;
&lt;p&gt;OBS 与 Bilibili直播姬 操作类似。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="问题起因"&gt;问题起因&lt;a href="#%e9%97%ae%e9%a2%98%e8%b5%b7%e5%9b%a0" class="anchor" aria-label="链接到“问题起因”"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;&lt;path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"&gt;&lt;/path&gt;&lt;line x1="8" y1="12" x2="16" y2="12"&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;由于我最近重新安装了系统，在重新下载 OBS 后发现捕获不到桌面。我之前解决方法就是直接在&lt;strong&gt;显卡控制面板&lt;/strong&gt; 里将 OBS 设置为集显就行了。但是这次设置了也不行，有可能是我下载的显卡驱动版本不对。。于是就找到了这个终极解决方案。&lt;/p&gt;</description><content:encoded><![CDATA[<blockquote>
<p>OBS 与 Bilibili直播姬 操作类似。</p>
</blockquote>
<h2 id="问题起因">问题起因<a href="#%e9%97%ae%e9%a2%98%e8%b5%b7%e5%9b%a0" class="anchor" aria-label="链接到“问题起因”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<p>由于我最近重新安装了系统，在重新下载 OBS 后发现捕获不到桌面。我之前解决方法就是直接在<strong>显卡控制面板</strong> 里将 OBS 设置为集显就行了。但是这次设置了也不行，有可能是我下载的显卡驱动版本不对。。于是就找到了这个终极解决方案。</p>
<p><img class="article-image"
     src="https://blogimagee.oss-cn-beijing.aliyuncs.com/images/image-20211203163901757.png"
     alt="NVIDIA 控制面板中的 OBS 图形处理器设置"
     loading="lazy"
     decoding="async">
</p>
<h2 id="问题环境">问题环境<a href="#%e9%97%ae%e9%a2%98%e7%8e%af%e5%a2%83" class="anchor" aria-label="链接到“问题环境”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<p>操作系统：Win10 64位 专业版 - 1909</p>
<p>CPU 集成显卡型号：Inter(R) UHD Graphics 630</p>
<p>独立显卡型号：NVIDIA GeForce GTX 1050</p>
<p>OBS 版本：25.0.4(64 bit)</p>
<blockquote>
<p>注：配置和我不一样也有可能解决，不用纠结</p>
</blockquote>
<h2 id="问题描述">问题描述<a href="#%e9%97%ae%e9%a2%98%e6%8f%8f%e8%bf%b0" class="anchor" aria-label="链接到“问题描述”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<p><img class="article-image"
     src="https://blogimagee.oss-cn-beijing.aliyuncs.com/images/obs-noscreen.png"
     alt="OBS 显示器捕获黑屏"
     loading="lazy"
     decoding="async">
</p>
<p>在来源中添加<strong>显示器捕获后</strong> ，是黑屏，<strong>无法捕获到显示器</strong>。</p>
<h2 id="问题分析">问题分析<a href="#%e9%97%ae%e9%a2%98%e5%88%86%e6%9e%90" class="anchor" aria-label="链接到“问题分析”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<p>目前网上能搜集到的解决方案大部分是以下几种：</p>
<ul>
<li>右键 OBS 属性 - 兼容性 - 兼容模式 - 以兼容模式运行这个程序（win7）设置 -  以管理员方式运行</li>
<li>桌面上右键打开 NVIDIA 控制面板，管理 3D 设置 - 程序设置，找到 OBS，设置为集成图形。</li>
</ul>
<p><img class="article-image"
     src="https://blogimagee.oss-cn-beijing.aliyuncs.com/images/obs-gpu.png"
     alt="Windows 任务管理器中的 GPU 引擎列"
     loading="lazy"
     decoding="async">
</p>
<p>但是我都试了都解决不了，我就去搜了一下问题的原因，<strong>最终</strong>发现 OBS <strong>只能捕捉到和自己跟自己使用相同显卡的应用</strong>，然后我右键打开任务管理器，发现笔记本桌面使用的是我的集显。所以就需要把 OBS 设置为应用集显。</p>
<h2 id="解决方法">解决方法<a href="#%e8%a7%a3%e5%86%b3%e6%96%b9%e6%b3%95" class="anchor" aria-label="链接到“解决方法”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<img src="https://blogimagee.oss-cn-beijing.aliyuncs.com/images/obs-setting.png" alt="图形设置" style="zoom:50%;" />
<p>左下角开始按钮 - 设置 - 系统 - 显示 - 图形设置 - 经典应用里浏览添加 OBS ，点击选项，设置为节能，节能就是使用集显。然后你再打开 OBS&hellip;&hellip;</p>
<p>这时候你就会发现你成功了。</p>
<p>其他的推流软件也一样，比如什么 B 站直播姬等同理。</p>
<p>以上。</p>
]]></content:encoded></item><item><title>Go+QML构建桌面应用</title><link>https://sh1luo.github.io/posts/chore/go+qamel/</link><pubDate>Mon, 23 Mar 2020 14:51:15 +0000</pubDate><guid>https://sh1luo.github.io/posts/chore/go+qamel/</guid><description>&lt;h2 id="引文"&gt;引文&lt;a href="#%e5%bc%95%e6%96%87" class="anchor" aria-label="链接到“引文”"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;&lt;path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"&gt;&lt;/path&gt;&lt;line x1="8" y1="12" x2="16" y2="12"&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;开发的项目是直播助手，相关介绍在 &lt;a href="https://sh1luo.github.io/posts/b%E7%AB%99%E7%9B%B4%E6%92%AD%E5%8A%A9%E6%89%8B%E5%BC%80%E5%8F%91%E4%B8%8A/"&gt;B 站直播协议分析上&lt;/a&gt; 有介绍，这里说一下技术选型方面遇到的问题，踩的坑，希望给后续的人一点建议和参考。&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="引文">引文<a href="#%e5%bc%95%e6%96%87" class="anchor" aria-label="链接到“引文”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<p>开发的项目是直播助手，相关介绍在 <a href="/posts/b%E7%AB%99%E7%9B%B4%E6%92%AD%E5%8A%A9%E6%89%8B%E5%BC%80%E5%8F%91%E4%B8%8A/">B 站直播协议分析上</a> 有介绍，这里说一下技术选型方面遇到的问题，踩的坑，希望给后续的人一点建议和参考。</p>
<h2 id="技术选型">技术选型<a href="#%e6%8a%80%e6%9c%af%e9%80%89%e5%9e%8b" class="anchor" aria-label="链接到“技术选型”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<p>因为当时第一步的主要想开发的功能是弹幕点歌，但是在稍做分析之后发现如何对 MP3 文件进行解码播放是个问题，自己实现可能略显复杂，也不是这个工具的主要想解决的问题。然后就考虑到使用什么组件来解码并播放音乐。</p>
<p><strong>最初</strong>的想法是另写一套前端，和已经写好的这套后端进行通信，来呈现内容，播放音乐等，这是一定可以实现的，但是同一条信息进行两次传输效率太低，于是放弃，想在同一端执行，增加效率。Go 确实不擅长写 GUI 程序（C#完美。。），决定使用 <a href="https://github.com/go-qamel/qamel">qamel</a>来使用开发，其和 <a href="https://github.com/therecipe/qt">therecipe</a> 比较如下：</p>
<ul>
<li>轻量（只使用少量的类）。构建简单的 QML 应用，比如 <code>QApplication</code>、 <code>QQuickView</code> and <code>QQMLApplicationEngine</code>。而 <code>therecipe/qt</code> 绑定了所有的 QT 模块。</li>
<li>安装简单。需要拿到编译好的 qamel 二进制文件（你可以选择自己编译构建，也可以选择直接从别人手里获取），放入 <code>path</code> 环境变量，再设置好配置，就可以构建自己的引用了。而 <code>therecipe/qt</code> 需要执行 <code>setup</code> 等一系列安装过程，繁琐且容易失败。</li>
<li>编译速度快。因为绑定部分代码是手动写的，使得绑定安装过程非常快，而 <code>therecipe/qt</code> 是安装时即时生成的，所以<strong>非常慢</strong>，可以自己体验一下。。</li>
</ul>
<blockquote>
<p>还有个缺点，目前跨平台编译只支持 windows 和 linux，macOS 作者未测试，目前未知。而<code>therecipe/qt</code> 支持 Linux, Windows, macOS, Android, Sailfish, Ubuntu Touch, WASM, and iOS各种平台。</p>
</blockquote>
<h2 id="坑在哪">坑在哪<a href="#%e5%9d%91%e5%9c%a8%e5%93%aa" class="anchor" aria-label="链接到“坑在哪”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<blockquote>
<p>以下均是在 Windows ，amd64 位环境下的结果。</p>
</blockquote>
<ul>
<li>
<p>不要在系统自带 CMD 或 PowerShell 中构建应用，不然会报代码第二行找不到 <code>QApplication</code> 的错误，换用 <code>cmder</code> 就可以解决，这个问题已经被证实，但是并未找到原因。</p>
</li>
<li>
<p>目前（Latest commit<a href="https://github.com/go-qamel/qamel/commit/72f913c4bdc9e8cf6f99d511c5eaa83233d36df5">72f913c</a>on 27 Nov 2019），<strong>不要用 Qt5.14.1及以上版本</strong>，但是版本号要大于5.11，不然都会报错。比如图示：</p>
</li>
</ul>
<blockquote>
<p>原错误截图所在的历史图床已经失效。错误现象是构建时提示找不到 <code>QApplication</code>；不影响下文结论。</p>
</blockquote>
<p>因为作者基本不维护，所以提的问题也没解决，目前也不确定是哪方面的问题，所以记录一下，希望以后使用此库的人能注意。</p>
<p>以上。</p>
]]></content:encoded></item><item><title>Go语言net/url包源码初探</title><link>https://sh1luo.github.io/posts/chore/go-net-url%E5%8C%85%E6%BA%90%E7%A0%81%E5%88%9D%E6%8E%A2/</link><pubDate>Wed, 11 Mar 2020 22:49:51 +0000</pubDate><guid>https://sh1luo.github.io/posts/chore/go-net-url%E5%8C%85%E6%BA%90%E7%A0%81%E5%88%9D%E6%8E%A2/</guid><description>&lt;h2 id="问题起因"&gt;问题起因&lt;a href="#%e9%97%ae%e9%a2%98%e8%b5%b7%e5%9b%a0" class="anchor" aria-label="链接到“问题起因”"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;&lt;path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"&gt;&lt;/path&gt;&lt;line x1="8" y1="12" x2="16" y2="12"&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;问题的起因是最近需要写一个小工具，期间因为需要自己在服务器上搭建了一个 Web 服务来提供 RESTful API 接口，在请求接口的时候需要拼接 url，没有考虑到部分字符的编码问题，导致 &lt;a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status/400"&gt;400 Bad Request&lt;/a&gt; 错误。原来使用的是这样：&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="问题起因">问题起因<a href="#%e9%97%ae%e9%a2%98%e8%b5%b7%e5%9b%a0" class="anchor" aria-label="链接到“问题起因”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<p>问题的起因是最近需要写一个小工具，期间因为需要自己在服务器上搭建了一个 Web 服务来提供 RESTful API 接口，在请求接口的时候需要拼接 url，没有考虑到部分字符的编码问题，导致 <a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status/400">400 Bad Request</a> 错误。原来使用的是这样：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">url</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;%ssearch?keywords=%s %s&amp;limit=1&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">Mp3Server</span><span class="p">,</span><span class="w"> </span><span class="nx">name</span><span class="p">,</span><span class="w"> </span><span class="nx">singer</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nx">resp</span><span class="p">,</span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="nx">url</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">......</span><span class="w">
</span></span></span></code></pre></div><p>这个 name 和 singer 都有可能是中文，直接请求服务器无法识别，所以导致 400 无效请求的错误。后来改成这样，就成功了：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">q</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">url</span><span class="p">.</span><span class="nx">Values</span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nx">q</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;keywords&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">name</span><span class="o">+</span><span class="s">&#34; &#34;</span><span class="o">+</span><span class="nx">singer</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nx">q</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;limit&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;1&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nx">u</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">url</span><span class="p">.</span><span class="nx">URL</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">Scheme</span><span class="p">:</span><span class="w">   </span><span class="s">&#34;http&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">Host</span><span class="p">:</span><span class="w">     </span><span class="s">&#34;shiluo.design:3000&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">Path</span><span class="p">:</span><span class="w">     </span><span class="s">&#34;search&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">RawQuery</span><span class="p">:</span><span class="w"> </span><span class="nx">q</span><span class="p">.</span><span class="nf">Encode</span><span class="p">(),</span><span class="w"> 				</span><span class="c1">// 关键在这</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nx">resp</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="nx">u</span><span class="p">.</span><span class="nf">String</span><span class="p">())</span><span class="w">
</span></span></span></code></pre></div><p>问题解决的关键就是在 <code>RawQuery: q.Encode()</code> 。由于我一直有看源码的习惯，我就 ctrl+左键跟进去把处理逻辑看了几遍，收获不小，特别记录下来。</p>
<h2 id="整体处理逻辑">整体处理逻辑<a href="#%e6%95%b4%e4%bd%93%e5%a4%84%e7%90%86%e9%80%bb%e8%be%91" class="anchor" aria-label="链接到“整体处理逻辑”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<p>整个方法的主逻辑过程：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Encode encodes the values into ``URL encoded&#39;&#39; form</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// (&#34;bar=baz&amp;foo=quux&#34;) sorted by key.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">v</span><span class="w"> </span><span class="nx">Values</span><span class="p">)</span><span class="w"> </span><span class="nf">Encode</span><span class="p">()</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">v</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="s">&#34;&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="kd">var</span><span class="w"> </span><span class="nx">buf</span><span class="w"> </span><span class="nx">strings</span><span class="p">.</span><span class="nx">Builder</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">keys</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nb">make</span><span class="p">([]</span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="nb">len</span><span class="p">(</span><span class="nx">v</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">for</span><span class="w"> </span><span class="nx">k</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">v</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">keys</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">append</span><span class="p">(</span><span class="nx">keys</span><span class="p">,</span><span class="w"> </span><span class="nx">k</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">sort</span><span class="p">.</span><span class="nf">Strings</span><span class="p">(</span><span class="nx">keys</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">for</span><span class="w"> </span><span class="nx">_</span><span class="p">,</span><span class="w"> </span><span class="nx">k</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">keys</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">vs</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">v</span><span class="p">[</span><span class="nx">k</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">keyEscaped</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nf">QueryEscape</span><span class="p">(</span><span class="nx">k</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">for</span><span class="w"> </span><span class="nx">_</span><span class="p">,</span><span class="w"> </span><span class="nx">v</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">vs</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="k">if</span><span class="w"> </span><span class="nx">buf</span><span class="p">.</span><span class="nf">Len</span><span class="p">()</span><span class="w"> </span><span class="p">&gt;</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">				</span><span class="nx">buf</span><span class="p">.</span><span class="nf">WriteByte</span><span class="p">(</span><span class="sc">&#39;&amp;&#39;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">buf</span><span class="p">.</span><span class="nf">WriteString</span><span class="p">(</span><span class="nx">keyEscaped</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">buf</span><span class="p">.</span><span class="nf">WriteByte</span><span class="p">(</span><span class="sc">&#39;=&#39;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">buf</span><span class="p">.</span><span class="nf">WriteString</span><span class="p">(</span><span class="nf">QueryEscape</span><span class="p">(</span><span class="nx">v</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="nx">buf</span><span class="p">.</span><span class="nf">String</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>其最终目的是把类似 <code>http://shiluo.design:3000/search?keywords=海阔天空 beyond&amp;limit=1</code> 这种的 URL 编码为服务器可识别的 <code>http://shiluo.design:3000/search?keywords=%E6%AD%8C%E6%9B%B2+%E6%AD%8C%E6%89%8B&amp;limit=1</code> 这种格式的 URL。</p>
<p>遍历的 vs 就是某个 key 对应的所有 value 值，<code>buf.WriteByte('&amp;')</code> 是在任何一次写入后追加 &amp; 连接查询字符串。我们重点关注最后一行代码 <code>buf.WriteString(QueryEscape(v))</code> 。</p>
<h2 id="深入剖析">深入剖析<a href="#%e6%b7%b1%e5%85%a5%e5%89%96%e6%9e%90" class="anchor" aria-label="链接到“深入剖析”"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg></a></h2>
<blockquote>
<p>Values 是自定义的 type Values map[string][]string</p>
</blockquote>
<p>处理过程是：判断 Values 是否为空，如果是返回空字符串。设置一个 Builder 用来构建未来需要返回的字符串。由于 url 的 query string 查询时一个 key 允许有多个 value，所以遍历每个 key 的所有value值，判定 key 值中是否有需要转义的字符，拿到转义处理后的字符串，然后通过 <code>buf.WriteString(keyEscaped)</code> 和 <code>buf.WriteByte('=')</code> 写入<strong>一对 key-value 键值对</strong> 。也就是说 key 判断转义了一次，它下面的 n 个 value 都判断转义了一次，最终<strong>处理完一个 key 时，写入了 n 对键值对</strong> 。其实最核心的逻辑在 <code>QueryEscape()</code> 这个函数。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// QueryEscape escapes the string so it can be safely placed</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// inside a URL query.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">QueryEscape</span><span class="p">(</span><span class="nx">s</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="nf">escape</span><span class="p">(</span><span class="nx">s</span><span class="p">,</span><span class="w"> </span><span class="nx">encodeQueryComponent</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">escape</span><span class="p">(</span><span class="nx">s</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">mode</span><span class="w"> </span><span class="nx">encoding</span><span class="p">)</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">spaceCount</span><span class="p">,</span><span class="w"> </span><span class="nx">hexCount</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">for</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="p">&lt;</span><span class="w"> </span><span class="nb">len</span><span class="p">(</span><span class="nx">s</span><span class="p">);</span><span class="w"> </span><span class="nx">i</span><span class="o">++</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">c</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">s</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">if</span><span class="w"> </span><span class="nf">shouldEscape</span><span class="p">(</span><span class="nx">c</span><span class="p">,</span><span class="w"> </span><span class="nx">mode</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="k">if</span><span class="w"> </span><span class="nx">c</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="sc">&#39; &#39;</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="nx">mode</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nx">encodeQueryComponent</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">				</span><span class="nx">spaceCount</span><span class="o">++</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">				</span><span class="nx">hexCount</span><span class="o">++</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">spaceCount</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="nx">hexCount</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="nx">s</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="kd">var</span><span class="w"> </span><span class="nx">buf</span><span class="w"> </span><span class="p">[</span><span class="mi">64</span><span class="p">]</span><span class="kt">byte</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="kd">var</span><span class="w"> </span><span class="nx">t</span><span class="w"> </span><span class="p">[]</span><span class="kt">byte</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">required</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nb">len</span><span class="p">(</span><span class="nx">s</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">2</span><span class="o">*</span><span class="nx">hexCount</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">required</span><span class="w"> </span><span class="o">&lt;=</span><span class="w"> </span><span class="nb">len</span><span class="p">(</span><span class="nx">buf</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">t</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">buf</span><span class="p">[:</span><span class="nx">required</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">t</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span><span class="w"> </span><span class="nx">required</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">hexCount</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nb">copy</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span><span class="w"> </span><span class="nx">s</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">for</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="p">&lt;</span><span class="w"> </span><span class="nb">len</span><span class="p">(</span><span class="nx">s</span><span class="p">);</span><span class="w"> </span><span class="nx">i</span><span class="o">++</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="k">if</span><span class="w"> </span><span class="nx">s</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="sc">&#39; &#39;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">				</span><span class="nx">t</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="sc">&#39;+&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="nb">string</span><span class="p">(</span><span class="nx">t</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">j</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="mi">0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">for</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="p">&lt;</span><span class="w"> </span><span class="nb">len</span><span class="p">(</span><span class="nx">s</span><span class="p">);</span><span class="w"> </span><span class="nx">i</span><span class="o">++</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">switch</span><span class="w"> </span><span class="nx">c</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">s</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">case</span><span class="w"> </span><span class="nx">c</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="sc">&#39; &#39;</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="nx">mode</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nx">encodeQueryComponent</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">t</span><span class="p">[</span><span class="nx">j</span><span class="p">]</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="sc">&#39;+&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">j</span><span class="o">++</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">case</span><span class="w"> </span><span class="nf">shouldEscape</span><span class="p">(</span><span class="nx">c</span><span class="p">,</span><span class="w"> </span><span class="nx">mode</span><span class="p">):</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">t</span><span class="p">[</span><span class="nx">j</span><span class="p">]</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="sc">&#39;%&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">t</span><span class="p">[</span><span class="nx">j</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">upperhex</span><span class="p">[</span><span class="nx">c</span><span class="o">&gt;&gt;</span><span class="mi">4</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">t</span><span class="p">[</span><span class="nx">j</span><span class="o">+</span><span class="mi">2</span><span class="p">]</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">upperhex</span><span class="p">[</span><span class="nx">c</span><span class="o">&amp;</span><span class="mi">15</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">j</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="mi">3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">default</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">t</span><span class="p">[</span><span class="nx">j</span><span class="p">]</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">s</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">j</span><span class="o">++</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="nb">string</span><span class="p">(</span><span class="nx">t</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// Return true if the specified character should be escaped when</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// appearing in a URL string, according to RFC 3986.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">//</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// Please be informed that for now shouldEscape does not check all</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// reserved characters correctly. See golang.org/issue/5684.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">shouldEscape</span><span class="p">(</span><span class="nx">c</span><span class="w"> </span><span class="kt">byte</span><span class="p">,</span><span class="w"> </span><span class="nx">mode</span><span class="w"> </span><span class="nx">encoding</span><span class="p">)</span><span class="w"> </span><span class="kt">bool</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="c1">// §2.3 Unreserved characters (alphanum)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="w"> </span><span class="o">&lt;=</span><span class="w"> </span><span class="nx">c</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="nx">c</span><span class="w"> </span><span class="o">&lt;=</span><span class="w"> </span><span class="sc">&#39;z&#39;</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="sc">&#39;A&#39;</span><span class="w"> </span><span class="o">&lt;=</span><span class="w"> </span><span class="nx">c</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="nx">c</span><span class="w"> </span><span class="o">&lt;=</span><span class="w"> </span><span class="sc">&#39;Z&#39;</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="sc">&#39;0&#39;</span><span class="w"> </span><span class="o">&lt;=</span><span class="w"> </span><span class="nx">c</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="nx">c</span><span class="w"> </span><span class="o">&lt;=</span><span class="w"> </span><span class="sc">&#39;9&#39;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">mode</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nx">encodeHost</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="nx">mode</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nx">encodeZone</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="c1">// mode 不符合，与这段代码无关....</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">switch</span><span class="w"> </span><span class="nx">c</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">case</span><span class="w"> </span><span class="sc">&#39;-&#39;</span><span class="p">,</span><span class="w"> </span><span class="sc">&#39;_&#39;</span><span class="p">,</span><span class="w"> </span><span class="sc">&#39;.&#39;</span><span class="p">,</span><span class="w"> </span><span class="sc">&#39;~&#39;</span><span class="p">:</span><span class="w"> </span><span class="c1">// §2.3 Unreserved characters (mark)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">case</span><span class="w"> </span><span class="sc">&#39;$&#39;</span><span class="p">,</span><span class="w"> </span><span class="sc">&#39;&amp;&#39;</span><span class="p">,</span><span class="w"> </span><span class="sc">&#39;+&#39;</span><span class="p">,</span><span class="w"> </span><span class="sc">&#39;,&#39;</span><span class="p">,</span><span class="w"> </span><span class="sc">&#39;/&#39;</span><span class="p">,</span><span class="w"> </span><span class="sc">&#39;:&#39;</span><span class="p">,</span><span class="w"> </span><span class="sc">&#39;;&#39;</span><span class="p">,</span><span class="w"> </span><span class="sc">&#39;=&#39;</span><span class="p">,</span><span class="w"> </span><span class="sc">&#39;?&#39;</span><span class="p">,</span><span class="w"> </span><span class="sc">&#39;@&#39;</span><span class="p">:</span><span class="w"> </span><span class="c1">// §2.2 Reserved characters (reserved)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="c1">// Different sections of the URL allow a few of</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="c1">// the reserved characters to appear unescaped.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">switch</span><span class="w"> </span><span class="nx">mode</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">case</span><span class="w"> </span><span class="nx">encodePath</span><span class="p">:</span><span class="w"> </span><span class="c1">// §3.3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="c1">// The RFC allows : @ &amp; = + $ but saves / ; , for assigning</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="c1">// meaning to individual path segments. This package</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="c1">// only manipulates the path as a whole, so we allow those</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="c1">// last three as well. That leaves only ? to escape.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="sc">&#39;?&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">case</span><span class="w"> </span><span class="nx">encodePathSegment</span><span class="p">:</span><span class="w"> </span><span class="c1">// §3.3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="c1">// The RFC allows : @ &amp; = + $ but saves / ; , for assigning</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="c1">// meaning to individual path segments.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="sc">&#39;/&#39;</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="nx">c</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="sc">&#39;;&#39;</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="nx">c</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="sc">&#39;,&#39;</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="nx">c</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="sc">&#39;?&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">case</span><span class="w"> </span><span class="nx">encodeUserPassword</span><span class="p">:</span><span class="w"> </span><span class="c1">// §3.2.1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="c1">// The RFC allows &#39;;&#39;, &#39;:&#39;, &#39;&amp;&#39;, &#39;=&#39;, &#39;+&#39;, &#39;$&#39;, and &#39;,&#39; in</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="c1">// userinfo, so we must escape only &#39;@&#39;, &#39;/&#39;, and &#39;?&#39;.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="c1">// The parsing of userinfo treats &#39;:&#39; as special so we must escape</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="c1">// that too.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="sc">&#39;@&#39;</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="nx">c</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="sc">&#39;/&#39;</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="nx">c</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="sc">&#39;?&#39;</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="nx">c</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="sc">&#39;:&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">case</span><span class="w"> </span><span class="nx">encodeQueryComponent</span><span class="p">:</span><span class="w"> </span><span class="c1">// §3.4</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="c1">// The RFC reserves (so we must escape) everything.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="k">return</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">case</span><span class="w"> </span><span class="nx">encodeFragment</span><span class="p">:</span><span class="w"> </span><span class="c1">// §4.1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="c1">// The RFC text is silent but the grammar allows</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="c1">// everything, so escape nothing.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="k">return</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">mode</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nx">encodeFragment</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="c1">// mode 不符合，与这段代码无关....</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="c1">// Everything else must be escaped.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>这个函数判断了<strong>哪些字符需要转义，并对这些字符进行处理</strong>。这里就举例说明一下 <code>http://shiluo.design:3000/search?keywords=海阔天空 beyond&amp;limit=1</code> 这个 keywords 参数的转换过程，因为其包含了中文字符和空格。</p>
<p>首先通过包裹了 <code>escape</code> 通过参数 mode 将过滤类型设置为 <strong>URL 编码类型</strong> 。<code>shouldEscape(c,mode)</code> 判定哪些字符需要转义，里面根据 RFC 规范进行了各种判断就是为了找出必须要转义的字符。</p>
<p>首先是大小写字母和一些规范内的正常字符是不需要转义的，其余的比如我们参数中的中文和空格都不在这些字符内，所以最终不满足任何一种条件，返回 true，也就是需要转义。所以遍历过“海阔天空 beyond”这个 value 后，统计出来的 hex 值是 4,space 值是 1。</p>
<p>因为一个汉字例如“海”转义后是“%E6”，由原来的一个字节变为了三个字节，所以要根据上面统计出来的 hex 值扩大容量。如果 buf 能够容纳就直接获取，否则使用 make 重新分配内存。最终遍历“海阔天空 beyond”这个字符串，<code>j</code> 是用来移动数组下标的。</p>
<blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">const</span><span class="w"> </span><span class="nx">upperhex</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s">&#34;0123456789ABCDEF&#34;</span><span class="w">
</span></span></span></code></pre></div></blockquote>
<p><strong>过程如下</strong>：“海”是需要转义的，所以将第一个字符设置为 %，紧接着取“海”这个字占用的字节数组 [230 181 183] 的第一位s[0] 230 右移4位是 14 对应是 ‘E’。第二位是和 15 按位与，结果是6，然后将下标指针向后移动三位，指向下一个存储位置。处理这个过程直到遇到空格，然后将空格替换为&rsquo;+&rsquo;，后面的英文正常不变，处理完成。</p>
<p><strong>最终我们可以看出，所谓转义就是取这个字符字节数组的第一位，按照一定规则将这个字符转换为十六进制的过程。所以到这里也很容易理解为什么我之前这样拼接 URL 会导致错误，之前一直没发现问题也可能是因为拼接的都是英文字符。</strong></p>
<p>最后，每次看完官方的某些代码还是不得不感叹处理逻辑的紧密，考虑之周到。还是要多多看别人源码，不断学习。</p>
]]></content:encoded></item></channel></rss>