使用 AJAX + Javascript 读取块中的文件

Vam*_*hi 3 javascript ajax jquery

所以,这是我的问题:我有一个很大的文本文件(大小约为 150 MB),有数十万行。我需要读取文件的内容,解析它,以便将这些行放入适当的 html 标签中并写入将其放入 window.document.open() 对象中。

我的代码适用于大小不超过 50 MB 的文件。

var rawFile=new XMLHttpRequest();
    rawFile.open("GET",file, true);
    rawFile.onreadystatechange= function () {
        if (rawFile.readyState === 4) {
            if (rawFile.status === 200 || rawFile.status === 0) {
                var allText = rawFile.responseText;
                var contents = allText.split("\n");
                var w = window.open();
                w.document.open();
                for (i = 0; i < contents.length; i++) {
                    //logc so that str= appropriate tags + contents[i]
                    w.document.write(str);
                }
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

该代码有效。逻辑是有效的。但如果文件大小大于 100MB 或类似大小,chrome 就会崩溃。我认为分块读取文件然后将其写入 window.document.open() 将为我解决这个问题。

非常感谢我如何实现这一目标的任何建议。谢谢 :)

(如果我上面发布的代码有任何错误,请忽略,我的实际代码非常大,所以我只写了它的微型版本)

小智 5

您的方法会削弱浏览器,因为您正在立即处理整个响应。更好的方法是分解流程,以便处理较小的块,或者通过流程流式传输文件。

使用Fetch API而不是XMLHttpRequest可以访问流数据。使用流的一大优点是在处理内容时不会占用浏览器的内存。

以下代码概述了如何使用流来执行任务:

var file_url = 'URL_TO_FILE';
// @link https://developer.mozilla.org/en-US/docs/Web/API/Request/Request
var myRequest = new Request( file_url );
// fetch returns a promise
fetch(myRequest)
  .then(function(response) {
    var contentLength = response.headers.get('Content-Length');
    // response.body is a readable stream
    // @link https://learn.microsoft.com/en-us/microsoft-edge/dev-guide/performance/streams-api
    var myReader = response.body.getReader();
    // the reader result will need to be decoded to text
    // @link https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder/TextDecoder 
    var decoder = new TextDecoder();
    // add decoded text to buffer for decoding
    var buffer = '';
    // you could use the number of bytes received to implement a progress indicator
    var received = 0;
    // read() returns a promise
    myReader.read().then(function processResult(result) {
      // the result object contains two properties:
      // done  - true if the stream is finished
      // value - the data
      if (result.done) {
        return;
      }
      // update the number of bytes received total
      received += result.value.length;
      // result.value is a Uint8Array so it will need to be decoded
      // buffer the decoded text before processing it
      buffer += decoder.decode(result.value, {stream: true});
      /* process the buffer string */

      // read the next piece of the stream and process the result
      return myReader.read().then(processResult);
    })
  })
Run Code Online (Sandbox Code Playgroud)

我没有实现处理缓冲区的代码,但算法如下:

If the buffer contains a newline character:
    Split the buffer into an array of lines
If there is still more data to read:
    Save the last array item because it may be an incomplete line
    Do this by setting the content of the buffer to that of the last array item
Process each line in the array
Run Code Online (Sandbox Code Playgroud)

快速查看Can I Use告诉我,这在 IE 中不起作用,因为 Fetch API 在 Edge 浏览器之前并未实现。然而,没有必要绝望,因为总会有一些人为不支持的浏览器实现了一个polyfill 。