前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础篇

    • 环境搭建
    • node部署
    • 基础应用
  • 内置模块

    • 1.0 本地路径处理 path
    • 2.0 文件系统操作 fs
    • 3.1 基础调试 console
    • 3.2 本地调试远程服务器上的Node代码
    • 4.1 网络服务 http
    • 4.2 网络服务 http res
    • 4.3 网络服务 http req
    • 4.4 网络服务 http server
    • 4.5 网络服务 http client
    • 4.6 网络服务 https
    • 4.7 网络TCP net
    • 4.8 网络UDP dgram
    • 4.9 域名解析 dns
    • 5.0 网络地址解析 url
    • 5.1 URL查询字符串 querystring
    • 6.1 流操作 stream
    • 6.2 逐行读取 readline
    • 7.1 进程相关 process
    • 7.2 子进程 child
    • 8.1 二进制数据 buffer
    • 8.2 二进制解码 string_decoder
    • 9.1 事件机制 events
    • 9.2 实用工具模块 util
    • 9.3 数据加密 crypto
    • 9.4 MD5入门介绍及crypto模块的应用
    • 9.4 资源压缩 zlib
    • 9.5 集群 cluster
    • 9.6 v8
  • 进阶篇

    • 5分钟入门非对称加密用法
    • cluster
    • cookie_parser深入
    • crypto模块之理论篇
    • express+cookie_parser签名机制深入剖析
    • express+session实现简易身份认证
    • https
    • log4js入门实例
    • node8_napi
    • 使用 async 控制并发
    • 使用 eventproxy 控制并发
    • 使用 superagent 与 cheerio 完成简单爬虫
    • 基于express+muter的文件上传
    • 将图片转成datauri嵌入到html
    • 常用中间件 body_parser 实现解析
    • 日志模块morgan
    • 服务端字符编解码&乱码处理 charset_enc_dec
    • 测试用例mocha,should,istanbul
    • 调试日志打印debug模块
  • Express

    • 1.1 Express概览篇
    • 1.2 Express
  • Koa2

    • 1.0 koa2概览篇
    • 1.1 快速开始
    • 1.2 async await使用
    • 1.3 koa2简析结构
    • 1.4 koa中间件开发与使用
    • 10.1 单元测试
    • 11.1 开发debug
    • 12.1 快速启动
    • 12.2 框架设计
    • 12.3 分层操作
    • 12.4 数据库设计
    • 12.5 路由设计
    • 12.6 webpack4环境搭建
    • 12.7 使用react
    • 12.8 登录注册功能实现
    • 12.9 session登录态判断处理
    • 13.1 import export使用
    • 2.1 原生koa2实现路由
    • 2.2 koa router中间
    • 3.1 GET请求数据获取
    • 3.2 POST请求数据获取
    • 3.3 koa bodyparser中间件
    • 4.1 原生koa2实现静态资源服务器
    • 4.2 koa static中间件
    • 5.1 koa2使用cookie
    • 5.2 koa2实现session
    • 6.1 koa2加载模板引擎
    • 6.2 ejs模板引擎
    • 7.1 busboy模块
    • 7.2 上传文件简单实现
    • 7.3 异步上传图片实现
    • 8.1 mysql模块
    • 8.2 async await封装使用mysql
    • 8.3 项目建表初始化
    • 9.1 原生koa2实现JSONP
    • 9.2 koa jsonp中间件
  • 其他

    • 操作数据库
    • Session 与 Token
    • Cookie、Session、Token、JWT
    • Socket

# 将图片转成datauri嵌入到html

30 秒速记

  • Data URI = 把图片字节转成 Base64 文本,直接写进 src:data:image/png;base64,iVBOR...
  • 好处:少一次 HTTP 请求;代价:体积大约多 33%,而且没法单独缓存,跟着 HTML / CSS 一起下载
  • 只适合几 KB 的小图标;大图内联会拖慢首包和解析
  • 构建工具自动做:Vite 默认 assetsInlineLimit 是 4096 字节,Webpack 5 的 asset 默认阈值是 8KB
  • MIME 要写对;用户上传的 SVG / HTML 别随便内联,有脚本注入风险

