跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
JavaScriptNode.js大前端java

前端实时通信与 WebSocket 核心原理及实战

实时通信方案经历了从整页刷新、短轮询、长轮询到 SSE 和 WebSocket 的演进。WebSocket 基于 TCP 实现全双工持久连接,通过 HTTP Upgrade 握手建立通道。核心流程包括连接创建、协议升级、双向通信、心跳保活、异常重连及资源清理。在架构设计上,需考虑网关层连接管理、管道层路由发布订阅及存储层状态维护,确保消息不丢不重且有序。面试常考协议对比、水平扩展策略、心跳设计、代理配置、鉴权机制及性能评估。前端封装需注意状态机控制、离线缓存与可观测性。

颠三倒四发布于 2026/4/7更新于 2026/8/2343 浏览

一、技术演进:从轮询到 WebSocket

网页一旦需要'实时',麻烦就开始了:数据在不断变化,用户却只能等下一次刷新。

  • 刷新解决不了的延迟,用短轮询凑数,又被无数空请求反噬;
  • 再加长轮询,试图把'有了新数据再说'变成一种伪推送,却仍困在请求—响应的笼子里。
  • 开发者于是继续前探:让连接不再频繁重建,尝试分块直输,把事件像水一样持续送达,于是有了更顺滑的 Streaming 与标准化的 SSE。

直到某一刻,我们不再满足于'更聪明的单向',而是迈向真正的'同时说话与倾听'——WebSocket 把通信从一次次请求,变成一条持久而通透的通道。此后,HTTP/2、HTTP/3 与 QUIC 又在底层为效率和时延开了绿灯,甚至提供了可选可靠与无序传输的更多可能。

接下来,我们就沿着这条主线,层层展开:它们各自解决了什么、在哪些场景最合拍、又如何在你的系统里形成清晰的选型边界。

1. 从整页刷新到长轮询

这一块是为了解决'整页刷新导致的高延迟与带宽浪费',逐级细化与优化。

a. 早期网页:整页刷新

  • 背景与问题:每次更新都整页请求,体验割裂、带宽浪费、延迟高。
  • 直接影响:促使前端与服务端思考'只取变化'。

b. 短轮询(Short Polling)

  • 解决:改为'隔一段时间拉一次',显著减少整页重载带来的浪费。
  • 局限:高频请求带来大量空响应与服务器开销。
  • 承接改进:为减少空转,演进到长轮询;同时催生更流式的思路(Streaming/SSE)。

c. 长轮询(Comet/挂起请求)

  • 解决:请求挂起,服务器有新数据才返回,接近'伪推送',显著降低空转。
  • 局限:本质仍是请求 - 响应;连接频繁重建;难做真正双向。
  • 承接改进:
    • 单向推送更稳:SSE 标准化单向事件流。
    • 若要真双向与二进制:交给 WebSocket。

d. HTTP Streaming(分块传输/持续输出)

  • 解决:保持连接,分块持续输出,适合连续文本/事件流,重连更少、延迟更低。
  • 局限:多为单向,受代理/中间件影响,兼容性不一。
  • 承接改进:单向事件由 SSE 标准化;双向场景仍需 WebSocket。

e. SSE(Server-Sent Events)

  • 解决:以标准事件流语义提供单向推送,浏览器原生支持,资源占用低。
  • 适配范围:通知、进度、日志/监控等文本或事件流。
  • 位置关系:在'只需单向推送'的场景中,SSE 是这一链条的稳定落点,而非过渡技术。

2. 范式跃迁:WebSocket

这不是前面链条的'又一改良',而是从请求 - 响应转向全双工持久连接的范式变化。

WebSocket(全双工、持久、低开销)

  • 解决:真正的双向实时通信,降低握手与头部开销,支持文本与二进制,端到端延迟低。
  • 典型场景:聊天、协同编辑、在线游戏、行情推送、IoT。
  • 与上一链条的关系:
    • 在'需要双向实时'的主战场,实质上取代了短轮询/长轮询等过渡方案。
    • 与 SSE 并存:若只有单向通知/事件流,SSE 更简单更省资源;若需要双向或二进制,WebSocket 更合适。
  • 运维关注:连接状态管理、容量与反压、企业代理/负载均衡兼容。

