Как избежать фатальной ошибки памяти PHP / WordPress для массовой обработки данных при больших загрузках файлов? - PullRequest
1 голос
/ 03 февраля 2020

У меня есть большой CSV-файл, который я загружаю на панель управления WordPress для импорта терминов таксономии. Я написал небольшой плагин, который использует функцию wp_insert_term() для вставки каждого термина, однако функция кэширует большую часть своих данных, чтобы проверить уникальность слагов и зависимости родительских терминов, процесс исчерпывает память около 1000 терминов, несмотря на увеличение выделение памяти до 0,5 Гб.

Я хотел разделить файл на управляемые куски, чтобы пакетно обрабатывать данные и запускать сеансы, ограниченные 1000 или строками данных, таким образом, каждый процесс завершился бы чисто.

Я искал такое решение и нашел интересную статью о похожей проблеме, с которой сталкивается массовый импорт изображений, и в ней рассказывается, как разработчики использовали javascript для управления пакетным процессом, отправив ajax запросы к серверу и управляемые куски.

Это дало мне идею читать CSV-файл при загрузке , читать его построчно и отправлять ajax запрос на сервер для обработки управляемого количества строк.

Есть ли лучший способ добиться этого?

1 Ответ

0 голосов
/ 05 февраля 2020

Я разработал следующее решение на основе ссылок в вопросе и некоторых дополнительных действий.

На стороне сервера 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, * 1008. *

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 последовательным образом в виде фрагментов строк, которые сервер может обработать без фатальной ошибки памяти.

...