Data URI 就是把图片的二进制内容编码成一段文字,直接塞进 HTML 里,浏览器不用再单独请求这张图。 打个比方,原来是在页面上写一个「图片在哪」的地址,现在是把图片本身抄进页面。省了一次请求,但 Base64 编码会让体积多出三分之一,而且图片和页面绑在一起,页面一改图片也得重新下,所以只适合很小、不常变的图标。现在一般不手写,Vite 和 Webpack 都会按体积阈值自动决定要不要内联。

理解「将图片转成datauri嵌入到html」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 module 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

在 Node.js 里生成 Data URI 只要三步:按二进制读文件、转 Base64、拼前缀。

const fs = require('fs')
const path = require('path')

const MIME = { '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.gif': 'image/gif', '.webp': 'image/webp' }

function toDataUri(file) {
  const mime = MIME[path.extname(file).toLowerCase()]
  if (!mime) throw new Error(`不支持的图片类型:${file}`)
  const base64 = fs.readFileSync(file).toString('base64') // 不传编码,拿到的是 Buffer
  return `data:${mime};base64,${base64}`
}

console.log(toDataUri('logo.png').slice(0, 30)) // data:image/png;base64,iVBORw0K
@前端进阶之旅: 代码已经复制到剪贴板

什么时候值得内联,可以按这几条判断。第一看体积,几百字节到几 KB 的图标、小背景图,省下的请求开销比多出的 33% 体积划算;几十 KB 的图片就别内联了。第二看复用,一张图在很多页面出现,单独文件能被浏览器缓存一次到处用,内联则每个页面都要重复下载。第三看更新频率,内联的图会跟着 HTML 或 CSS 一起失效,图不变但页面频繁发版的话,图也被反复下载。

HTTP/2 普及之后,多路复用让「请求数多」的代价小了很多,内联的收益也跟着变小,所以构建工具的默认阈值都只有 4KB 到 8KB。另外 SVG 可以不转 Base64,直接用 encodeURIComponent 编码后写成 data:image/svg+xml,...,体积更小;但如果 SVG 来自用户上传,要先清洗掉里面的脚本和事件属性。

面试官追问

追问 1把首页所有图片都转成 Data URI,首屏会更快吗?
参考回答

大概率更慢。HTML 体积暴涨,首包变大,浏览器要等这一大坨下载完才能开始解析后面的内容,图片也失去了独立缓存和懒加载。几 KB 以内的小图标内联才划算。

追问 2为什么 Base64 后体积会变大三分之一?
参考回答

Base64 每 3 个字节编成 4 个字符,每个字符只携带 6 位信息,所以是 4/3 倍。开了 gzip 能压回来一部分,但还是比原图大。

追问 3用 Node.js 把一张图转成 Data URI,最短怎么写?
参考回答

先 fs.readFileSync('a.png').toString('base64') 拿到编码,再在前面拼上 data:image/png;base64,。读的时候别传 'utf8',图片是二进制,按文本读会把字节弄坏。

追问 4项目里谁来决定一张图内联还是单独文件?
参考回答

交给构建工具。Vite 用 build.assetsInlineLimit,Webpack 5 用 type: 'asset' 配 parser.dataUrlCondition.maxSize,小于阈值自动转 Data URI,大于就输出文件,不用手动维护。

追问 5CSP 开了之后内联图片显示不出来,为什么?
参考回答

img-src 没放行 data: 协议。加上 img-src 'self' data: 就行,但别顺手给 script-src 也加 data:,那等于开了脚本注入的口子。

# 问题:将图片转成datauri

30 秒速记

  • 「Node.js 读图转 Base64」真正想要的是:把图片嵌进网页 → 生成 Data URI
  • 光有 Base64 不够,浏览器认的是带前缀的完整格式:data:image/png;base64, + 编码
  • 读文件别指定 'utf8',图片要按二进制(Buffer)读
  • MIME 要和真实格式一致,格式多了别全写死 image/png

