根据我的测试,标题中的错误只在谷歌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来启动下载,但它不起作用。它是不可靠的,并且不会抛出任何错误来进行调试。


当前回答

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

将数据转换为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')

其他回答

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

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

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

作为Stefan Steiger回答的补充:(作为评论看起来不太好)

扩展字符串原型:

String.prototype.b64encode = function() { 
    return btoa(unescape(encodeURIComponent(this))); 
};
String.prototype.b64decode = function() { 
    return decodeURIComponent(escape(atob(this))); 
};

用法:

var str = "äöüÄÖÜçéèñ";
var encoded = str.b64encode();
console.log( encoded.b64decode() );

注意:

正如评论中所述,不建议使用unescape,因为它将来可能会被删除:

警告:尽管unescape()并没有被严格弃用(就像“从Web标准中删除”一样),但它在ECMA-262标准的附件B中有定义,其介绍说明: 本附录中规定的所有语言特性和行为都有一个或多个不受欢迎的特性,如果没有遗留用法,将从本规范中删除。 注意:不要使用unescape来解码uri,而是使用decodeURI或decodeURIComponent来代替。

将字符串转换为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类。目前大多数浏览器都支持这一功能,但如果您需要针对较老的浏览器,请检查它是否可用。

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

将数据转换为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')

另一个不使用unescape的浏览器解决方案:

function ToBinary(str)
{
    let result="";

    str=encodeURIComponent(str);

    for(let i=0;i<str.length;i++)
        if(str[i]=="%")
        {
            result+=String.fromCharCode(parseInt(str.substring(i+1,i+3),16));
            i+=2;
        }
        else
            result+=str[i];

    return result;
}

btoa(ToBinary("тест"));//0YLQtdGB0YI=