3. 底座升级与新选项:HTTP/2·HTTP/3·QUIC 家族

这部分不是替代前两块,而是提供更高效的承载与更灵活的传输语义。

WS over H2/H3

  • 价值:与同域请求复用连接、更好穿透与效率、更低握手成本。
  • 作用:让 WebSocket 的部署与网络效率更优。

WebTransport(基于 QUIC)

  • 价值:可选可靠与有序/无序、更低延迟,适合实时媒体、游戏、定制协议。
  • 关系:不是取代 WebSocket/SSE 的通吃方案,而是当你需要'更细粒度的可靠性与顺序控制'时的新工具。

二、速查表

实时推送的目标是'低延迟、双向或单向地把数据从服务端送到客户端'。主流技术选型包括上述几种方案的对比权衡。

三、WebSocket 核心定义

WebSocket 是 HTML5 推出的一种全双工 (Full-Duplex)、持久化 (Persistent) 的网络通信协议,基于 TCP 协议构建,允许客户端 (浏览器) 与服务器之间建立一条长期稳定的连接通道,实现「服务器主动向客户端推送数据」和「客户端实时向服务器发送数据」的双向通信,无需频繁建立/断开连接。

其核心特点可概括为:

  • 全双工:通信双方可同时发送/接收数据 (区别于 HTTP 的「请求 - 响应」单向通信);
  • 持久连接:连接建立后长期保持,避免 HTTP 每次通信都需重新握手的开销;
  • 轻量协议:数据帧头部信息简洁 (仅 2-14 字节),传输效率远高于 HTTP;
  • 协议标识:客户端发起连接时使用 ws://(非加密) 或 wss://(加密,基于 TLS,类似 HTTPS) 作为协议前缀。

从零开始的完整流程

下面是一条你在前端真实会走的链路:创建连接 → HTTP 握手与协议切换 → 进入 WebSocket 双向通信 → 启动心跳检测 → 发现异常并重连 → 重连成功后的补偿 → 服务端跨域放行 → 正常/异常关闭。

1) 创建连接(入口)

你写下第一行代码时,要解决两件事:用对协议前缀、绑定好事件。

  • 如果网页是 HTTPS 必须用 wss://,HTTP 页面用 ws://
  • 立刻绑定 onopen/onmessage/onerror/onclose,以便后续重用。

示意代码(精简):

const ws = new WebSocket(url);
initWebSocketEvents(ws);
2) HTTP 握手与协议切换(从'请求'到'长连')

客户端 (浏览器) 创建 WebSocket 实例时,会发起一个特殊的 HTTP - GET,核心目的是「请求将协议从 HTTP 升级为 WebSocket」。服务端验证通过后返回 101,双方切换到 WebSocket 帧通信。

WebSocket 帧是双向通信中的最小传输结构,携带**'数据类型、是否为消息的最后一段、负载长度、掩码/密钥、实际数据'**。消息可以被拆成多帧连续发送,也可以一个帧就送完。

客户端发起「协议升级请求」(HTTP - GET 请求)

请求头中关键字段 (面试高频考点):

  • GET /ws-endpoint HTTP/1.1:请求方法为 GET,路径为服务器的 WebSocket 端点 (如 /ws);
  • Host: example.com:服务器域名;
  • Upgrade: websocket:核心字段,告知服务器「要升级协议为 WebSocket」;
  • Connection: Upgrade:配合 Upgrade,表示「这是一个协议升级请求」;
  • Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==:客户端生成的随机字符串 (Base64 编码,长度 16 字节),用于服务器验证 (防止恶意连接);
  • Sec-WebSocket-Version: 13:WebSocket 协议版本 (当前主流为 13,需服务器支持);
  • Sec-WebSocket-Origin: https://example.com:客户端所在域名 (用于服务器跨域验证)。