这个问题表面是「怎么把图片读成 Base64」,实际要解决的是「怎么把图片直接嵌进网页」,答案就是生成 Data URI。 很多人卡在只拿到了 Base64 字符串,直接塞进 src 发现是破图,因为浏览器需要知道这段文字是什么类型、用什么编码,所以前面得加上 data:image/png;base64,。Node.js 里一行就能搞定:fs.readFileSync(file).toString('base64'),注意读的时候别传 'utf8',不然二进制字节会被当成文字解码,图就坏了。

今天,在QQ群有个群友问了个问题:“nodejs读取图片,转成base64,怎么读取呢?” 想了一下,他想问的应该是 怎么样把图片嵌入到网页中去,即如何把图片转成对应的 datauri。

是个不错的问题,而且也是个很常用的功能。快速实现了个简单的demo,这里顺便记录一下。

面试官追问

追问 1拿到 Base64 字符串直接赋给 img.src,为什么是破图?
参考回答

浏览器会把它当成一个相对路径的 URL 去请求,当然请求不到。要拼成 data:image/png;base64,xxx,浏览器才知道这是内联数据。

追问 2fs.readFileSync(file, 'utf8') 再转 Base64,图片显示不出来,为什么?
参考回答

按 utf8 解码时,不合法的字节会被替换成 U+FFFD,原始字节已经丢了,再编码回去也还原不了。图片要直接拿 Buffer 转:fs.readFileSync(file).toString('base64')。

追问 3接口返回裸 Base64 还是完整 Data URI?
参考回答

只给页面用的话返回完整 Data URI,前端拿来就能用。如果还要存库、传给别的系统,返回 { mime, base64 } 两个字段更清楚,裸 Base64 不带类型,调用方只能靠猜。

追问 4允许用户传任意格式的图片,转换时要注意什么?
参考回答

MIME 别信扩展名,最好读文件头判断真实类型,并且只放行白名单里的格式。尤其是 SVG,它本质是 XML,能带脚本,用户上传的要先清洗。

# 实现思路

30 秒速记

  • 三步:读图片二进制 → 转 Base64 字符串 → 拼成 Data URI
  • 格式:data:[<mediatype>][;base64],<data>,中间不能有空格
  • PNG 示例:data:image/png;base64,iVBORw0KGgo...,PNG 编码后总是以 iVBOR 开头
  • Base64 只是编码,Data URI 是一种 URL 协议,前者是后者的一部分

实现就三步:读出图片的二进制,转成 Base64 字符串,再拼上 data:image/png;base64, 前缀。 Data URI 的格式是 data:[类型][;base64],数据,冒号、分号、逗号都得在,而且整串里不能夹空格,写文档时为了好看加的空格照抄进代码,浏览器就不认了。PNG 的文件头编码后总是以 iVBOR 开头,调试时看一眼这几个字符就能知道读得对不对。

const buf = fs.readFileSync('logo.png')
const uri = `data:image/png;base64,${buf.toString('base64')}`
console.log(uri.slice(0, 27)) // data:image/png;base64,iVBOR
@前端进阶之旅: 代码已经复制到剪贴板

思路很直观:1、读取图片二进制数据 -> 2、转成base64字符串 -> 3、转成datauri。

关于base64的介绍,可以参考阮一峰老师的 文章

