图片转 Base64
给出 data: URL 与 CSS 片段,标明膨胀比例与内联阈值。
这一页把一张图片整串摊成 data URL,再给你 CSS 与 HTML 两种现成写法,同时把「内联到底划不划算」用实测字节说清楚。字节只从文件选择、拖放或粘贴进来,用 FileReader 读进内存做编码,不上传、不发任何外链请求;粘贴进来的一段以 data: 开头的文本会被灌进反解框反着解包。类型判定不认扩展名也不信 File.type:一张签名表按顺序过前若干字节,第一个命中的算,截断的签名一律不误判,空文件与不认识的头部返回 null 而不抛异常;输出 MIME 取嗅探结果,其次才用声明,都没有时落 application/octet-stream,声明与文件头不一致时提示条点名并说明这一页按文件头给。Base64 编码器是本页自写的,不用 btoa 也不用 Buffer,输出长度严格等于 ceil(n/3)×4。
这一页不接收图片网址。转 data URL 这一步不需要解码,所以 BMP、TIFF、ICO、HEIC 甚至 MP4 这种浏览器画不出来的文件照样能整串转出,但卡片左上那块 120×120 缩略图是另一回事——整串照样给,本页不替你保证它真能显示。自动护栏是 4 MiB:超限的文件被跳过并预告展开后的字符数,而不是截断一半。内联判读是三档经验值而不是浏览器硬限制。SVG 的百分号编码变体只在输入被嗅探为 SVG 时出现,这一页也不背书「直写一定更小」,体检报告那行按实测字节告诉你这次是哪一个小。产物格子是 user-select:all,点一下就整段选中——没有复制按钮,也不做收藏;下载解码结果的按钮只在 MIME 以 image/ 开头时才出现。
图片转 Base64怎么用
- 拖入并读体检报告:一次可拖多张,每张一颗小标签,带 28×28 缩略图、文件名与「原始字节 → data URI 字节 +膨胀%」,点一下切 active 那张;标签下面有「清空已选文件」。选中的那张在卡片头部给 120×120 缩略图与膨胀读数,再往下是「体检报告与内联阈值」折叠块,逐行给原始字节、base64 字符数、实测与理论膨胀比、声明 MIME、魔数嗅探结果(与声明不一致时加一行按文件头为准的提示),动图会写明 data URL 含全部帧、体积按整档算——折叠只影响看不看得到,收起时那些数字照样在算。文件读取与编码都在本机内存里,没有上传环节。0 字节的文件报「可能是空文件或读取被中断」;刚好压过 4 MiB 这条线的输入会摊成 5592408 个字符,提示条把这个预测值套上同一条字节格式化函数写成「约 5.33 MB 个字符」,理由是那种长度会在选中和排版时把标签页拖住。
- 需要 SVG 时挑编码方式:当前这张被嗅探为 SVG 时「SVG 百分号编码变体」那一档才放开,否则整档禁用并写明「输入不是 SVG:百分号编码变体只适用于文本类图像」;放开时下方同时出现三个开关,分别管「属性双引号换单引号」「空格编成 %20」「压掉标签间换行」,每档的代价写在开关旁,换成非 SVG 的文件,这一档和三个开关一起收回去,若正停在这一档会自动跳回「data URI 整串」。体检报告里同时给出两版字节数与这次谁更小。转义集是 14 个字符 % # < > " { } & \ ^ ` | [ ],其余 ASCII 原样留着,空格默认也留着(可切 %20,给不带引号的 CSS url() 用),非 ASCII 与控制字符交给 encodeURIComponent 按 UTF-8 展开,一个汉字变成三个百分号序列共 9 个字符,一个平面外 emoji 也是 12 个;转义先处理 % 本身,所以已经编码过的内容不会被二次吃掉(%23 变成 %2523)。另外两档开关都有代价:属性双引号换单引号确实更短,但它改的是 SVG 内容本身,原文里本来就有单引号时不会再补一层转义,产出的是属性引号不配对的写法;压掉标签间换行这一档做两件事:把「> 空白 <」收成「><」,再剥掉每行行首的空白——所以它不只动标签之间,文本节点里的换行缩进同样会被吃掉(<text> 后跟两个空格再闭合会变成空元素)。
- 反解与取值:点「从 data URI 反解」那一档(或直接粘一段以 data: 开头的文本,会自动切过去并填好),读出状态、头部(超过 80 字符截断显示)、MIME、编码方式、整串字节、解码后字节与整串/数据比值、被忽略的空白数、补位判读和魔数结果;是图片类型时才给预览并出现下载按钮。产物一屏只给一段:正向那栏上方是一排分段按钮——data URI 整串|CSS 片段|img 标签|SVG 百分号编码变体|从 data URI 反解,点哪档下面那一格 `<pre>` 就换成哪一段,格子上方摆 120×120 缩略图与「原始 → data URI +膨胀%」,格子下面一行给这一段的字符数与 UTF-8 字节数。CSS 片段是 .box 的 background-image 写法(这一页调用时不带尺寸,所以不出 background-size 那两行),img 片段是 <img src="…" alt="文件名去掉扩展名" loading="eager">(同理不写 width/height)。格子本身是 user-select:all,点一下就整段选中,本页没有复制按钮,也不做收藏与登录。反解格只在 MIME 以 image/ 开头时画一张预览图,画不出来给一行原因;下载解码结果的按钮也只在这时才出现,文件名是 decoded-from-data-url.扩展名(扩展名取魔数结果,认不出就 .bin),走 Blob 加 objectURL 再交给 <a download>,两秒后回收;要提醒的是百分号编码那一路下载的是数据段原样文本(里面还是 %3C 这些),不是解开后的字节。
- 签名表逐条过前几个字节:PNG 要 89 50 4E 47 0D 0A 1A 0A 八位全对、JPEG 要 FF D8 FF 三字节(只有 FF D8 不算)、GIF 认 87a 与 89a 而不凭空认 88a、WebP 要 RIFF 套 WEBP(RIFF 套 AVI 不算图片)、AVIF 认 ftyp 品牌 avif/avis/avio、HEIF/HEIC 认 heic/heix/hevc/mif1/msf1/mif2 这六个并对外报 image/heic、MP4 认 isom/mp4v/mp42/iso2/M4V 并报 video/mp4、TIFF 两种字节序都认、ICO 认 00 00 01 00、BMP 只有 BM 两字节所以排在长签名之后以免抢别人的判定。SVG 走文本判定:去掉一个前置 BOM 与空白后,前 512 字节里以 <?xml、<svg 或 <!doctype svg 开头才算,窗口外的标记不认,<SVG> 这种大写标签不认(<!doctype 那一支例外,它不分大小写)。
- 编码器的出处与体积数学:Base64 用 A-Z、a-z、0-9、+、/ 加 = 补位,与 RFC 4648 §10 的 f、fo、foo、foob、fooba、foobar 六组非空向量逐字相同(表里的空串那组也照样过),PNG 那 8 字节头的产物就是大家眼熟的 iVBORw0KGgo=;编码按每 8192 字符分块拼接,30 万与 40 万长度的输入仍与独立参照实现逐字一致。体积数学给两栏:实测膨胀比与理论膨胀比,理论值就是 ceil(n/3)×4 ÷ n,也就是每 3 字节摊成 4 个 6 bit 字符,范围从 1.3333 一直到 4(1 字节的输入最亏,四个字符里两个是补位)。头部字节单独读出(data:image/png;base64, 是 22 字节),按 MIME 惯例每 76 字符折一行时还会再多出每行 2 字节的 CRLF,400 字符那一档算出来是 10 个字符。
常见问题
为什么膨胀正好是 33%?什么时候远不止这个数?
Base64 把每 3 字节重排成 4 个只携带 6 bit 的字符,所以长度恒为 ceil(n/3)×4,大文件上就是 4/3 ≈ 1.3333。文件越小越亏:1 字节要 4 个字符(4 倍)、2 字节要 4 个(2 倍),因为补位符 = 也是占字符的。再往上叠两笔:头部 data:MIME;base64, 那 20 来个字节,以及若按 MIME 惯例 76 字符折行时每个断点的 2 字节 CRLF。这也是内联的真实成本——每字符只装 6 bit 信息,且这段串跟着 CSS 重下、不能单独缓存。
我的文件明明叫 .png,为什么页面说是 GIF(或反过来)?
因为判定看的是文件头而不是名字。签名表按顺序取第一个命中的:PNG 那 8 个字节一位都不能差,JPEG 要 FF D8 FF,WebP 要 RIFF 套 WEBP,BMP 只有 BM 两字节所以被排到后面。声明那侧(File 自带的 MIME、或 data URL 头里写的 MIME)说是 image/gif 而字节是 PNG 时,嗅探照样报 image/png,页面于是按文件头给输出的 MIME 并挂一行不一致提示——编进 data URL 的本来就是这个文件的字节,头里写什么都改不了它。反过来说,一条 data URL 的声明 MIME 与内容不符时,浏览器实际解出什么由它自己的解码器决定,这一页只负责把两者都读出来给你看。被截断的头(PNG 少最后一个 0x0A、JPEG 只有 FF D8、ICO 少第 4 字节)都判不出来,返回 null 而不是猜一个。
多大的图就别内联了?会不会被浏览器直接拒掉?
这一页的经验档位是 ≤2 KB 内联、≤10 KB 看情况、再大建议外链,档位不是浏览器规则,代价那行会跟着判读一起显示(≤2048 字节判「内联划算」,≤10240 判「看情况」——这一档的措辞会把代价写全:这段串跟着 CSS 一起重下、无法单独缓存)。硬限制方面要分场景:HTTP/2 的 16384 字节默认帧上限只在 data URL 被塞进 URL 查询串或请求头时才用得上,写进 CSS 正文不受它约束;IE8 会在 32768 字节处失败;解码长度上限各浏览器差得很远(MDN 记录 Firefox 97+ 约 32 MB、Chromium 512 MB 以上、WebKit 2048 MB 以上),但规范不保证支持任何规定长度,超长串被拒是可能的。本页自己的护栏是 4 MiB 自动上限,超了直接跳过并预告展开后的字符数,不做静默截断;更大文件请走文件外链,或用压缩页把图本身变小。四行参照数字用的都是公开值,出处写在每个小标签的悬停提示里:Vite 的 build.assetsInlineLimit 默认 4096 字节(这条常被误传成 nginx 的限制,nginx 的 client_max_body_size 默认其实是 1 MiB)、HTTP/2 单帧默认上限 16384 字节(SETTINGS_MAX_FRAME_SIZE,只有把 data URL 塞进 URL 查询串或请求头时才会撞上,写进 CSS 正文不受它约束)、IE8 的 32768 字节上限(IE9 起取消,只影响还在支持 IE8 的项目)、以及 MDN 记录的解码长度上限 33554432 字节(Firefox 97+ 约 32 MB、更早接近 256 MB,Chromium 512 MB 以上,WebKit 2048 MB 以上;规范并不保证支持任何规定长度,这一栏只用来看「会不会直接被拒」)。
Base64 校验有多严?
Base64 校验是严格的:先剥 ASCII 空白并单独报剥了几个(粘贴带折行的串常有),补位数量必须与余数配对——QQ 缺补位照样解出但标「结尾缺 =:多数解码器容忍,严格校验会拒」,而 QQ= 这种半截补位直接拒,判决与浏览器 atob 的 forgiving-base64 一致;模 4 余 1、字表外字符(给出下标与原字符)、= 后面还有数据、结尾多于两个 = 各有各的话。
反解那格对 data URL 的写法认到什么程度?
反向解包那格会读:首部允许前后空白与 DATA: 大写、只有字面量 base64 这一个参数(大小写随意)才走 Base64,写成 ;base64url 不会(它按百分号编码解,长度给的是解码后文本的 UTF-8 字节数,汉字按三个字节计),MIME 省掉时按规范等同 text/plain;charset=US-ASCII,解出的字节再嗅一次魔数。
对应功能编号 #82无需安装,手机端同样可用按 Ctrl + K 换工具