服务器响应「协议升级成功」(HTTP - 101 状态码)

服务器收到请求后,若支持 WebSocket 协议且验证通过 (如 Sec-WebSocket-Key 验证、跨域验证),会返回 HTTP - 101 (SwitchingProtocols) 状态码,表示「同意协议升级」。

响应头中关键字段 (面试高频考点):

  • HTTP/1.1 101 Switching Protocols:101 状态码是协议切换的标志;
  • Upgrade: websocket:确认升级为 WebSocket 协议;
  • Connection: Upgrade:确认连接用于协议升级;
  • Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=:服务器对 Sec-WebSocket-Key 的处理结果 (核心验证逻辑):
    1. 服务器将客户端发送的 Sec-WebSocket-Key 与固定字符串 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 拼接;
    2. 对拼接后的字符串进行 SHA-1 哈希计算;
    3. 将哈希结果转为 Base64 编码,即为 Sec-WebSocket-Accept 的值;
    4. 客户端会验证该值是否正确,若不正确则拒绝建立连接 (防止伪造响应)。
3) 进入通信阶段(双向数据 + 基础发送)

握手通过,onopen 会触发。此时做两件事:

  • 发送初始化数据(如身份、订阅主题)
  • 启动心跳(下一步会讲)

通信注意:

  • onmessage 既可能是字符串,也可能是二进制(Blob/ArrayBuffer)
  • bufferedAmount 可用来做背压控制(积压太大时暂停继续 send)
4) 启动心跳(让连接'活着'且可感知)

持久连接会遭遇 Wi‑Fi 抖动、防火墙清理等问题。心跳=周期性发 ping,超时未收到 pong 就判死链。

推荐参数(可按业务调优):

  • HEARTBEAT_INTERVAL ≈ 30s
  • HEARTBEAT_TIMEOUT ≈ 10s

实操要点:

  • 启动前先清理旧定时器,避免重复
  • 收到 pong 立即清除超时定时器
  • onclose/onerror 必须停止心跳
5) 异常→重连(恢复连接但不过载)

一旦 onerror、onclose(code ≠ 1000) 或心跳判定超时,进入重连。

目标是:能恢复、不过载、可被用户停止。

策略三件套:

  • 指数退避:1s → 2s → 4s … 最多 30s
  • 次数上限:如 10 次(达到即停)
  • 可控停止:提供'停止重连'或页面关闭时停

补偿机制:

  • 在断开前缓存'待发送'数据(例如未发出的聊天消息)
  • 重连成功后按序补发,确保业务连续性
6) 服务器放行跨域(握手能否过关的关键)

虽然 WebSocket 原生'支持跨域',但握手是 HTTP,服务端需要对 Sec-WebSocket-Origin 做白名单校验。

否则会 403 或直接关闭。

  • Node.js(ws)
    • 读取 req.headers['sec-websocket-origin']
    • 不在 allowedOrigins 列表:关闭 1008 'Cross-origin access denied'
  • Spring Boot
    • registry.addHandler(...).setAllowedOrigins("https://a.com", "https://b.com")
    • 需要时 .withSockJS() 提供降级
7) 正常关闭与资源清理(善始善终)
  • 用户离开页面或主动退出:ws.close(1000, '用户主动退出')
  • onclose 中停止心跳与重连,清空定时器与队列,避免泄漏
  • 记录关闭原因码:1000 正常、1006 常见于异常/心跳超时

四、面试题

面试关注点通常围绕'协议对比、连接管理、消息语义、可靠性与扩展性、安全与运维成本'。

1. WebSocket 与 SSE 的差异与使用场景,HTTP 轮询呢?

WebSocket(全双工,二进制/文本)

  • 适用:即时聊天、协作编辑、游戏状态同步、行情推送、需要客户端→服务端主动上行的实时交互。
  • 优点:低延迟、头开销小、全双工、支持二进制、可自定义子协议。
  • 注意:需处理心跳、重连、背压、鉴权与扇出扩展;代理/LB 要正确透传 Upgrade 和超时设置。

