【问题标题】:Convert binary data to base64 does not work with btoa unescape将二进制数据转换为 base64 不适用于 btoa unescape
【发布时间】:2020-12-02 10:40:04
【问题描述】:

我是 React 新手,想显示下载为二进制数据的图像。我从 api 调用下载图像数据到 adobe lightroom api。 api 调用有效,因为图像在 Postman 中显示没有问题。我还可以将图像数据保存为 jpeg 文件,并且显示正常。

在 React 中,我想做<img src={`data:image/jpeg;base64,${theImage}`} />,为此我需要将二进制数据转换为 base64 编码的字符串。当我使用 cat image.jpeg|base64 > base64.txt 转换下载的 jpeg 时,生成的字符串可以在我的 React 应用程序中使用。

但是当我在 React 中尝试 var theImage = btoa(binarydata) 时,我得到了 Unhandled Rejection (InvalidCharacterError): Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.

搜索问题后,我尝试使用var theImage = btoa(unescape(encodeURIComponent( binarydata ))) 和类似的建议解决方案,但从这些结果中得到的字符串并不像看起来那样是 jpeg 的有效 base64 编码(我尝试在线 base64 中的转换结果- >图像服务,没有显示图像)。我还尝试了其他建议的解决方案,例如 base64-jsjs-base64 库,但这些库都没有创建可以在我的 React 代码中显示的有效 base64 有效图像。

btoa 抛出 latin1 异常时,如何将 jpeg 二进制数据转换为有效的 Base64 图像编码?

