Чтение большого файла CSV по 100 строк средствами сервера. Возможно ли?
Приветствую. Была задача загрузить данные из файла формата csv в БД. В файле два столбца, грубо говоря ключ и значение, и 270000 строк. Проблема была в том, что если читать в цикле построчно и сразу писать в БД сервер не вывозит, старшие товарищи в группе Телеграм сказали надо читать по 100 строк и потом записывать в базу. Я не придумал ничего лучше, чем дёргать скрипт чтения файла с фронта ajax'jv каждый раз меняя $offset. Вопрос, можно ли это же сделать без запросов с фронта, а внутри скрипта, но при этом чтобы инициализация происходила с фронта. Другими словами у меня так: уходит первый запрос, если в ответе есть $offset, уходит второй запрос с этим $offset и так пока не вернётся false. А хочется, чтобы ушёл запрос, скрипт отработал и я показал результат. Возможно ли такое в условиях ограничения времени выполнения скрипта и объемов памяти?
Мой вариант скрипта.
Мой вариант скрипта.
<?php
if ($snippet = $modx->getObject('modSnippet', array('name' => 'formit'))) {
$properties = $snippet->getProperties();
$property_set = !empty($set)
? $snippet->getPropertySet($set)
: array();
$scriptProperties = array_merge($properties, $property_set, $scriptProperties);
$snippet->_cacheable = false;
$snippet->_processed = false;
$response = $snippet->process($scriptProperties);
$response = json_decode($AjaxForm->handleFormIt($scriptProperties),1);
} else {
return $AjaxForm->error('Что-то пошло не так :-(', array());
}
if ($response['success'] && $_POST['filename']) {
$pathToFile = MODX_ASSETS_PATH . $_POST['filename'];
$data = false;
$count = 0;
$length = 20;
$row = 0;
$fields = array();
$offset = (int)$_POST['offset'] ?: 0;
$handle = fopen($pathToFile, "r");
if ($handle !== FALSE) {
if($offset*$length < filesize($pathToFile)){
fseek($handle, $offset*$length);
while ($row < 100) {
$fields = fgetcsv($handle, 20, ";");
$personal_account = (string)$fields[0];
$kvc_number = $fields[1];
if($personal_account && $kvc_number){
$sql = "INSERT INTO gazx_personal_accounts (personal_account, kvc_number) values('$personal_account', $kvc_number) ON DUPLICATE KEY UPDATE personal_account = '$personal_account'";
$c = $modx->exec($sql);
if($c){ $count += $c; }
}
$row++;
}
$offset = $offset + 100;
}else{
$offset = false;
}
fclose($handle);
$data = array('count' => $count, 'offset' => $offset, 'filename' => $_POST['filename']);
return $AjaxForm->success($scriptProperties['successMessage'], $data);
}else{
return $AjaxForm->error('Проблемы с файлом', array());
}
}else{
return $AjaxForm->error($scriptProperties['validationErrorMessage'], $response['data']);
}
Поскольку я слал запросы через ajaxform, то есть куски с валидацией, но думаю основная суть ясна. Надеюсь услышать мнения более опытных коллег, а ещё лучше варианты решения.
Поблагодарить автора
Отправить деньги
Комментарии: 3
Можно вот так:
<?php
$mysqli = new mysqli("localhost","my_user","my_password","my_db");
$csv = '/path/to/file.csv';
$table_name = 'table_name';
$sql = "LOAD DATA LOCAL INFILE '{$csv}'
REPLACE INTO TABLE {$table_name} FIELDS TERMINATED BY ','
ENCLOSED BY '\"' LINES TERMINATED BY '\r\n' IGNORE 1 LINES";
$result = $mysqli->query($sql);
Интересный вариант)))
Сам пользуюсь приемом ajax запросов. Использовать ajaxForm — слишком ограничений много, можно проще:
1. создаем страничку, там кнопку старт
2. js, ну для быстроты сборки конечно jquery
3. Пишем запрос по нажатию кнопки ".startProcessing"
4. создаем request.php в нем обрабатываем, что посылает ajax. Типа того(код не 100% рабочий)
«Выполнено 7847 из 270000»
1. создаем страничку, там кнопку старт
2. js, ну для быстроты сборки конечно jquery
3. Пишем запрос по нажатию кнопки ".startProcessing"
const urlRequests = '/ajaxQuester/request.php';
function requestToHandler(data = {}, btn = '') {
$.ajax({
dataType: 'json',
type: 'POST',
url: urlRequests,
data: {
action: 'Processing',
data: data,
},
success: function( r) {
console.log( r);
if(r.success){
if(r.data.done){
//Здесь делаем что-то по завершению
removeLoader(btn,1);
}else {
//если обработка не завершена вызываем заново
// так же можно выводить на страницу информацию(лог), что сделал какой этап и offset
requestToHandler(r.data,btn);
}
}else{
removeLoader(btn);
console.log('err');
}
}
});
}
$(document).ready(function() {
$(document).on('click', '.startProcessing', function (e) {
requestToHandler({'offset' : 0,'firstQuery' : 1}, $(this));
addLoader($(this),'',1)
e.preventDefault();
})
});
4. создаем request.php в нем обрабатываем, что посылает ajax. Типа того(код не 100% рабочий)
// тут всякие проверки и подключение к modx api
.....
switch ($_POST['action']){
/*
Processing
*/
case 'Processing':
if(empty($_POST['data']) || !isset($_POST['data']['offset'])){
echo returnError('no Processing', $resp);return;
}
$resp['data'] = $_POST['data'];
$data = &$resp['data'];
$offset = $data['offset'];
$arrayObjects = [];
$arrayObjects = $testArr;
if($data['firstQuery']){
$data['totalObjects'] = count($arrayObjects);
$data['firstQuery'] = 0;
}
$done = false;
$steps = 35;
if($data['totalObjects'] < $steps + $offset){
$steps = $data['totalObjects'] - $offset;
$done = true;
}
//здесь цикл обработки
$data['done'] = $done;
$data['offset'] += $steps;
if($done){
$resp['text'] .= '<div class="text-success ">Успешно завершено</div>';
}else{
$resp['text'] .= '<div class="text-secondary ">Выполнено '.$data['offset']. 'из '.$data['totalObjects'].'</div>';
}
echo json_encode($resp);return;
break;
}
Преимущество перед ajaxForm в том, что как угодно можно построить лог на самой странице например«Выполнено 7847 из 270000»
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.