如何在大文件上传中批量处理数据时避免 PHP/WordPress 内存致命错误?

How to avoid PHP/WordPress memory fatal error for bulk processing of data in large file uploads?

我有一个很大的 CSV 文件,我正在将其上传到 WordPress 仪表板以导入分类术语。我写了一个小插件,它使用 wp_insert_term() 函数来插入每个术语,但是,该函数缓存了大量数据以检查 slugs 的唯一性和父术语依赖性,过程 运行s内存大约 1000 个术语,尽管将内存分配增加到 0.5 Gb。

我一直想将文件拆分成可管理的块,以便批处理数据和 运行 会话限制在 1000 或行数据,这样每个进程都会干净地终止。

我一直在寻找这样的解决方案,发现这个有趣的 article 关于批量图像导入面临的类似问题,它概述了开发人员如何使用 javascript 来控制批处理通过向服务器和可管理的块发送 ajax 请求。

它给了我 reading the CSV file on upload 的想法,逐行阅读并向服务器发送 ajax 请求以处理可管理的行数。

有更好的方法吗?

我根据问题中的链接和一些额外的修改开发了以下解决方案。

在 WordPress 服务器端,加载 javascript 文件时,我根据使用的内存分配确定服务器可以处理的行数,

$limit = ini_get('memory_limit');
$limit = wp_convert_hr_to_bytes($limit) / MB_IN_BYTES; //in MBs.
switch(true){
    case $limit >= 512:
        $limit = 1000;
        break;
    default:
        $limit = 500;
        break;
}
wp_enqueue_script( 'my-javascript-file');
wp_localize_script( 'my-javascript-file', 'cirData', array(
    'limit'=>$limit
));

您应该根据自己的流程确定并设置自己的限制。

在javascript文件中,使用jQuery,

var reader,formData, lineMarker=0, csvLines, isEOF=false, $file, $form ;
  $(document).ready(function(){
    $file = $(':file'); //file input field
    $form = $('form');  //form
    //when the file field changes....
    $file.on('change', function(){
      //check if the file field has a value.
      if($file.val()){
        //setup file reader.
        reader = new FileReader();
        //now listen for when the file is ready to be read.
        reader.addEventListener('load', function (e) {
          csvLines = e.target.result.split("\n");
          batchProcess(); //launch process.
        });
        //when the form is being submitted, start reading the file.
        $(document).on('click', ':submit', function(e){
          e.preventDefault(); //disable normal submit.
          //setup data for the ajax.
          formData = new FormData($form.get(0));

          //read the file and batch request to server.
          reader.readAsBinaryString($file.get(0).files[0]);
        })
      }
    })
  });

  // Methods
  //posting
  function postCSVdata(csvdata){
    formData.set('csvlines', csvdata); //set the current datat to send.
    $.ajax({
      type: 'POST',
      url: $form.attr('action'),
      data: formData,
      contentType: false,
      processData: false,
      cache: false,
      success: function(data){
        var msg ="";
        if(isEOF){ //is this end of the file?
          console.log("success!");
        }else{ //continue reading file.
          console.log("uploaded:"+ Math.round(lineMarker/csvLines.length*100)+"%");
          batchProcess(); //process the next part of the file.
        }
      }
    })
  }
  //batch process.
  function batchProcess(){
    //csvlines is the array containing all the lines read from the file.
    //lineMarker is the index of the last line read.
    var parsedata='', stop = csvLines.length - lineMarker, line='';

    for(var i = 0; i < stop; i++) {
      line = csvLines[i+lineMarker];
      parsedata +=line+"\n"; //add a new line char for server to process.
      //check if max limit of lines server can process is reached.
      if(i>(cirData.limit-2)) break; //batch limit.
    }
    lineMarker += i;
    if(i==stop) isEOF = true;
    postCSVdata(parsedata); //send to server.
  }

这会以顺序方式发送多个 AJAX 请求,服务器能够在没有致命内存错误的情况下处理这些行块。