从 xhr.responseText 恢复 ArrayBuffer

2024-03-14

我需要从向我发送 Base64 答案的 http 请求中获取数组缓冲区。 对于这个请求,我无法使用XMLHttpRequest.responseType="arraybuffer".

我从该请求中得到的响应已通读xhr.responseText。因此它被编码为 DOMString。我正在尝试将其作为数组缓冲区恢复。

我尝试使用 DOMString 返回到 base64btoa(mysString) or window.btoa(unescape(encodeURIComponent(str)))但第一个选项失败了,而第二个选项没有给出相同的 base64。每个 base64 的前几个字符的示例:

传入:UEsDBBQACAgIACp750oAAAAAAAAAAAAAAAALAAAAX3JlbHMvLnJlbH

第二次处理后:UEsDBBQACAgIAO+/ve+/ve+/vUoAAAAAAAAAAAAAAAALAAAAX3JlbHMvLnJlbH

正如你所看到的,它的一部分是相似的,但有些部分却相差甚远。 为了让它正确,我缺少什么?


我也有同样的问题。

解决方案(我在 Chrome(68.0.3440.84) 上运行)

let url = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='

let iso_8859_15_table = { 338: 188, 339: 189, 352: 166, 353: 168, 376: 190, 381: 180, 382: 184, 8364: 164 }

function iso_8859_15_to_uint8array(iso_8859_15_str) {
    let buf = new ArrayBuffer(iso_8859_15_str.length);
    let bufView = new Uint8Array(buf);
    for (let i = 0, strLen = iso_8859_15_str.length; i < strLen; i++) {
        let octet = iso_8859_15_str.charCodeAt(i);
        if (iso_8859_15_table.hasOwnProperty(octet))
            octet = iso_8859_15_table[octet]
        bufView[i] = octet;
        if(octet < 0 || 255 < octet)
            console.error(`invalid data error`)
    }
    return bufView
}

req = new XMLHttpRequest();
req.overrideMimeType('text/plain; charset=ISO-8859-15');
req.onload = () => {
    console.log(`Uint8Array : `)
    var uint8array = iso_8859_15_to_uint8array(req.responseText)
    console.log(uint8array)
}
req.open("get", url);
req.send();

下面是我学到的解决这个问题的解释。

解释

为什么有些部分偏离了?

因为 TextDecoder 会导致数据丢失(您的情况是 utf-8)。

例如,我们来谈谈UTF-8 https://en.wikipedia.org/wiki/UTF-8

  • Unicode 的可变宽度字符编码。

  • It has rules(这会成为问题。)由于可变长度特性和ASCII兼容性等原因。

  • 因此,解码器可能会将不符合要求的字符替换为替换字符,例如 U+003F(?,问号)或 U+FFFD(�,Unicode 替换字符)。

  • 在utf-8情况下,0~127的值是稳定的,128~255的值是不稳定的。 128~255将转换为U+FFFD

除了 UTF-8 之外,其他文本解码器是否安全?

不。在大多数情况下,不安全rules.

UTF-8 也是不可恢复的。 (128~255设置为U+FFFD)

如果二进制数据和解码结果能够一一对应,则可以将其恢复。

怎么解决呢?

  1. 查找可恢复的文本解码器。
  2. 强制 MIME 类型为传入数据的可恢复字符集。xhr_object.overrideMimeType('text/plain; charset=ISO-8859-15')
  3. 从字符串中恢复二进制数据recover table收到时。

查找可恢复的文本解码器。

要恢复,请避免解码结果重复的情况。

以下代码是一个简单的示例,因此可能会丢失可恢复的文本解码器,因为它只考虑 Uint8Array。

let bufferView = new Uint8Array(256);
for (let i = 0; i < 256; i++)
    bufferView[i] = i;

