使用Papa Parse处理大型CSV文件

9

我正在尝试加载一份大约有10万行的文件,但目前浏览器已经崩溃(本地加载)。我在互联网上搜索了一下,发现Papa Parse似乎可以处理大型文件。现在它只需要3-4分钟就可以加载到文本区域中。一旦文件被加载,我想通过一些jQuery来进行计数和其他操作,这个过程需要一些时间。是否有办法让CSV加载更快? 我是不是正确地使用了这个程序?

<div id="tabs">
<ul>
  <li><a href="#tabs-4">Generate a Report</a></li>
</ul>
<div id="tabs-4">
  <h2>Generating a CSV report</h2>
  <h4>Input Data:</h4>      
  <input id="myFile" type="file" name="files" value="Load File" />
  <button onclick="loadFileAsText()">Load Selected File</button>
  <form action="./" method="post">
  <textarea id="input3" style="height:150px;"></textarea>

  <input id="run3" type="button" value="Run" />
  <input id="runSplit" type="button" value="Run Split" />
  <input id="downloadLink" type="button" value="Download" />
  </form>
</div>
</div>

$(function () {
    $("#tabs").tabs();
});

var data = $('#input3').val();

function handleFileSelect(evt) {
    var file = evt.target.files[0];

Papa.parse(file, {
    header: true,
    dynamicTyping: true,
    complete: function (results) {
        data = results;
    }
});
}

$(document).ready(function () {

    $('#myFile').change(function(handleFileSelect){

    });
});


function loadFileAsText() {
    var fileToLoad = document.getElementById("myFile").files[0];

    var fileReader = new FileReader();
    fileReader.onload = function (fileLoadedEvent) {
        var textFromFileLoaded = fileLoadedEvent.target.result;
        document.getElementById("input3").value = textFromFileLoaded;
    };
    fileReader.readAsText(fileToLoad, "UTF-8");
}

为了更快的加载,请关闭头部和动态类型;并确保使用流式传输!现在你只是将所有数据加载到内存中,所以你很幸运它没有崩溃。 - Matt
2个回答

12

你可能已经正确使用了它,只是程序需要一些时间来解析所有 100k 行!

这很可能是 Web Workers 的一个很好的用例场景

注意:根据下面@tomBryer 的回答,Papa Parse 现在已经支持 Web Workers,这可能比自己编写工作线程更好。

如果您以前没有使用过 Web Workers,请查看此站点,其中提供了一个不错的概述,但关键部分是:

  

Web Workers 模拟多线程,允许在后台运行密集脚本,因此不会阻止其他脚本运行。非常适合同时保持 UI 响应性能并执行处理器密集型函数。

浏览器覆盖也相当不错,除了 IE10 以下的半现代浏览器不支持外。

Mozilla 有一个很好的视频,展示了 web workers 如何在页面上提高帧速率

我会试着为您提供一个可以使用 Web Workers 的工作示例,但请注意,这并不会加速脚本处理速度,只会使其异步处理,以便您的页面保持响应。

编辑:

(注意: 如果您想在 Worker 内部解析 CSV,则可能需要使用importScript函数在 worker.js 中导入 Papa Parser 脚本(该函数在工作线程内全局定义)。有关此信息,请参见MDN 页面)。

这是我的工作示例:

csv.html

<!doctype html>
<html>
<head>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/4.1.2/papaparse.js"></script>
</head>

<body>
  <input id="myFile" type="file" name="files" value="Load File" />
  <br>
  <button class="load-file">Load and Parse Selected CSV File</button>
  <div id="report"></div>

<script>
// initialize our parsed_csv to be used wherever we want
var parsed_csv;
var start_time, end_time;

// document.ready
$(function() {

  $('.load-file').on('click', function(e) {
    start_time = performance.now();
    $('#report').text('Processing...');

    console.log('initialize worker');

    var worker = new Worker('worker.js');
    worker.addEventListener('message', function(ev) {
      console.log('received raw CSV, now parsing...');

      // Parse our CSV raw text
      Papa.parse(ev.data, {
        header: true,
        dynamicTyping: true,
        complete: function (results) {
            // Save result in a globally accessible var
          parsed_csv = results;
          console.log('parsed CSV!');
          console.log(parsed_csv);

          $('#report').text(parsed_csv.data.length + ' rows processed');
          end_time = performance.now();
          console.log('Took ' + (end_time - start_time) + " milliseconds to load and process the CSV file.")
        }
      });

      // Terminate our worker
      worker.terminate();
    }, false);

    // Submit our file to load
    var file_to_load = document.getElementById("myFile").files[0];

    console.log('call our worker');
    worker.postMessage({file: file_to_load});
  });

});
</script>
</body>

</html>

worker.js

self.addEventListener('message', function(e) {
    console.log('worker is running');

    var file = e.data.file;
    var reader = new FileReader();

    reader.onload = function (fileLoadedEvent) {
        console.log('file loaded, posting back from worker');

        var textFromFileLoaded = fileLoadedEvent.target.result;

        // Post our text file back from the worker
        self.postMessage(textFromFileLoaded);
    };

    // Actually load the text file
    reader.readAsText(file, "UTF-8");
}, false);

处理过程的GIF,不到一秒就完成了(全部在本地运行)

演示工作的GIF


太好了,我期待着看到它。 - Keith
@Keith,我发了一个例子。我可以在不到一秒的时间内加载并处理文件(100,000行,5列,~2.4 MB文件)。如果你觉得通过循环数据进行的任何其他操作仍然需要时间,那么也可以通过worker来完成。这是在Chrome Canary v53中运行的。 - romellem
太棒了@romellem,感谢您为此付出了努力! - Necevil
这个确切的例子帮助了我,当与 Papa Parse 一起提供的工作程序给了我一个“window is not defined”的错误时,解决起来比它的价值更麻烦。因此编写一个自定义的 worker,并且我使用这个确切的例子作为指南 - 谢谢!然而,我不得不进行修改,因为我在 React 中使用 Papa Parse 和上面的 web worker 示例,遇到了各种各样的错误。如果你正在使用 React 编写 web worker,请看看这个教程:https://www.fullstackreact.com/articles/introduction-to-web-workers-with-react/ - RachieVee

7

从v5开始,PapaParse现在已经内置了WebWorkers

在Papaparse中调用worker的一个简单示例如下:

Papa.parse(bigFile, {
    worker: true,
    step: function(results) {
        console.log("Row:", results.data);
    }
});

如果你已经有自己的PP工作器,就不需要重新实现,但对于未来的项目,一些人可能会发现使用PapaParse的解决方案更容易。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接