如何使用 FileReader 读取二进制文件以便在 CryptoJS 中使用 SHA-256 对其进行散列?

人气:267 发布:2022-10-16 标签: javascript utf-8 character-encoding cryptojs latin1

问题描述

如何使用 JavaScript 将 UTF-8 字符串转换为 Latin1 编码的字符串?

how do I convert a UTF-8 string to Latin1 encoded string using javascript?

这是我想要做的:

我得到一个文件,通过读取数组缓冲区将其分成块然后,我将数组缓冲区解析为字符串

并使用以下代码将其传递给cryptoJS进行哈希计算: I get a file, split that in chunks by reading as arraybuffer then, I parse the arraybuffer as string

and passing it to cryptoJS for hash computation using following code:

cryptosha256 = CryptoJS.algo.SHA256.create();
cryptosha256.update(text);
hash = cryptosha256.finalize();

对于文本文件,这一切都很好.使用代码散列非文本文件(图像/.wmv 文件)时出现问题.我在另一个博客中看到,CryptoJS 作者要求使用 Latin1 格式而不是 UTF-8 发送字节,这就是我被卡住的地方.

It all works well for a text file. I get problems when using the code for hashing a non-text files (image/.wmv files). I saw in another blog and there the CryptoJS author requires the bytes to be sent using Latin1 format instead of UTF-8 and that's where I am stuck.

不确定,如何在javascript中从arraybuffer使用Latin1格式生成字节(或字符串)?

Not sure, how can I generate the bytes (or strings) using Latin1 format from arraybuffer in javascript?

$('#btnHash').click(function () {
    var fr = new FileReader(), 
        file = document.getElementById("fileName").files[0];
    fr.onload = function (e) {
        calcHash(e.target.result, file);
    };
    fr.readAsArrayBuffer(file);
});
function calcHash(dataArray, file) {
    cryptosha256 = CryptoJS.algo.SHA256.create();
    text = CryptoJS.enc.Latin1.parse(dataArray);
    cryptosha256.update(text);
    hash = cryptosha256.finalize();
}

推荐答案

CryptoJS 不了解 ArrayBuffer 是什么,如果您使用某些文本编码,例如 Latin1 或 UTF-8,您将不可避免地丢失一些字节.并非所有可能的字节值都在这些文本编码之一中具有有效编码.

CryptoJS doesn't understand what an ArrayBuffer is and if you use some text encoding like Latin1 or UTF-8, you will inevitably lose some bytes. Not every possible byte value has a valid encoding in one of those text encodings.

您必须将 ArrayBuffer 转换为 CryptoJS 的内部 WordArray,它将字节保存为一个字数组(32 位整数).我们可以将 ArrayBuffer 视为一个无符号 8 位整数数组,并将它们放在一起构建 WordArray(参见 arrayBufferToWordArray).

You will have to convert the ArrayBuffer to CryptoJS' internal WordArray which holds the bytes as an array of words (32 bit integers). We can view the ArrayBuffer as an array of unsigned 8 bit integers and put them together to build the WordArray (see arrayBufferToWordArray).

以下代码显示了一个完整示例:

The following code shows a full example:

function arrayBufferToWordArray(ab) {
  var i8a = new Uint8Array(ab);
  var a = [];
  for (var i = 0; i < i8a.length; i += 4) {
    a.push(i8a[i] << 24 | i8a[i + 1] << 16 | i8a[i + 2] << 8 | i8a[i + 3]);
  }
  return CryptoJS.lib.WordArray.create(a, i8a.length);
}

function handleFileSelect(evt) {
  var files = evt.target.files; // FileList object

  // Loop through the FileList and render image files as thumbnails.
  for (var i = 0, f; f = files[i]; i++) {
    var reader = new FileReader();

    // Closure to capture the file information.
    reader.onloadend = (function(theFile) {
      return function(e) {
        var arrayBuffer = e.target.result;

        var hash = CryptoJS.SHA256(arrayBufferToWordArray(arrayBuffer));
        var elem = document.getElementById("hashValue");
        elem.value = hash;
      };

    })(f);
    reader.onerror = function(e) {
      console.error(e);
    };

    // Read in the image file as a data URL.
    reader.readAsArrayBuffer(f);
  }
}

document.getElementById('upload').addEventListener('change', handleFileSelect, false);

<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/sha256.js"></script>
<form method="post" enctype="multipart/form-data">
  Select image to upload:
  <input type="file" name="upload" id="upload">
  <input type="text" name="hashValue" id="hashValue">
</form>

您可以使用我的其他答案中的技术扩展此代码,以便对任意大小的文件进行哈希处理而不会冻结浏览器.

You can extend this code with the techniques in my other answer in order to hash files of arbitrary size without freezing the browser.

921