根据我的测试,标题中的错误只在谷歌Chrome中抛出。我正在base64编码一个大的XML文件,以便它可以下载:

this.loader.src = "data:application/x-forcedownload;base64,"+
                  btoa("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
                  +"<"+this.gamesave.tagName+">"
                  +this.xml.firstChild.innerHTML
                  +"</"+this.gamesave.tagName+">");

这一点。加载器是隐藏的iframe。

这个错误实际上是一个相当大的变化,因为通常情况下,谷歌Chrome浏览器会崩溃在btoa调用。Mozilla Firefox在这里没有问题,所以这个问题与浏览器有关。 我不知道档案里有什么奇怪的字。事实上,我相信没有非ascii字符。

问: 我如何找到有问题的字符,并替换他们,让Chrome停止抱怨?

我已经尝试使用downadify来启动下载,但它不起作用。它是不可靠的,并且不会抛出任何错误来进行调试。


当前回答

使用btoa和unescape和encodeURIComponent不适合我。用XML/HTML实体替换所有特殊字符,然后转换为base64表示是解决这个问题的唯一方法。一些代码:

base64 = btoa(str.replace(/[\u00A0-\u2666]/g, function(c) {
    return '&#' + c.charCodeAt(0) + ';';
}));

其他回答

使用btoa和unescape和encodeURIComponent不适合我。用XML/HTML实体替换所有特殊字符,然后转换为base64表示是解决这个问题的唯一方法。一些代码:

base64 = btoa(str.replace(/[\u00A0-\u2666]/g, function(c) {
    return '&#' + c.charCodeAt(0) + ';';
}));

我自己也遇到了这个问题。

首先,稍微修改你的代码:

var download = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
                  +"<"+this.gamesave.tagName+">"
                  +this.xml.firstChild.innerHTML
                  +"</"+this.gamesave.tagName+">";

this.loader.src = "data:application/x-forcedownload;base64,"+
                  btoa(download);

然后使用你最喜欢的web检查器,在指定this.loader的代码行上放置一个断点。Src,然后执行以下代码:

for (var i = 0; i < download.length; i++) {
  if (download[i].charCodeAt(0) > 255) {
    console.warn('found character ' + download[i].charCodeAt(0) + ' "' + download[i] + '" at position ' + i);
  }
}

根据应用程序的不同,替换超出范围的字符可能有效,也可能无效,因为您将修改数据。请参阅MDN上关于使用btoa方法使用unicode字符的说明:

https://developer.mozilla.org/en-US/docs/Web/API/window.btoa

使用库代替

我们没必要白费力气。使用库来节省时间和麻烦。

js-base64

https://github.com/dankogai/js-base64很好,我确认它很好地支持unicode。

Base64.encode('dankogai');  // ZGFua29nYWk=
Base64.encode('小飼弾');    // 5bCP6aO85by+
Base64.encodeURI('小飼弾'); // 5bCP6aO85by-

Base64.decode('ZGFua29nYWk=');  // dankogai
Base64.decode('5bCP6aO85by+');  // 小飼弾
// note .decodeURI() is unnecessary since it accepts both flavors
Base64.decode('5bCP6aO85by-');  // 小飼弾

我只是觉得我应该分享我是如何解决这个问题的,以及为什么我认为这是正确的解决方案(前提是你不针对旧浏览器进行优化)。

将数据转换为dataURL (data:…)

var blob = new Blob(
              // I'm using page innerHTML as data
              // note that you can use the array
              // to concatenate many long strings EFFICIENTLY
              [document.body.innerHTML],
              // Mime type is important for data url
              {type : 'text/html'}
); 
// This FileReader works asynchronously, so it doesn't lag
// the web application
var a = new FileReader();
a.onload = function(e) {
     // Capture result here
     console.log(e.target.result);
};
a.readAsDataURL(blob);

允许用户保存数据

除了显而易见的解决方案-打开新窗口,以您的dataURL作为URL,您还可以做其他两件事。

1. 使用fileSaver.js

文件保护程序可以使用预定义的文件名创建实际的文件保存对话框。它还可以退回到正常的dataURL方法。

2. 使用(实验性)URL.createObjectURL

这对于重用base64编码的数据非常有用。它为你的dataURL创建一个简短的URL:

console.log(URL.createObjectURL(blob));
//Prints: blob:http://stackoverflow.com/7c18953f-f5f8-41d2-abf5-e9cbced9bc42

不要忘记使用包含前导blob前缀的URL。我使用文档。身体:

你可以使用这个短URL作为AJAX目标,<脚本>源或< > href位置。你要负责破坏URL:

URL.revokeObjectURL('blob:http://stackoverflow.com/7c18953f-f5f8-41d2-abf5-e9cbced9bc42')

将字符串转换为utf-8的解决方案,这比许多其他答案建议的utf-16或URLEncoded版本略短。它也与其他语言(如python和PHP)解码字符串的方式更加兼容:

编码

function btoa_utf8(value) {
    return btoa(
        String.fromCharCode(
            ...new TextEncoder('utf-8')
                   .encode(hash_value)
        )
    );
}

解码

function atob_utf8(value) {
    const value_latin1 = atob(value);
    return new TextDecoder('utf-8').decode(
        Uint8Array.from(
            { length: value_latin1.length },
            (element, index) => value_latin1.charCodeAt(index)
        )
    )
}

如果您愿意,您可以使用不同的字符编码替换其中的'utf-8'字符串。

注意:这取决于TextEncoder类。目前大多数浏览器都支持这一功能,但如果您需要针对较老的浏览器,请检查它是否可用。