在Javascript中将base64数据保存为二进制文件

5
我将尝试用JavaScript下载xlsx电子表格。我已经测试了base64数据。我按以下方式解码它:var data = atob(validBase64Data); 之后,我执行以下操作:
save(name, data, type) {
  const blob = new Blob([data], {type: type});
  let objectURL = window.URL.createObjectURL(blob);
  let anchor = document.createElement('a');

  anchor.href = objectURL;
  anchor.download = name;
  anchor.click();

  URL.revokeObjectURL(objectURL);
}

当名称为filename.xlsx时,data为解码数据,type为mime类型字符串。

Excel文件被下载但无法用Excel打开。数据在某些方面上已经损坏。

此外,我使用Unix终端命令对相同的数据进行了测试,以将xlsx直接写入该文件,并且可以生成工作文件。测试如下:

  1. 将base64数据保存到test_excel.txt中
  2. 运行命令base64 -D -i test_excel.txt -o test_excel.xlsx
  3. test_excel.xlsx可以被Excel识别。

我的代码有何问题?


类型参数的值是什么? - James
在我断言我是正确的之前,请尝试运行没有“-i”的base64。如果那个不起作用,那么问题就是你的atob()不符合RFC 3548标准,这将有一个不同的解决方案,而且更简单。 - Patrick Roberts
你的保存函数没问题 - 使用客户端的xlsx文件快速演示 - 所以像@PatrickRoberts所建议的那样,你得到的数据可能有点混乱。 - James
@PatrickRoberts 尝试了不带 -i 选项。仍然正常工作。 - Lex Podgorny
@PatrickRoberts 哦,好的。我只是想提供一个基于代码的工作示例,可以用作起点来消除那些有效的部分。 - James
显示剩余9条评论
2个回答

4
这是解决问题的代码:
export default {
 save(name, data, type, isBinary) {
  if (isBinary) {
    var bytes = new Array(data.length);
    for (var i = 0; i < data.length; i++) {
        bytes[i] = data.charCodeAt(i);
    }
    data = new Uint8Array(bytes);
  }

  var blob = new Blob([data], {type: type});
  let objectURL = window.URL.createObjectURL(blob);
  let anchor = document.createElement('a');

  anchor.href = objectURL;
  anchor.download = name;
  anchor.click();

  URL.revokeObjectURL(objectURL);
 }
}

感谢所有参与解决问题的人。 此外,也要感谢:在JavaScript中从Base64字符串创建Blob的作者。


1

好的,在任何人试图“解释”问题之前,让我们澄清一些事情。

原始的.xlsx是一个二进制编码文件,这意味着数据将包含从0x000xFF的字节。

在问题中,假设此字符串已成功编码为有效的base64字符串,没有多余的字符(由使用base64而不带-i标志的测试的成功指示),并存储到validBase64Data中。

问题在于atob(validBase64Data)生成的字符串是解码为utf-8格式,而不是二进制格式。正如我之前所说,原始的二进制字符串包含在0x800xFF范围内的非ASCII字节。在utf-8中,这些代码点被存储为两个字节而不是一个字节,因此解决方案是将utf-8字符串data中每个字符的代码点转换为存储为Uint8Array的字节,然后从中构建一个Blob,如Creating a Blob from a base64 string in JavaScript所述。
一个天真的解决方案可能看起来像这样,请参考Creating a Blob from a base64 string in JavaScript以获取更高效的解决方案:
const blob = new Blob([Uint8Array.from(data, c => c.charCodeAt(0))], { type });
//...

这里使用了TypedArray.from(iterable, mapFn)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接