let recoverable = []
let decoding = ['utf-8', 'ibm866', 'iso-8859-2', 'iso-8859-3', 'iso-8859-4', 'iso-8859-5', 'iso-8859-6', 'iso-8859-7', 'iso-8859-8', 'iso-8859-8i', 'iso-8859-10', 'iso-8859-13', 'iso-8859-14', 'iso-8859-15', 'iso-8859-16', 'koi8-r', 'koi8-u', 'macintosh', 'windows-874', 'windows-1250', 'windows-1251', 'windows-1252', 'windows-1253', 'windows-1254', 'windows-1255', 'windows-1256', 'windows-1257', 'windows-1258', 'x-mac-cyrillic', 'gbk', 'gb18030', 'hz-gb-2312', 'big5', 'euc-jp', 'iso-2022-jp', 'shift-jis', 'euc-kr', 'iso-2022-kr', 'utf-16be', 'utf-16le', 'x-user-defined', 'ISO-2022-CN', 'ISO-2022-CN-ext']
for (let dec of decoding) {
    try {
        let decodedText = new TextDecoder(dec).decode(bufferView);
        let loss = 0
        let recoverTable = {}
        let unrecoverable = 0
        for (let i = 0; i < decodedText.length; i++) {
            let charCode = decodedText.charCodeAt(i)
            if (charCode != i)
                loss++

            if (!recoverTable[charCode])
                recoverTable[charCode] = i
            else
                unrecoverable++
        }
        let tableCnt = 0
        for (let props in recoverTable) {
            tableCnt++
        }
        if (tableCnt == 256 && unrecoverable == 0){
            recoverable.push(dec)
            setTimeout(()=>{
                console.log(`[${dec}] : err(${loss}/${decodedText.length}, ${Math.round(loss / decodedText.length * 100)}%) alive(${tableCnt}) unrecoverable(${unrecoverable})`)
            },10)
        }
        else {
            console.log(`!! [${dec}] : err(${loss}/${decodedText.length}, ${Math.round(loss / decodedText.length * 100)}%) alive(${tableCnt}) unrecoverable(${unrecoverable})`)
        }
    } catch (e) {
        console.log(`!! [${dec}] : not supported.`)
    }
}

setTimeout(()=>{
    console.log(`recoverable Charset : ${recoverable}`)
}, 10)

在我的控制台中,这个返回

可恢复的字符集:ibm866、iso-8859-2、iso-8859-4、iso-8859-5、iso-8859-10、iso-8859-13、iso-8859-14、iso-8859-15、iso-8859 -16,koi8-r,koi8-u,macintosh,windows-1250,windows-1251,windows-1252,windows-1254,windows-1256,windows-1258,x-mac-西里尔文,x-用户定义

我用过iso-8859-15在这个答案的开头。 (它的桌子尺寸最小。)


附加测试)UTF-8 和 ISO-8859-15 结果之间的比较

检查U+FFFD在使用ISO-8859-15时确实消失了。

function requestAjax(url, charset) {
    let req = new XMLHttpRequest();
    if (charset)
        req.overrideMimeType(`text/plain; charset=${charset}`);
    else
        charset = 'utf-8';
    req.open('get', url);
    req.onload = () => {
        console.log(`==========\n${charset}`)
        console.log(`${req.responseText.split('', 50)}\n==========`);
        console.log('\n')
    }
    req.send();
}

var url = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==';
requestAjax(url, 'ISO-8859-15');
requestAjax(url);