基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础篇

    • 环境搭建
    • node部署
    • 基础应用
  • 内置模块

    • 1.0 本地路径处理 path
    • 2.0 文件系统操作 fs
    • 3.1 基础调试 console
    • 3.2 本地调试远程服务器上的Node代码
    • 4.1 网络服务 http
    • 4.2 网络服务 http res
    • 4.3 网络服务 http req
    • 4.4 网络服务 http server
    • 4.5 网络服务 http client
    • 4.6 网络服务 https
    • 4.7 网络TCP net
    • 4.8 网络UDP dgram
    • 4.9 域名解析 dns
    • 5.0 网络地址解析 url
    • 5.1 URL查询字符串 querystring
    • 6.1 流操作 stream
    • 6.2 逐行读取 readline
    • 7.1 进程相关 process
    • 7.2 子进程 child
    • 8.1 二进制数据 buffer
    • 8.2 二进制解码 string_decoder
    • 9.1 事件机制 events
    • 9.2 实用工具模块 util
    • 9.3 数据加密 crypto
    • 9.4 MD5入门介绍及crypto模块的应用
    • 9.4 资源压缩 zlib
    • 9.5 集群 cluster
    • 9.6 v8
  • 进阶篇

    • 5分钟入门非对称加密用法
    • cluster
    • cookie_parser深入
    • crypto模块之理论篇
    • express+cookie_parser签名机制深入剖析
    • express+session实现简易身份认证
    • https
    • log4js入门实例
    • node8_napi
    • 使用 async 控制并发
    • 使用 eventproxy 控制并发
    • 使用 superagent 与 cheerio 完成简单爬虫
    • 基于express+muter的文件上传
    • 将图片转成datauri嵌入到html
      • 问题:将图片转成datauri
      • 实现思路
      • 具体实现
      • 相关链接
    • 常用中间件 body_parser 实现解析
    • 日志模块morgan
    • 服务端字符编解码&乱码处理 charset_enc_dec
    • 测试用例mocha,should,istanbul
    • 调试日志打印debug模块
  • Express

    • 1.1 Express概览篇
    • 1.2 Express
  • Koa2

    • 1.0 koa2概览篇
    • 1.1 快速开始
    • 1.2 async await使用
    • 1.3 koa2简析结构
    • 1.4 koa中间件开发与使用
    • 10.1 单元测试
    • 11.1 开发debug
    • 12.1 快速启动
    • 12.2 框架设计
    • 12.3 分层操作
    • 12.4 数据库设计
    • 12.5 路由设计
    • 12.6 webpack4环境搭建
    • 12.7 使用react
    • 12.8 登录注册功能实现
    • 12.9 session登录态判断处理
    • 13.1 import export使用
    • 2.1 原生koa2实现路由
    • 2.2 koa router中间
    • 3.1 GET请求数据获取
    • 3.2 POST请求数据获取
    • 3.3 koa bodyparser中间件
    • 4.1 原生koa2实现静态资源服务器
    • 4.2 koa static中间件
    • 5.1 koa2使用cookie
    • 5.2 koa2实现session
    • 6.1 koa2加载模板引擎
    • 6.2 ejs模板引擎
    • 7.1 busboy模块
    • 7.2 上传文件简单实现
    • 7.3 异步上传图片实现
    • 8.1 mysql模块
    • 8.2 async await封装使用mysql
    • 8.3 项目建表初始化
    • 9.1 原生koa2实现JSONP
    • 9.2 koa jsonp中间件
  • 其他

    • 操作数据库
    • Session 与 Token
    • Cookie、Session、Token、JWT
    • Socket
完整面试题地址:
作者:程序员poetry
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

将图片转成datauri嵌入到html|Nodejs专题系列

# 将图片转成datauri嵌入到html

30 秒速记

  • Data URI = 把图片字节转成 Base64 文本,直接写进 src:data:image/png;base64,iVBOR...
  • 好处:少一次 HTTP 请求;代价:体积大约多 33%,而且没法单独缓存,跟着 HTML / CSS 一起下载
  • 只适合几 KB 的小图标;大图内联会拖慢首包和解析
  • 构建工具自动做:Vite 默认 assetsInlineLimit 是 4096 字节,Webpack 5 的 asset 默认阈值是 8KB
  • MIME 要写对;用户上传的 SVG / HTML 别随便内联,有脚本注入风险

Data URI 就是把图片的二进制内容编码成一段文字,直接塞进 HTML 里,浏览器不用再单独请求这张图。 打个比方,原来是在页面上写一个「图片在哪」的地址,现在是把图片本身抄进页面。省了一次请求,但 Base64 编码会让体积多出三分之一,而且图片和页面绑在一起,页面一改图片也得重新下,所以只适合很小、不常变的图标。现在一般不手写,Vite 和 Webpack 都会按体积阈值自动决定要不要内联。

理解「将图片转成datauri嵌入到html」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 module 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

在 Node.js 里生成 Data URI 只要三步:按二进制读文件、转 Base64、拼前缀。

