Laravel 8: Excel (xlsx) reader

dakata__92

Super Moderator
Колеги, каква библиотека ползвате за прочитане на съдържанието от Екселски файл?

Трябва ми нещо надеждно, което да замени настоящо използвани Linux библиотеки от мен (xlsx2csv и xls2csv). В момента така се експортва съдържанието:
Код:
$fileString = shell_exec(' xls2csv -x "' . $path . $file['file'] . '"');
Код:
shell_exec('xlsx2csv ' . $path . $file['file'] . ' '.$tempDir.' --delimiter="' . $delimiterCSV . '" -i -e');

Търся надеждна библиотека, която не се налага да инсталирам на сървърите където приложението се изпълнява, а да си върви с инсталацията на проекта.
 
Off topic от интерес: Какво мислиш за контейнерите (например Docker)? С тях изчезва проблемът с "налага да инсталирам на сървърите", цялото приложение върви с всичко необходимо.
 
djman каза:
Off topic от интерес: Какво мислиш за контейнерите (например Docker)? С тях изчезва проблемът с "налага да инсталирам на сървърите", цялото приложение върви с всичко необходимо.

Проблеми с контейнери нямам. Системата, която поддържам е особена и Docker не е имплементиран, но честно казано не е и необходимо според мен да ползвам тези библиотеки, ако има добра PHP библиотека занимаваща се с това. Тези дни ще тествам тази:
https://packagist.org/packages/phpoffice/phpspreadsheet
Ако имате мнение за нея, моля споделете го.
 
uphero каза:
Най-доброто, ползвам я на много места
Свалих си тази библиотека и я тествам.
https://packagist.org/packages/phpoffice/phpspreadsheet
Как се справяте с четенето на големи файлове?

Код:
switch ($extension) {
            case 'xls':
            case 'xlsx':
                try {
                    $reader = IOFactory::createReaderForFile($documentPath);
                    $reader->setReadDataOnly(true);
                    $spreadsheet = $reader->load($documentPath);
                    $sheets = $spreadsheet->getAllSheets();
                    foreach ($sheets as $worksheet) {
                        $documentRows[] = $worksheet->toArray();
                    }
                } catch (\Throwable $e) {
                    $documentRows = [];
                }
                break;
        }
 
Учудвам се, че библиотеката не предлага ни итератори, ни генератори, ни стриймове...

Колко големи файлове имаш? Гигабайти?
 
Revelation каза:
Учудвам се, че библиотеката не предлага ни итератори, ни генератори, ни стриймове...

Колко големи файлове имаш? Гигабайти?

Искам да извъртам поне 100 000 реда от sheet. Това значи, че доста ще се натовари системата. Гледам, че има някакви chunk филтри, но не сработват, когато се налага да чета от няколко sheet-а. Тъпото от цялата работа е, че дори нямам филтър за skip на празни редове. Реално ако Имам на ред 1 някакъв текст по колоните, то ако сложа чак на ред 5 някакъв текст, реално $worksheet->toArray(); ще ми върне масив от ред 1 до 5, като 2, 3 и 4 ще са празни... Като взема масива $documentRows за вторична валидация ще итерирам не два реда с резултат а 5.
 
dakata__92 каза:
Revelation каза:
Учудвам се, че библиотеката не предлага ни итератори, ни генератори, ни стриймове...

Колко големи файлове имаш? Гигабайти?

Искам да извъртам поне 100 000 реда от sheet. Това значи, че доста ще се натовари системата. Гледам, че има някакви chunk филтри, но не сработват, когато се налага да чета от няколко sheet-а. Тъпото от цялата работа е, че дори нямам филтър за skip на празни редове. Реално ако Имам на ред 1 някакъв текст по колоните, то ако сложа чак на ред 5 някакъв текст, реално $worksheet->toArray(); ще ми върне масив от ред 1 до 5, като 2, 3 и 4 ще са празни... Като взема масива $documentRows за вторична валидация ще итерирам не два реда с резултат а 5.