Server-Sent Events(SSE,单向 server→client)

  • 适用:通知流、日志流、监控事件、流式生成文本(如增量输出)、只需服务端下行的实时更新。
  • 优点:浏览器原生 EventSource、文本流、自动重连、支持 Last-Event-ID 断点续传;实现简单。
  • 注意:单向、仅文本(可 base64 二进制)、连接数限制与代理超时需要关注;移动端网络切换要做容错。

HTTP 轮询/长轮询(兼容兜底)

  • 适用:对实时性要求不高的小流量场景、受网络环境或企业防火墙限制无法使用 WS/SSE 时的兜底。
  • 优点:最易落地、与缓存/鉴权/监控体系天然兼容;对中间设备最友好。
  • 注意:延迟更高、资源利用低;高频轮询会带来成本与限流压力。

重点

  • WebSocket 通过 HTTP/1.1 Upgrade → 101 完成切换,此后是帧协议的全双工通道;Keep-Alive 仅是复用 TCP,不改变 HTTP 的请求 - 响应语义。
  • 选型规则:需要双向实时交互选 WebSocket;单向事件流选 SSE;受限或低实时性场景用轮询作兜底。

2. 如何设计一个可水平扩展的实时推送系统?

在可水平扩展的实时推送系统中,WebSocket 连接会分布在多台网关节点上。

核心挑战是如何在连接与消息不在同一台机器时,仍能把消息快速路由到正确的连接。可行的范式是

  • 网关层负责连接
  • 管道层负责路由与发布订阅
  • 存储层负责状态与回放

网关层(负责连接)

  • 终止 TLS/WS,维持心跳与速率限制,保持无状态实例。
  • 建立本地索引:connectionId → 订阅集合,userId → connectionIds。
  • 将连接元数据上报共享存储:connectionId、userId、nodeId、订阅、最近心跳。
  • 仅订阅'与自己有关的分片':按 userId/topic 的哈希分片从管道层拉取,减少无关扇出。
  • 写通道背压与优先级:控制帧/关键消息优先,低优先级可丢尾或抽样。

管道层(负责路由与发布订阅)

  • 选型具备分片与回放能力的总线(Kafka/Pulsar/NATS/Redis Streams)。
  • 分片策略:
    • 点推:按 userId/connectionId 一致性哈希到分区,保证单用户局部有序。
    • 主题推送:按 topic 分区,网关本地再做订阅过滤与扇出。
  • 路由方式:
    • 生产者只需写对的分片;总线按分区把消息送到订阅该分片的网关。
    • 广播/超大房间采用'分层扇出':先到分片,再由各网关本地扇出,必要时加中间扇出代理。
  • 去重与幂等:messageId 或 (topic, partition, offset) 作为幂等键,网关/客户端各自维护短期去重集合。

存储层(负责状态与回放)

  • 会话与订阅状态:使用 Redis Cluster 或 KV 服务存 userId→connectionIds、connectionId→nodeId、订阅清单、心跳时间。
  • 游标与回放:在总线层保留 offset;客户端重连携带 resumeToken,网关据此恢复订阅并按 offset 增量补发。
  • 一致性与更新:订阅变更写事件流,相关网关消费后刷新本地索引;用版本号/逻辑时钟避免乱序覆盖。

3. 如何保证消息不丢、不重、按序?

  1. 不丢: 消息先落到能持久化、带副本确认的总线里(像'写盘且多副本到位才算成功'),写失败就退避重试;消费侧是'先送到用户手里或进可靠下行队列,再更新位点',断线后拿着 resumeToken+offset 从保留的历史里把漏掉的补回来。
  2. 不重: 每条消息都有一个不会撞车的'指纹'(messageId 或 topic-partition-offset);网关用一小块内存做近端去重,只有第一次真正写入才前进位点,重复的一概忽略;客户端也按同一指纹做幂等处理,避免业务状态被二次改动。
  3. 按序: 把需要有序的对象(userId/roomId)哈希到同一分区,借用分区内天然顺序;同一个键在网关里串行发送、同队列内重试,不跨分区不并行穿插,这样即使重试和补发也不会把顺序打乱。