const fs = require('fs')
const path = require('path')

const MIME = { '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.gif': 'image/gif', '.webp': 'image/webp' }

function toDataUri(file) {
  const mime = MIME[path.extname(file).toLowerCase()]
  if (!mime) throw new Error(`不支持的图片类型:${file}`)
  const base64 = fs.readFileSync(file).toString('base64') // 不传编码,拿到的是 Buffer
  return `data:${mime};base64,${base64}`
}

console.log(toDataUri('logo.png').slice(0, 30)) // data:image/png;base64,iVBORw0K
@前端进阶之旅: 代码已经复制到剪贴板

什么时候值得内联,可以按这几条判断。第一看体积,几百字节到几 KB 的图标、小背景图,省下的请求开销比多出的 33% 体积划算;几十 KB 的图片就别内联了。第二看复用,一张图在很多页面出现,单独文件能被浏览器缓存一次到处用,内联则每个页面都要重复下载。第三看更新频率,内联的图会跟着 HTML 或 CSS 一起失效,图不变但页面频繁发版的话,图也被反复下载。

HTTP/2 普及之后,多路复用让「请求数多」的代价小了很多,内联的收益也跟着变小,所以构建工具的默认阈值都只有 4KB 到 8KB。另外 SVG 可以不转 Base64,直接用 encodeURIComponent 编码后写成 data:image/svg+xml,...,体积更小;但如果 SVG 来自用户上传,要先清洗掉里面的脚本和事件属性。

面试官追问

追问 1把首页所有图片都转成 Data URI,首屏会更快吗?
参考回答

大概率更慢。HTML 体积暴涨,首包变大,浏览器要等这一大坨下载完才能开始解析后面的内容,图片也失去了独立缓存和懒加载。几 KB 以内的小图标内联才划算。

追问 2为什么 Base64 后体积会变大三分之一?
参考回答

Base64 每 3 个字节编成 4 个字符,每个字符只携带 6 位信息,所以是 4/3 倍。开了 gzip 能压回来一部分,但还是比原图大。

追问 3用 Node.js 把一张图转成 Data URI,最短怎么写?
参考回答

先 fs.readFileSync('a.png').toString('base64') 拿到编码,再在前面拼上 data:image/png;base64,。读的时候别传 'utf8',图片是二进制,按文本读会把字节弄坏。

追问 4项目里谁来决定一张图内联还是单独文件?
参考回答

交给构建工具。Vite 用 build.assetsInlineLimit,Webpack 5 用 type: 'asset' 配 parser.dataUrlCondition.maxSize,小于阈值自动转 Data URI,大于就输出文件,不用手动维护。

追问 5CSP 开了之后内联图片显示不出来,为什么?
参考回答

img-src 没放行 data: 协议。加上 img-src 'self' data: 就行,但别顺手给 script-src 也加 data:,那等于开了脚本注入的口子。

# 问题:将图片转成datauri

30 秒速记

  • 「Node.js 读图转 Base64」真正想要的是:把图片嵌进网页 → 生成 Data URI
  • 光有 Base64 不够,浏览器认的是带前缀的完整格式:data:image/png;base64, + 编码
  • 读文件别指定 'utf8',图片要按二进制(Buffer)读
  • MIME 要和真实格式一致,格式多了别全写死 image/png

这个问题表面是「怎么把图片读成 Base64」,实际要解决的是「怎么把图片直接嵌进网页」,答案就是生成 Data URI。 很多人卡在只拿到了 Base64 字符串,直接塞进 src 发现是破图,因为浏览器需要知道这段文字是什么类型、用什么编码,所以前面得加上 data:image/png;base64,。Node.js 里一行就能搞定:fs.readFileSync(file).toString('base64'),注意读的时候别传 'utf8',不然二进制字节会被当成文字解码,图就坏了。

今天,在QQ群有个群友问了个问题:“nodejs读取图片,转成base64,怎么读取呢?” 想了一下,他想问的应该是 怎么样把图片嵌入到网页中去,即如何把图片转成对应的 datauri。