Има си причина да е така, ако не върне празен резултат ти как ще знаеш че следващия текст е 5 ред а не :D
 
dakata__92 каза:
Revelation каза:
Учудвам се, че библиотеката не предлага ни итератори, ни генератори, ни стриймове...

Колко големи файлове имаш? Гигабайти?

Искам да извъртам поне 100 000 реда от sheet. Това значи, че доста ще се натовари системата. Гледам, че има някакви chunk филтри, но не сработват, когато се налага да чета от няколко sheet-а. Тъпото от цялата работа е, че дори нямам филтър за skip на празни редове. Реално ако Имам на ред 1 някакъв текст по колоните, то ако сложа чак на ред 5 някакъв текст, реално $worksheet->toArray(); ще ми върне масив от ред 1 до 5, като 2, 3 и 4 ще са празни... Като взема масива $documentRows за вторична валидация ще итерирам не два реда с резултат а 5.

Потърси библиотека, която може да работи със стриймове от данни. С тази ще имаш проблеми понеже се опитва да зарежда всичко в паметта.

Според мен тази https://opensource.box.com/spout/docs/ би ти свършила доста по-добра работа. И като гледам имаш опция дали да задържаш празни редове или не.
Не съм и чел кода, но според описанието изглежда, че са я написали като хората и не се опитват да ти заредят огромен файл в паметта.
 
Revelation каза:
dakata__92 каза:
Revelation каза:
Учудвам се, че библиотеката не предлага ни итератори, ни генератори, ни стриймове...

Колко големи файлове имаш? Гигабайти?

Искам да извъртам поне 100 000 реда от sheet. Това значи, че доста ще се натовари системата. Гледам, че има някакви chunk филтри, но не сработват, когато се налага да чета от няколко sheet-а. Тъпото от цялата работа е, че дори нямам филтър за skip на празни редове. Реално ако Имам на ред 1 някакъв текст по колоните, то ако сложа чак на ред 5 някакъв текст, реално $worksheet->toArray(); ще ми върне масив от ред 1 до 5, като 2, 3 и 4 ще са празни... Като взема масива $documentRows за вторична валидация ще итерирам не два реда с резултат а 5.

Потърси библиотека, която може да работи със стриймове от данни. С тази ще имаш проблеми понеже се опитва да зарежда всичко в паметта.

Според мен тази https://opensource.box.com/spout/docs/ би ти свършила доста по-добра работа. И като гледам имаш опция дали да задържаш празни редове или не.
Не съм и чел кода, но според описанието изглежда, че са я написали като хората и не се опитват да ти заредят огромен файл в паметта.
Принципно, накрая се двоумех между тази, която ти предлагаш и https://packagist.org/packages/phpoffice/phpspreadsheet. Избрах втората, защото е наследник на PHPExcel, която не бе толкова зле. Реално цялата обработка на файловете съм я изнесъл на schedule задача и сметнах, че ще е добре да използвам масов продукт с добра поддръжка. Обработката при мен става офлайн, така че за момента файлове от рода на 200 000 реда се зареждат в пълна готовност за вторична обработка, за 14 секунди. Реално мисля да забраня файлове над 3 MB, но тук още не съм се интересувал горе долу на колко реда от шаблона на системата се равнява това (само ще предположа че са около 500 000 итерации).

Понеже няма филтър за пропускане на празни редове, си създадох мой и ще го споделя за колегите, които мислят, че ще им е полезно.

Код:
switch ($extension) {
	case 'xls':
	case 'xlsx':
		try {
			$reader = IOFactory::createReaderForFile($documentPath);
			$reader->setReadDataOnly(true);
			$spreadsheet = $reader->load($documentPath);
			$sheets = $spreadsheet->getAllSheets();
			foreach ($sheets as $sheet) {
				$sheetRows = self::removeEmptyRowsFromTwoDimensionArray($sheet->toArray());
				$documentRows[] = $sheetRows;
			}
		} catch (\Throwable $e) {
			$documentRows = [];
		}
		break;
}