4. 心跳如何设计?超时如何判定?

这里的心跳,目标是 '保活、探测、可平滑重连'。

  1. 不失联: 用应用层 ping/pong,客户端主发、服务端回;
    1. 间隔 20–60s,加±10% 抖动
    2. 未知网络时取 20–30s,确保小于最短 NAT 空闲回收。
  2. 怎么判死: 别一跳不回就拍板。记录 lastSeen,允许 2–3 次心跳未达或 now-lastSeen 超过 2–3 个周期再判断;进入'Suspect'时降级写入,仍有业务流量即立刻恢复。
  3. 断了咋办: 重连走指数退避并带抖动,携带 resumeToken/offset 补发;移动端切网优先复用会话,失败再重建。监控 RTT/丢包与 Suspect 比例,自动调心跳与阈值。

5. 如何在 Nginx / Envoy 反向代理后稳定运行 WebSocket?

核心思路:让代理'不瞎操心'、连接'常被看见'、后端'可续上'。

  • 代理设置: 开启 WebSocket 升级;调大超时,禁用缓冲与压缩;保持 TCP keepalive,HTTP/2 用 CONNECT(H2/WebSocket)。
  • 心跳与保活: 应用层 ping/pong 20–30s(±10% 抖动),保证小于代理/NAT 空闲回收;大连接数用轻量负载均衡(hash by userId/roomId)避免跨节点迁移。
  • 断线与重连: 客户端指数退避 + 抖动,带会话 token/offset 续传;后端幂等去重,重放不重不丢。
  • 运维与观测: 开代理层指标(升级成功率、idle 关闭数、5xx)、RTT/丢包与重连率,异常时自适应缩短心跳或放宽超时。

6. 如何做鉴权与权限隔离?

握手前校验 JWT;Subprotocol 指定租户/版本;频道级 ACL;避免敏感数据从客户端请求非授权频道。

  • 握手前校验 JWT: 在 HTTP Upgrade 前验证 iss/aud/exp/签名并解析 tenant_id/user_id/scopes,避免建立长连后再踢。
  • Subprotocol 指定租户/版本: 用 Sec-WebSocket-Protocol 携带 tenant 和策略版本做白名单匹配,确保连接上下文一致。
  • 频道级 ACL: 频道强制以租户前缀命名,每次 subscribe/publish 依据 RBAC+scope 前缀(到资源或前缀)做服务端授权。
  • 避免敏感数据越权: 仅按服务器维护的'已授权订阅集合'下发数据,忽略客户端自报筛选请求并拒绝未授权频道。

7. 如何评估性能与成本?

  • 每连接内存占用: 用基线压测量出 MB/1k 连接的实际占用,结合目标并发外推单机上限并监控 GC/碎片。
  • 每秒消息数(fanout×频率): 用发布频率×平均扇出得到总吞吐,核算带宽与发送队列容量,识别热点频道放大效应。
  • 尾延迟 P95/P99: 持续跟踪端到端延迟长尾并关联队列深度与 CPU/GC 事件,确保在 SLA 红线下仍稳定。
  • 压测考虑广播峰值与重连风暴: 分别模拟大扇出瞬时广播与大量短时间内握手重连,验证背压、限速和握手路径的韧性。

8. 遇到'重连风暴'怎么处理?

  • 抖动退避(指数退避 + 随机抖动): 客户端按指数退避间隔重试并加入随机抖动,避免同相位同时重连造成尖峰。
  • 分批恢复: 将连接恢复按固定批次/时间片发放(如每 100ms 开放 N 个),把尖峰摊平到更长窗口。
  • 服务端限流与排队: 在握手与认证路径设置并发/速率上限与队列,超限直接返回可重试错误或延迟令牌。
  • 灰度放量: 按租户/区域/版本逐步提升允许重连比例,结合健康度与错误率自动调节放量速度。

