你了解 input,那你了解 Safari 里的 input 么?

29 阅读4分钟

大家好,我卡尔。

近日,一位前端同学在处理图片上传业务时,遇到了一个极其诡异的 Bug。在 Chrome、Firefox 上跑得好好的代码,一到 Safari就莫名其妙地报错。

经过一番抽丝剥茧,发现这竟然是 Safari 对 input[type=text] 的一种"特殊照顾"。

这种"照顾"不仅让 DataURL 截断,还可能导致你的生产应用出现各种难以排查的事故。

本文让我们来聊聊。

为什么需要关注 input 的长度限制

在我们的认知里,HTML5 的 input 标签如果没有显式设置 maxlength,那它应该是"无限长"的(受限于内存)。

但在 Safari 浏览器中,这个常识被打破了。

考虑如下场景:你需要实现一个图片上传功能,流程是这样的:

  1. 用户选图。
  2. 前端用 Canvas 压缩、加水印。
  3. 将结果转为 base64DataURL
  4. 把这个长长的字符串塞进一个隐藏的 input 随表单提交。

代码可能长这样:

// 假设 imageDataURL 是生成的 DataURL
const hiddenInput = document.querySelector('input[type="text"]');
const previewImage = document.querySelector('img');

// 赋值
hiddenInput.value = imageDataURL;
previewImage.src = imageDataURL;

// 验证一下
console.log('数据一致吗?', hiddenInput.value === previewImage.src);

你猜在 Safari 里会输出什么?

在 Chrome 里,这当然是 true。但在 Safari 里,如果你的图片稍微大一点(比如超过 512KB),结果竟然是 false

是不是已经有点晕了?

实际上,Safari 对 input[type=text] 有一个隐性的 512KB 长度限制。一旦超过这个值,它会默默地把后面的内容截断,而且不报任何错。

生产环境的高风险场景

有些同学会说,我平时不怎么传图片,这个 Bug 离我很远。

真的吗?其实在现代复杂的 Web 应用中,这个坑无处不在。

1. 图片上传/编辑功能

这是最直接的受灾区。现在大家手机拍的照片动辄几 MB,即使经过 Canvas 压缩,1920x1080 分辨率的 Base64 编码也很容易突破 512KB。

症状: Safari 用户上传后,后端收到的是损坏的图片,或者直接报"DataURL 格式错误"。

2. 富文本编辑器

很多老牌富文本编辑器(或者自研的编辑器)在提交时,会将整个 HTML 内容序列化后塞进一个隐藏的 inputhidden 字段。

症状: 当用户写了一篇长文,或者文章里嵌入了大量格式化信息时,Safari 用户保存后会发现文章"断了",末尾的内容直接消失。

3. JSON 数据存储

为了省事,有些开发者喜欢把复杂的表单状态(比如多步骤表单、购物车数据)直接 JSON.stringify 后存入隐藏 input

症状: 当数据结构变得复杂,JSON 字符串超过 512KB 时,Safari 提交的数据将不再是合法的 JSON(因为末尾的 } 被截断了),后端解析直接崩溃。

4. 文件上传预览

使用 FileReader 读取文件为 Base64 用于预览或临时存储。

症状: 用户选择稍大的文件(如 PDF 或文档)后,发现无法正常上传或预览。

相比于 input,textarea 的优势

既然知道了 Safari 的这个"毛病",解决起来其实很简单。

既然 input[type=text] 有限制,那我们就换一个真正没有限制的标签 —— textarea

<textarea style="display:none;" name="image_data"></textarea>

经过测试,textarea 在 Safari 下表现非常稳定,能够完美承载大容量的字符串。

有些同学可能会问:为什么 Safari 要这么设计?

我猜想,Safari 可能是为了优化内存占用,或者是想在底层强制区分"单行输入"和"多行文本"的使用场景。毕竟,谁会在一个单行输入框里塞进半兆的数据呢?

但这种"不打招呼"的截断,确实给前端开发者带来了不小的麻烦。

总结

Safari 真是"新时代的 IE"。

毕竟,这种违反 W3C 直觉的设计,隐藏在最基础的标签里。如果:

  • 你的项目涉及大量客户端数据处理
  • 你的用户中有大量的 iOS/macOS 用户
  • 你正在使用隐藏 input 传递大数据

那大概率需要检查一下你的代码,看看有没有掉进这个 512KB 的陷阱。

对于 Safari 的这种设计,你怎么看?