【问题讨论】:

  • 旁注:在 data URI 中,base64, 之后不应有空格。
  • unescape(及其表亲escape)永远不应该被使用。它们是 1995 年以来的遗留问题。特别是,unescape 不是 encodeURIComponent 的反面。
  • 你是怎么得到binarydata的?错误消息告诉您问题在于binarydata 不是有效的binary string(其中包含字符代码值> 255 的字符)。
  • 我通过对 Adob​​e lightroom API 的 API 调用获得它。 const resp = await axios.get('https://lr.adobe.io/v2/catalogs/<catalogid>/assets/${imageid}/renditions/thumbnail2x', {... 调用在 Postman 中有效(显示图像)。文档在这里:adobe.io/apis/creativecloud/lightroom/apidocs.html#/assets/…
  • 这只是故事的一部分。一旦你得到resp,你会怎么做?服务器如何发送数据?请注意,您不能只将 Blob 或 ArrayBuffer(例如)转储到 btoa

标签: javascript reactjs base64 jpeg


【解决方案1】:

您说过您正在使用axios.get 从服务器获取图像。您可能会收到BufferArrayBufferBlob 等,但这取决于您如何处理从axios 收到的回复。

我不使用axios(从不觉得有必要),但您可以通过fetch 轻松地从服务器获取二进制数据的data URI:

// 1.
const response = await fetch("/path/to/resource");
// 2.
if (!response.ok) {
    throw new Error("HTTP error " + response.status);
}
// 3.
const buffer = await response.arrayBuffer();
// 4.
const byteArray = new Uint8Array(buffer);
// 5.
const charArray = Array.from(byteArray, byte => String.fromCharCode(byte));
// 6.
const binaryString = charArray.join("");
// 7.
const theImage = btoa(binaryString);

或者更简洁:

const response = await fetch("/path/to/resource");
if (!response.ok) {
    throw new Error("HTTP error " + response.status);
}
const buffer = await response.arrayBuffer();
const binaryString = Array.from(new Uint8Array(buffer), byte => String.fromCharCode(byte)).join("");
const theImage = btoa(binaryString);

这是如何工作的:

  1. 我们请求图像数据。
  2. 我们检查请求是否有效(fetch 仅在 network 错误时拒绝它的承诺,而不是 HTTP 错误;这些是通过 statusok 属性报告的。
  3. 我们将响应正文读入ArrayBuffer;缓冲区将包含二进制图像数据。
  4. 我们希望将该缓冲区数据转换为binary string。为此,我们需要单独访问字节,因此我们创建了一个Uint8Array(使用该缓冲区)来访问它们。
  5. 要将字节数组转换为二进制字符串,我们需要将每个字节转换为其等效字符,然后将它们连接成一个字符串。让我们通过使用Array.from 来做到这一点,并在其映射函数(为每个字节调用)中,我们将使用String.fromCharCode 将字节转换为字符。 (这并不是真正的转换。字节 25 [例如] 成为字符串中字符代码为 25 的字符。)
  6. 现在我们通过将该数组中的字符连接成一个字符串来创建二进制字符串。
  7. 最后,我们将该字符串转换为 Base64。

查看文档,看起来axios 允许您提供选项responseType: "arraybuffer" 来获取数组缓冲区。如果我没看错,您可以像这样使用axios

const response = await axios.get("/path/to/resource", {responseType: "arraybuffer"});
const binaryString = Array.from(new Uint8Array(response.body), v => String.fromCharCode(v)).join("");
const theImage = btoa(binaryString);

【讨论】:

  • 谢谢,我尝试了 fetch 版本,但没有收到转换错误,因此我得到了正确的 Base64 编码。但是,我得到Unhandled Rejection (Error): HTTP error 200Uncaught (in promise) Error: HTTP error 200。并且图像仍然没有显示。我对 React 和 Javascript 很陌生,所以我想我不太明白我应该如何将 await fetch 代码转换为实际下载数据而不是陷入等待承诺的东西。
  • @MatsSkoglund - 这是答案中的一个错误,我错过了应该是if (!response.ok) {!。我现在已经修好了。 (很好地描述了您在答案中使用代码时遇到的问题,顺便说一句,阅读它我知道我做错了什么。)快乐编码!
  • 对,@T:J.Crowder,我修复了!。但是,我现在在const theImage = atob(binaryString); 行上得到Unhandled Rejection (InvalidCharacterError): Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.。我尝试改用const theImage = btoa(binaryString);,现在它可以工作了。它正在添加做到这一点的const buffer = await response.arrayBuffer(); const binaryString = Array.from(new Uint8Array(buffer), v => String.fromCharCode(v)).join("");。我不明白,但现在可以了。非常感谢!
  • @MatsSkoglund - 啊!当我的意思是btoa 时,我总是输入atob!很高兴弄清楚这一点。我已经更新了答案以添加解释性说明,希望对您有所帮助。
【解决方案2】:

从中获取您的图像作为Blobgenerate a blob:// URI

data:// URLs 完全没有效率,并且比 blob:// URLs 需要更多的内存空间。 data:// URL 比它所代表的实际数据多占用 34% 的空间,并且它必须存储在 DOM 中 + 再次解码为二进制文件才能被图像解码器读取。而 blob:// URI 只是一个指向内存中二进制数据的指针。

blob:// URLs 并不完美,但在浏览器正确实现 srcDoc 之前,它仍然是我们拥有的最好的。

所以如果根据 cmets 你在浏览器中使用 axios,你可以这样做

const blob = await axios.get("/path/to/resource", {responseType: "blob"});
const theImage = URL.createObjectURL(blob);

如果你想使用 fetch API

const response = await fetch("/path/to/resource");
if (!response.ok) {
    throw new Error("HTTP error " + response.status);
}
const blob = await response.blob();
const theImage = URL.createObjectURL(blob);

附:
如果您没有任何特殊原因通过 AJAX 获取此图像(例如凭据或特殊 POST 参数),则直接将资源的 URL 作为图像的src 传递:

<img src="/path/to/resource" />

【讨论】:

    猜你喜欢
    • 2017-10-13
    • 2012-06-14
    • 1970-01-01
    • 2020-04-25
    • 1970-01-01
    • 2013-01-15
    • 1970-01-01
    • 2021-05-07
    • 1970-01-01
    相关资源
    最近更新 更多