9. 前端如何封装一个健壮的 WebSocket 客户端?

  • 状态机(CONNECTING/OPEN/CLOSING/CLOSED): 用有限状态机驱动所有事件与迁移,单航道控制避免并发重连与回调竞态。
  • 心跳/重连策略: 按固定心跳探活与半开检测,重连采用指数退避叠加随机抖动并设上限与冷却期。
  • 消息序列化: 统一 envelope(type/id/ts/payload),默认 JSON,性能敏感时切 Protobuf/MessagePack 并保持向后兼容。
  • 离线缓存与去重: 未连通时将待发入队、跨刷新用 IndexedDB,按 seq/uuid 去重并用 last-seq 做断点续传。
  • 可观测日志: 记录连接尝试/关闭码/重连次数/心跳 RTT/队列长度等指标与事件,便于快速定位长尾与故障。

目录

  1. 一、技术演进:从轮询到 WebSocket
  2. 1. 从整页刷新到长轮询
  3. 2. 范式跃迁:WebSocket
  4. 3. 底座升级与新选项:HTTP/2·HTTP/3·QUIC 家族
  5. 二、速查表
  6. 三、WebSocket 核心定义
  7. 从零开始的完整流程
  8. 1) 创建连接(入口)
  9. 2) HTTP 握手与协议切换(从“请求”到“长连”)
  10. 3) 进入通信阶段(双向数据 + 基础发送)
  11. 4) 启动心跳(让连接“活着”且可感知)
  12. 5) 异常→重连(恢复连接但不过载)
  13. 6) 服务器放行跨域(握手能否过关的关键)
  14. 7) 正常关闭与资源清理(善始善终)
  15. 四、面试题
  16. 1. WebSocket 与 SSE 的差异与使用场景,HTTP 轮询呢?
  17. 2. 如何设计一个可水平扩展的实时推送系统?
  18. 3. 如何保证消息不丢、不重、按序?
  19. 4. 心跳如何设计?超时如何判定?
  20. 5. 如何在 Nginx / Envoy 反向代理后稳定运行 WebSocket?
  21. 6. 如何做鉴权与权限隔离?
  22. 7. 如何评估性能与成本?
  23. 8. 遇到“重连风暴”怎么处理?
  24. 9. 前端如何封装一个健壮的 WebSocket 客户端?
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Apache SeaTunnel Web 可视化数据集成平台搭建指南
  • DIY 无人机升压降压电路设计
  • TRAE 中国版 SOLO 模式全量免费开放,重塑 AI 编程流程
  • AI 大模型入门教程:从零基础到精通
  • 大模型应用元年,有哪些场景可以实际落地?
  • BFS 解决多源最短路问题
  • 大模型与小模型的关系:实验、蒸馏与部署
  • FastAPI 打造基于 LLM 的 Web 接口实战教程
  • Swin2SR 在 AI 绘图后期的应用:Midjourney 草图 4K 放大实操
  • GitHub Copilot提示词终极攻略:从“能用”到“精通”的AI编程艺术
  • 宇树机器人强化学习:PPO 算法 Python 实现与解析
  • Gemini 3.0 编程能力实测与免费使用指南
  • 大语言模型 (LLM) 产品开发流程参考
  • Java 程序员必读:LeetCode 热门算法核心精讲
  • C++ 算法入门:一维动态规划基础实战
  • C语言标准库与工具链:string.h、stdio.h、stdlib.h及CMake构建
  • 基于 Python + Tkinter 实现桌面悬浮股票行情监控工具
  • Sql Server 中 ISNULL、IIF 与 CASE WHEN 的空值判断用法
  • Python 数据分析实战:从数据生成到统计输出详解
  • 无人机智能巡检在城市生命线管理中的应用实践

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online