HTML5 文件 API 读取为文本和二进制

IT技术 javascript file html
2021-02-03 14:56:18

我目前正在研究 HTML5 文件 API,我需要获取二进制文件数据。 The FileReaderreadAsTextreadAsDataURL方法工作正常,但readAsBinaryString返回与 相同的数据readAsText

我需要二进制数据,但我得到了一个文本字符串。我错过了什么吗?

1个回答

2018 年的注意事项readAsBinaryString已过时。对于您以前使用过的用例,现在您将使用readAsArrayBuffer(或在某些情况下,readAsDataURL)代替。


readAsBinaryString表示数据必须表示为二进制字符串,其中:

...每个字节都由 [0..255] 范围内的整数表示。

JavaScript 最初没有“二进制”类型(直到 ECMAScript 5 的 WebGL 支持Typed Array * (详细信息如下) ——它已被 ECMAScript 2015 的ArrayBuffer取代),因此它们使用 String 并保证不存储任何字符在字符串中将超出范围 0..255。(他们本来可以使用数字数组代替,但他们没有;也许大字符串比大数字数组更节省内存,因为数字是浮点数。)

如果你正在读这主要是在西部的脚本文本(主要是英语,例如)的文件,那么该字符串看起来会很多像文本。如果您读取一个包含 Unicode 字符的文件,您应该会注意到一个不同之处,因为 JavaScript 字符串是UTF-16 ** (详情如下),因此某些字符的值会高于 255,而根据 File 的“二进制字符串” API 规范不会有任何大于 255 的值(对于 Unicode 代码点的两个字节,您将有两个单独的“字符”)。

如果您正在阅读一个根本不是文本的文件(也许是图像),您可能仍然会在readAsText之间获得非常相似的结果readAsBinaryString,但是readAsBinaryString知道不会尝试解释多字节序列作为字符。你不知道如果你使用readAsText, 因为readAsText将使用编码确定来尝试找出文件的编码是什么,然后将它映射到 JavaScript 的 UTF-16 字符串。

如果您创建一个文件并将其存储在 ASCII 或 UTF-8 以外的其他格式中,您可以看到效果。(在 Windows 中,您可以通过记事本执行此操作;“另存为”作为带有“Unicode”的编码下拉列表,通过查看数据,它们似乎意味着 UTF-16;我确定 Mac OS 和 * nix 编辑器有一个类似的功能。)这是一个页面,它转储两种方式读取文件的结果:

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>Show File Data</title>
<style type='text/css'>
body {
    font-family: sans-serif;
}
</style>
<script type='text/javascript'>

    function loadFile() {
        var input, file, fr;

        if (typeof window.FileReader !== 'function') {
            bodyAppend("p", "The file API isn't supported on this browser yet.");
            return;
        }

        input = document.getElementById('fileinput');
        if (!input) {
            bodyAppend("p", "Um, couldn't find the fileinput element.");
        }
        else if (!input.files) {
            bodyAppend("p", "This browser doesn't seem to support the `files` property of file inputs.");
        }
        else if (!input.files[0]) {
            bodyAppend("p", "Please select a file before clicking 'Load'");
        }
        else {
            file = input.files[0];
            fr = new FileReader();
            fr.onload = receivedText;
            fr.readAsText(file);
        }

        function receivedText() {
            showResult(fr, "Text");

            fr = new FileReader();
            fr.onload = receivedBinary;
            fr.readAsBinaryString(file);
        }

        function receivedBinary() {
            showResult(fr, "Binary");
        }
    }

    function showResult(fr, label) {
        var markup, result, n, aByte, byteStr;

        markup = [];
        result = fr.result;
        for (n = 0; n < result.length; ++n) {
            aByte = result.charCodeAt(n);
            byteStr = aByte.toString(16);
            if (byteStr.length < 2) {
                byteStr = "0" + byteStr;
            }
            markup.push(byteStr);
        }
        bodyAppend("p", label + " (" + result.length + "):");
        bodyAppend("pre", markup.join(" "));
    }

    function bodyAppend(tagName, innerHTML) {
        var elm;

        elm = document.createElement(tagName);
        elm.innerHTML = innerHTML;
        document.body.appendChild(elm);
    }

</script>
</head>
<body>
<form action='#' onsubmit="return false;">
<input type='file' id='fileinput'>
<input type='button' id='btnLoad' value='Load' onclick='loadFile();'>
</form>
</body>
</html>

如果我将它与存储在 UTF-16 中的“Testing 1 2 3”文件一起使用,我得到的结果如下:

文字 (13):

54 65 73 74 69 6e 67 20 31 20 32 20 33

二进制 (28):

ff fe 54 00 65 00 73 00 74 00 69 00 6e 00 67 00 20 00 31 00 20 00 32 00 20 00 33 00

如您所见,readAsText解释了字符,所以我得到了 13(“测试 1 2 3”的长度),readAsBinaryString但没有,所以我得到了 28(两字节BOM加上每个字符的两个字节)。


* XMLHttpRequest.response withresponseType = "arraybuffer"在 HTML 5 中受支持。

** “JavaScript 字符串是 UTF-16”可能看起来很奇怪;他们不只是Unicode吗?不,JavaScript 字符串是一系列 UTF-16 代码单元您将代理对视为两个单独的 JavaScript“字符”,尽管实际上,代理对作为一个整体只是一个字符。有关详细信息,请参阅链接。

非常感谢@TJCrowder——我删除了我的评论以防止其他人被我的过度简化所误导,并感谢你发布细节——我相信其他人会发现你的评论更有帮助(阅读这篇文章真的是一个好主意,因为你说,如果有人在这个区域闲逛)。
2021-03-16 14:56:18
@digitalFresh:字符串二进制数据。在您发表评论时,我发布了一个可能有帮助的示例。JavaScript 没有“二进制”类型,因此他们使用字符串,并保证字符串中存储的任何字符都不会超出范围 0..255。(他们本可以使用数字数组来代替,但他们没有。)该示例显示了如何从字符串中获取“字符”的原始值。
2021-03-17 14:56:18
@TheProHands:如果您使用readAsBinaryString,您将得到一个字符串,其中包含字符代码为 0 的字符。请参阅示例的第四个字节(以及之后的每个其他字节)。
2021-03-28 14:56:18
@BrianM.Hunt:谢谢,不过,我确实在答案中澄清了我的陈述,因为“JavaScript 字符串是 UTF-16”本身似乎是一个奇怪的陈述(UTF-16?为什么它们不只是 Unicode?)。:-)
2021-04-06 14:56:18
@morpheus:因为这个东西是异步的,所以不能抛出异常;您需要查看界面以查看是否有某种异步方式报告错误。确实,FileReaderonerror这个。当您使用file://URL 时,您的文档域是 ~null,这往往会使您与很多事情隔绝(我不知道细节,我从不这样做),尤其是。当 SOP 进入时。我很确定这取决于浏览器,至少现在是这样,因为规范的安全部分仍然是临时的:w3.org/TR/FileAPI/#security-discussion但我不希望它起作用。
2021-04-07 14:56:18