public static function removeEmptyRowsFromTwoDimensionArray(array $data): array
{
	return array_filter($data, function($value) {
		if (is_array($value) && empty(array_diff($value, [null]))) {
			return false;
		}
		return true;
	});
}

Иска ми се да намаля времето за зареждане на файла, за да не тормозя сървърите излишно. Принципно сме на собствени машини и няма проблем, но пък ако има вариант да спестя ресурси от тези операции, ще е добре.
 
Дака, наистина няма как да минеш без стриминг или нещо от сорта за големи файлове. Право казва Revelation...
 
Момчета, не ме отчайвайте. Тази библиотека (phpoffice/phpspreadsheet), която съм избрал, няма ли възможност да чете файла на порции, без да го зарежда накуп? В частност видях, че има възможност да се използват филтри, но никъде няма пример за комбиниране на филтри и sheet-ове. Така или иначе файловете, които ще чете библиотеката, реално са вече качени на машината и започва да ги обработва и валидира.
 
Като цяло не разбрах какъв е проблема с библиотеката, която предложих.

Не казвам, че трябва да я ползваш, но ако я тестваш и ти върши работата, която трябва да ти свърши, не виждам защо трябва да се мъчиш с тази, с която не можеш да намериш решение на проблемите си.

Не винаги броя сваляния на дадена библиотека означава, че тя е най-добрата, особено когато не върши това, което ти искаш.

Като цяло в момента очакваш някой да ти даде решение на твоя проблем, който вероятно можеш да решиш по различен начин, с по-малко мъки.

Ти гледай, че не съм тръгнал да ти давам решения на Go. :D
 
Revelation каза:
Като цяло не разбрах какъв е проблема с библиотеката, която предложих.

Не казвам, че трябва да я ползваш, но ако я тестваш и ти върши работата, която трябва да ти свърши, не виждам защо трябва да се мъчиш с тази, с която не можеш да намериш решение на проблемите си.

Не винаги броя сваляния на дадена библиотека означава, че тя е най-добрата, особено когато не върши това, което ти искаш.

Като цяло в момента очакваш някой да ти даде решение на твоя проблем, който вероятно можеш да решиш по различен начин, с по-малко мъки.

Ти гледай, че не съм тръгнал да ти давам решения на Go. :D
Не ме плаши с GO, че съм си качил Swoole. 😁

Аз питам за оптимизация. Тук има хора, които са ползвали библиотеката много преди мен. Реално погледнато, дори да я оставя в сегашният и вид, няма да бутна системата, а тази библиотека ми дава и други благинки, които ще използвам, за това не се отказвам от нея.
 
Някой опитвал ли се е да кешира съдържанието с вградената кешираща система на Laravel? Открих един опит в нета, но нещо май не е сполучил.

I am not sure this is the best way to acquire a Simple Cache interface from Laravel, but PhpSpreadsheet seemed to be interacting with our Redis cache. However, 197K round trips over the network was going to take far too long. I gave up after waiting 5 minutes.

Код:
$cache = new Cache();
$cacheStore = $cache::getStore();
$cacheInterface = new Repository($cacheStore);
Settings::setCache($cacheInterface);

$spreadsheet = new Spreadsheet();

Това казва документацията на библиотеката:
https://phpspreadsheet.readthedocs.io/en/latest/topics/memory_saving/
 
Аз ей това ползвам, ама и то е базирано на spout.

https://github.com/rap2hpoutre/fast-excel
 
Честно казано съм адски доволен от тази библиотека в момента. Установихме че на реална машина от 168 секунди обработка времето на по-голям файл се сведе до близо 30 секунди. С по-добро желязо се реши част от проблема.
 

Горе