<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Web on Quarix.Log</title><link>http://blog.quarix.me/categories/web/</link><description>Recent content in Web on Quarix.Log</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Fri, 27 Feb 2026 12:51:53 +0000</lastBuildDate><atom:link href="http://blog.quarix.me/categories/web/index.xml" rel="self" type="application/rss+xml"/><item><title>解决 qrz.com 上 Bio 保存不全</title><link>http://blog.quarix.me/p/qrz-bio-emoji-issue/</link><pubDate>Fri, 27 Feb 2026 12:51:53 +0000</pubDate><guid>http://blog.quarix.me/p/qrz-bio-emoji-issue/</guid><description>&lt;img src="http://blog.quarix.me/" alt="Featured image of post 解决 qrz.com 上 Bio 保存不全" /&gt;&lt;p&gt;恰好准备换卡，在 qrz.com 上整理了一下个人简介。翻了翻编辑页面，发现 Bio 支持 HTML语法，于是写了一份带样式的 Html，加了颜色、字号、间距。保存，刷新，结果页面上只剩开头一截。&lt;/p&gt;
&lt;p&gt;第一反应是触发了 qrz 的安全过滤。HTML 里有 &lt;code&gt;style&lt;/code&gt; 属性，说不定被当成 XSS 风险砍掉了。于是改成最朴素的 &lt;code&gt;&amp;lt;table&amp;gt;&amp;lt;td&amp;gt;&lt;/code&gt; 结构，内联样式都去掉，再试一次还是一样，保存后内容依然面目全非，只剩个开头残躯。div 标签被强制闭合，但是手动添加文字进去又能够正常保存。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;qrz 的 Bio 并不是直接嵌在页面 HTML 里的，用开发者工具翻了一圈，内容藏在渲染 Bio 简介的 &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; 下的 &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; 里，用 Base64 编码塞进去，关键部分长这样：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;jQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;.action-render-&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#biodata&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;PHRhYmxlIGNlbGx...MjRweDsgZm9udC13ZWlnaHQ6IGJvbGQ7IG1hcmdpbjogMCAwIDVweCAwOyI+&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;把这段 Base64 解码出来，截断的位置恰好卡在 &lt;code&gt;&amp;lt;p style=&amp;quot;color: #0050E6; font-size: 24px; font-weight: bold; margin: 0 0 5px 0;&amp;quot;&amp;gt;&lt;/code&gt; 后面原本紧跟着一个Emoji 👋 字符。Emoji 以及之后的所有内容全没了。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;这个现象指向一个很经典的历史遗留问题。MySQL 等数据库后端早年的 &lt;code&gt;utf8&lt;/code&gt; 字符集是个残缺实现，每个字符最多只存 3 个字节。这对绝大多数文字来说够用，但 Unicode 中的 Emoji 字符需要 4 个字节来编码。一旦数据库列的字符集是 &lt;code&gt;utf8&lt;/code&gt; 而非后来补全的 &lt;code&gt;utf8mb4&lt;/code&gt;，写入时碰到第一个 4 字节字符，就会直接在那里截断，后面的内容全部丢弃。&lt;/p&gt;
&lt;p&gt;qrz.com 是一个运营了很久的老站，底层用的大概率正是这套配置。&lt;/p&gt;
&lt;p&gt;把 Bio 里所有 Emoji 删掉，保存，立刻生效。&lt;/p&gt;
&lt;p&gt;如果不去翻 Base64 编码的内容，浏览器又自动把截断的标签补上了，大概会一直以为是 HTML 过滤在作怪，然后在哪些标签被允许上兜圈子兜很久。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;有时候问题的答案藏得比预期深一点，多翻一层就能看见。&lt;/p&gt;
&lt;/blockquote&gt;</description></item></channel></rss>