开发者问题收集

JavaScript - 文件到字节数组

2020-08-20
2236

我正在尝试让一个脚本运行,该脚本名为 sfo.js

该仓库仅提到了这种用法:

keys = parse_sfo(Some_ArrayBuffer);
console.log(keys['TITLE']);

查看 sfo.jsparse_sfo 具有 sfoBytes 参数。

由此我得出结论, sfoBytes 参数需要是文件字节的数组缓冲区。

我试图编写一个脚本,将 SFO 文件解析为字节数组:

<script src="sfo.js"></script>
<script>
function stringToArrayBuffer(str) {
    var buf = [];
    
    for (var i=0, strLen=str.length; i<strLen; i++) {
        buf[i] = str.charCodeAt(i);
    }
    
    console.log(buf);

    return buf;
}

function testing(url) {
    var request = new XMLHttpRequest();
    request.open('GET', url, false);
    request.send(null);

    if (request.status === 200) {
        console.log(request.response);
        
        var response = request.response;
        var array = stringToArrayBuffer(response);

        return array;
    } else {
        alert('Error!');
    }
 }

var data = testing('param.sfo');
var sfo = parse_sfo(data);
</script>

这会在控制台中引发错误:

Uncaught RangeError: byte length of Uint32Array should be a multiple of 4 at new Uint32Array (<anonymous>)
    at readUint32At (sfo.js:20)
    at parse_sfo (sfo.js:113)
    at (index):29

我很确定我做错了什么。有人知道我该如何让脚本正常工作吗?

我有一个 param.sfo 的示例文件: https://filebin.net/gghosrp6u93jn7y8 (如果不允许链接到下载,请告诉我)

3个回答

好的,终于有了一个可行的示例。

我在互联网上找到了一个小的 param.sfo 文件。

注意:

  • 文件阅读器有两个版本:本地和远程
  • 在下面的代码片段中,您可以测试两者(我已将 param.sfo 添加为外部链接以测试“远程”)。要测试“本地”,您只需从 PC 中选择任何 sfo 文件即可。
  • 结果,我显示了所有键,而不仅仅是“TITLE”(就像您的问题中那样)。然后您可以选择所需的键
function getSfoLocal(callback) {
  // for now I use local file for testing
  document.querySelector('input').addEventListener('change', function() {

    var reader = new FileReader();
    reader.onload = function() {

      var arrayBuffer = this.result;
      var array = new Uint8Array(arrayBuffer);
      // var binaryString = String.fromCharCode.apply(null, array);

      if (typeof callback === 'function') callback(array);
    }
    reader.readAsArrayBuffer(this.files[0]);

  }, false);
}

function getSfoRemote(url, callback) {
  var concatArrayBuffers = function(buffer1, buffer2) {

    if (!buffer1) {
      return buffer2;
    } else if (!buffer2) {
      return buffer1;
    }

    var tmp = new Uint8Array(buffer1.length + buffer2.length);
    tmp.set(buffer1, 0);
    tmp.set(buffer2, buffer1.byteLength);
    return tmp.buffer;
  };

  fetch(url).then(res => {
    const reader = res.body.getReader();
    let charsReceived = 0;
    let result = new Uint8Array;

    reader.read().then(function processText({
      done,
      value
    }) {
      // Result objects contain two properties:
      // done  - true if the stream has already given you all its data.
      // value - some data. Always undefined when done is true.
      if (done) {
        if (typeof callback === 'function') callback(result);
        return result;
      }

      // value for fetch streams is a Uint8Array
      charsReceived += value.length;
      const chunk = value;
      result = concatArrayBuffers(result, chunk);

      // Read some more, and call this function again
      return reader.read().then(processText);
    });
  });
}

function getSfo(type, url, callback) {
  if (type === 'local') getSfoLocal(callback);
  if (type === 'remote') getSfoRemote(url, callback);
}

getSfo('local', null, (data) => {
  keys = parse_sfo(data);
  console.log('LOCAL =', keys);
});

function goremote() {
  getSfo('remote', 'https://srv-file9.gofile.io/download/Y0gVfw/PARAM.SFO', (data) => {
    keys = parse_sfo(data);
    console.log('REMOTE =', keys);
  });
}
div { padding: 4px; }
<!--script src="https://rawcdn.githack.com/KuromeSan/sfo.js/c7aa8209785cc5a39c4231e683f6a2d1b1e91153/sfo.js" for="release"></script-->
<script src="https://raw.githack.com/KuromeSan/sfo.js/master/sfo.js" for="develop"></script>

<div>Local version: <input type="file" /></div>
<div>Remote version: <button onclick="goremote()">Go remote</button></div>

附言似乎我用于远程示例的 gofile.io 服务有时不可见。如果您有指向 param.sfo 的永久链接,请告诉我。但现在我必须 访问我的文件 ,然后它才会可见。

Anton
2020-08-25

您正在使用一些已有 20 年历史的 JavaScript。 请使用 fetch 添加 TextEncoder 来确保自己的理智。

fetch(url).then(res => res.json().then(data => {
  const encoder = new TextEncoder()
  const bytes = encoder.encode(data)
  parse_sfo(data)
})
Konrad
2020-08-20

sfoBytes 应该是 sfo 文件的实际数组缓冲区。它比您尝试的更简单,您不需要使用 stringToArrayBuffer 转换请求响应,因为您可以从 XMLHttpRequest 获取数组缓冲区。此外,SFO 文件不是文本文件,它是二进制文件,因此从字符串转换无论如何都行不通。

更改您的请求以获取 arraybuffer 响应类型应该这样做:

function testing(url) {
    var request = new XMLHttpRequest();
    request.open('GET', url, true);
    request.responseType = "arraybuffer";
    request.send(null);

    if (request.status === 200) {
        console.log(request.response);
        
        var response = request.response;
        var sfo = parse_sfo(response);
    } else {
        alert('Error!');
    }
 }
Julien Grégoire
2020-08-23