为什么 window.btoa 不能处理 Javascript 中的 – ” 字符?

问题描述 投票:0回答:4

所以我将字符串转换为 BASE64,如下面的代码所示...

var str = "Hello World";
var enc = window.btoa(str);

这会产生

SGVsbG8gV29ybGQ=
。但是,如果我添加这些字符
– ”
(例如下面所示的代码),则不会发生转换。这背后的原因是什么?非常感谢。

var str = "Hello – World”";
var enc = window.btoa(str);
javascript
4个回答
9
投票

btoa
是一个奇异的函数,因为需要一个“二进制字符串”,即它是一个
String
数据类型,但每个“字母”并不代表一个字母而是一个字节。因此,您不能有任何 Unicode 代码点高于 0xFF(字符代码 255)的“字母”,例如破折号和“花式”引号符号所使用的字符。

您必须首先对数据进行 uri 编码,以使其安全:

> var str = `Hello – World`;
> window.btoa(encodeURIComponent(str));
"SGVsbG8lMjAlRTIlODAlOTMlMjBXb3JsZA=="

然后自己拆包的时候记得再次解码:

> var base64= "SGVsbG8lMjAlRTIlODAlOTMlMjBXb3JsZA==";
> decodeURIComponent(window.atob(base64));
"Hello – World"

或者依赖自动应用 URI 解码的目标,如

href
属性(
a
link
等)。

但是,如果您的目标没有(您自己的代码,或

src
img
上的
script
属性等),那么您需要将字符串转换为符合单字节打包的新字符串。 base64 的 MDN 上明确指出了这一点,他们的解决方案是:

function base64(data) {
  const bytes = new TextEncoder().encode(data);
  const binString = String.fromCodePoint(...bytes);
  return btoa(binString);
}

function decode64(base64) {
  const binString = atob(base64);
  const bytes = Uint8Array.from(binString, (m) => m.codePointAt(0));
  return new TextDecoder().decode(bytes);
}

如果您想在自己的代码中解压内容,则需要

decode64
,但是
base64
函数将生成一个转换后的字符串,该字符串在放入数据 URL 时将起作用(例如
data:text/javascript;base64,${base64text}
);


1
投票

问题是字符

位于 Latin1 范围之外。

为此,您可以使用

unescape
(现已弃用)

var str = "Hello – World”";
var enc = btoa(unescape(encodeURIComponent(str)));

alert(enc);

并解码:

var encStr = "SGVsbG8g4oCTIFdvcmxk4oCd";
var dec = decodeURIComponent(escape(window.atob(encStr)))

alert(dec);


0
投票

这最终是由于 JavaScript 类型系统的缺陷。

JavaScript 字符串是 16 位代码单元的字符串,通常被解释为 UTF-16。 Base64 编码是一种将 8 位 字节流转换为数字字符串的方法,方法是将每三个字节映射为四个数字,每个数字覆盖 6 位:3 × 8 = 4 × 6。正如我们所见,这很大程度上取决于每个符号的位宽度。

在定义

btoa
函数时,JavaScript 没有 8 位字节流的类型,因此 API 被定义为采用普通的 16 位字符串类型作为输入,限制是每个代码单元应该限制在[U+0000, U+00FF]范围内;当编码为 ISO-8859-1 时,这样的字符串将准确地再现预期的字节流。

字符

是U+2013,而
是U+201D;这些字符都不符合上述范围,因此该函数拒绝它。

如果要将 Unicode 文本转换为 Base64,则需要先选择一种字符编码并将其转换为字节字符串,然后对 that 进行编码。要求 Unicode 字符串本身的 Base64 编码是没有意义的。


0
投票

最可靠的方法是直接处理二进制数据。

为此,您可以将字符串编码为表示字符串的 UTF-8 版本的

ArrayBuffer
对象。

然后

FileReader
实例将能够很容易地为您提供base64。

var str = "Hello – World”";
var buf = new TextEncoder().encode( str );
var reader = new FileReader();
reader.onload = evt => { console.log( reader.result.split(',')[1] ); };
reader.readAsDataURL( new Blob([buf]) );

而且由于

Blob()
构造函数会自动将
DOMString
实例编码为 UTF-8,我们甚至可以删除
TextEncoder
对象:

var str = "Hello – World”";
var reader = new FileReader();
reader.onload = evt => { console.log( reader.result.split(',')[1] ); };
reader.readAsDataURL( new Blob([str]) );

© www.soinside.com 2019 - 2024. All rights reserved.