底线

  • Recover binary data to, from string needs some additional job.
    • 查找可恢复的文本编码器/解码器。
    • 制作恢复表
    • 用桌子恢复。
    • (你可以参考最上面的代码。)
  • 要使用此技巧,请将传入数据的 MIME 类型强制为所需的字符集。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从 xhr.responseText 恢复 ArrayBuffer 的相关文章

  • 实现快速 Javascript 搜索?

    基本上 我有一个带有文本框的页面和 ul 列在其下面 这 ul 由用户的朋友列表填充 用户开始在文本框中输入朋友的名字 例如按 r 我想立即更新 ul 每次按键仅显示名字以 R 开头的朋友 例如 Richard Redmond Raheem
  • Angular2 与 Jquery-ui 滑块

    我尝试将 Jquery ui 滑块与 angular2 一起使用 我想让变量 slideValue 显示滑块的值 但我不知道如何将我的模型或变量从角度绑定到滑块 这是我的滑块组件 import Component ElementRef In
  • 带有闭包的 JavaScript 性能

    var name function n var digits one two three four return digits n var namenew function digits one two three four return
  • Chrome 开发工具:无需切换到“源”选项卡即可进入调试器

    如果我把debugger https developer mozilla org en US docs JavaScript Reference Statements debugger当 Chrome 开发工具打开时 我的 JavaScri
  • 如何翻转 Twitter Bootstrap 的工具提示

    我正在使用 Twitter 的 Bootstrap 来实现工具提示 目前 工具提示显示在链接上方 我希望工具提示出现在链接下方 我该怎么做呢 我正在触发工具提示 它明确指出 底部 但它不想为我工作 tooltip tooltip place
  • 用于传输命名参数和正文的云端点资源属性不起作用

    我正在尝试通过gapi client rpc调用实现对谷歌云端点的调用 如文档中所述 和Google Cloud Endpoints 使用 JS 客户端进行调用 传递参数和 JSON 正文 https stackoverflow com q
  • 从 html5

    我正在寻找一种方法来根据用户代理字符串将控件属性添加到视频标签 我不希望在 iPad 和 Android 之外的任何浏览器或设备上出现控件属性 所以我认为用户代理是最好的识别方法 因为 ipad 和 android 一词出现在各自的 UA
  • 创建 html 结构,每个 li 中仅允许 3 个 div 元素。在 React + underscore.js 中

    这是以下内容的位副本如何创建每个 li 中仅允许 3 个 div 元素的 html 结构 在 React underscore js 中 https stackoverflow com questions 38008023 how to c
  • 在 JQuery ui 自动完成中显示图像

    我有一个带有 JQuery ui 自动完成功能的脚本 可以完美运行 有一个显示用户名字和姓氏的搜索过程 但在我的数据库中 还有用户的图片 我想将其显示在带有名字和姓氏的建议中 数据库中pic包含图片url 剧本 function searc
  • 如何更改元素的 CSS 类并在单击时删除所有其他类

    我如何处理 AngularJS 2 中的一种情况 即单击一个元素需要更改其自己的样式 并且如果其他元素具有该样式 则需要将其删除 最好在一个函数中 如同Angular js 如何在单击时更改元素 css 类并删除所有其他元素 https s
  • 我们如何使用 thymeleaf 绑定对象列表的列表

    我有一个表单 用户可以在其中添加任意数量的内容表对象这也可以包含他想要的列对象 就像在 SQL 中构建表一样 我尝试了下面的代码 但没有任何效果 并且当我尝试绑定两个列表时 表单不再出现 控制器 ModelAttribute page pu
  • Onblur 事件在另一个 div 的 onclick 之前触发

    如上所述 我有一个按钮 单击该按钮将打开子菜单 对于子菜单中的每个选项 都有三个元素 我认为实际上还有更多元素 但为了简单起见 将其保留为 3 我将焦点放在子菜单的主 div 白色 框架 上 Onblur 这个 div 然后我隐藏子菜单 这
  • Facebook API Javascript JSON 响应

    function getUser FB api me function response console log Response is response alert Your name is response first name ale
  • Socket.io 与服务器离线连接

    如何检测服务器是否离线或由于其他原因无法连接 我的代码看起来像这样 this socket io connect connectionInfo reconnect false 它不会抛出任何错误 因此 try catch 子句不起作用 Us
  • 在React组件中使用的字符串变量中插入html

    我正在为我的投资组合网站构建一个反应应用程序 目前我已经用 JSX 编写了应用程序 因此我可以添加以下内容 class Project extends React Component render return div h1 this pr
  • 动态 dom 操作后,如何在浏览器历史记录中保留 dom 状态?

    是否有一个通用的解决方案来保留 dom 状态 以便当用户使用后退 前进返回页面时 整个页面处于他们离开时的确切状态 这篇文章询问并回答了为什么不同浏览器和不同 javascript 库的行为不一致 Ajax 后退按钮和 DOM 更新 htt
  • 有没有办法从画布上清除一个元素而不消除其他元素?

    我正在使用画布构建页面加载器 并使用 es6 类 虽然目前我无法使其正常工作 原因之一是我找不到清除画布的方法进展 到目前为止 这是我的代码 class Loader constructor width height this width
  • YouTube iFrame Player API 无法在 DOMWindow 上执行 postMessage

    我正在尝试使用以下命令将 youtube 加载到我的网页中YouTube iFrame Player API 并在加载时出现以下错误 Failed to execute postMessage on DOMWindow The target
  • 角度 4 单击按钮功能未触发

    我正在尝试检查文本输入是否为空或不在角度 4 中 我没有为此使用表单 这只是一个输入字段 当我在下面的按钮中执行 addLocaton 函数时 需要进行检查 我的输入字段
  • 我如何用 javascript/jquery 进行两指拖动?

    我正在尝试创建当有两个手指放在 div 上时拖动 div 的功能 我已将 div 绑定到 touchstart 和 touchmove 事件 我只是不确定如何编写这些函数 就像是if event originalEvent targetTo

随机推荐