是个不错的问题,而且也是个很常用的功能。快速实现了个简单的demo,这里顺便记录一下。

面试官追问

追问 1拿到 Base64 字符串直接赋给 img.src,为什么是破图?
参考回答

浏览器会把它当成一个相对路径的 URL 去请求,当然请求不到。要拼成 data:image/png;base64,xxx,浏览器才知道这是内联数据。

追问 2fs.readFileSync(file, 'utf8') 再转 Base64,图片显示不出来,为什么?
参考回答

按 utf8 解码时,不合法的字节会被替换成 U+FFFD,原始字节已经丢了,再编码回去也还原不了。图片要直接拿 Buffer 转:fs.readFileSync(file).toString('base64')。

追问 3接口返回裸 Base64 还是完整 Data URI?
参考回答

只给页面用的话返回完整 Data URI,前端拿来就能用。如果还要存库、传给别的系统,返回 { mime, base64 } 两个字段更清楚,裸 Base64 不带类型,调用方只能靠猜。

追问 4允许用户传任意格式的图片,转换时要注意什么?
参考回答

MIME 别信扩展名,最好读文件头判断真实类型,并且只放行白名单里的格式。尤其是 SVG,它本质是 XML,能带脚本,用户上传的要先清洗。

# 实现思路

30 秒速记

  • 三步:读图片二进制 → 转 Base64 字符串 → 拼成 Data URI
  • 格式:data:[<mediatype>][;base64],<data>,中间不能有空格
  • PNG 示例:data:image/png;base64,iVBORw0KGgo...,PNG 编码后总是以 iVBOR 开头
  • Base64 只是编码,Data URI 是一种 URL 协议,前者是后者的一部分

实现就三步:读出图片的二进制,转成 Base64 字符串,再拼上 data:image/png;base64, 前缀。 Data URI 的格式是 data:[类型][;base64],数据,冒号、分号、逗号都得在,而且整串里不能夹空格,写文档时为了好看加的空格照抄进代码,浏览器就不认了。PNG 的文件头编码后总是以 iVBOR 开头,调试时看一眼这几个字符就能知道读得对不对。

const buf = fs.readFileSync('logo.png')
const uri = `data:image/png;base64,${buf.toString('base64')}`
console.log(uri.slice(0, 27)) // data:image/png;base64,iVBOR
@前端进阶之旅: 代码已经复制到剪贴板

思路很直观:1、读取图片二进制数据 -> 2、转成base64字符串 -> 3、转成datauri。

关于base64的介绍,可以参考阮一峰老师的 文章。而 datauri 的格式如下

data:[<mediatype>][;base64],<data>

具体到png图片,大概如下,其中 “xxx” 就是前面的base64字符串了。接下来,我们看下在nodejs里该如何实现

data: image/png;base64, xxx

面试官追问

追问 1生成的值是 data: image/png;base64, xxx,浏览器有时不认,问题在哪?
参考回答

多了空格。冒号后面和逗号后面都不能有空格,必须是连续的 data:image/png;base64,xxx。

追问 2混合 PNG 和 JPEG 输入,前缀统一写 image/png 会怎样?
参考回答

多数浏览器会嗅探内容,JPEG 写成 image/png 往往也能显示,但这是浏览器在兜底,不是规范行为,换个环境或者用在 CSS、邮件里就可能失效。按扩展名或文件头给出正确的 MIME,JPEG 写 image/jpeg。

追问 3怎么快速判断生成的 Data URI 是不是好的?
参考回答

直接把它粘到浏览器地址栏打开,能看到图就没问题。或者看开头,PNG 是 iVBOR、JPEG 是 /9j/、GIF 是 R0lGOD,开头不对基本是读文件那步就错了。

追问 4在浏览器端怎么把用户选的图片转成 Data URI?
参考回答

用 FileReader.readAsDataURL(file),在 onload 里拿 reader.result,前缀它会自动带上。只是做预览的话,URL.createObjectURL(file) 更好,不用编码、不占额外内存,用完记得 revokeObjectURL。

# 具体实现

← 基于express+muter的文件上传常用中间件 body_parser 实现解析 →

fe