Phpexcel не хватает памяти
Мой предел памяти PHP 128 МБ быстро исчерпывается, даже когда я пытаюсь открыть только небольшой файл Excel размером ~ 350 КБ с помощью PHPExcel.
Узнайте, что в PHP Excel использует так много памяти, а затем исправьте это . - SoapBox
@SoapBox: Короче говоря, PHPExcel - это настоящий пожиратель памяти :( Кроме того, это своего рода сложная библиотека, поэтому найти (не говоря уже об исправлении) ошибки довольно нетривиально. В худшем случае может быть проще обойти проблему и найти альтернативную библиотеку. Piskvor left the building
Чтобы получить сертификат Microsoft, нужно только знать, как перезагрузить компьютер. Это освободит память, используемую «дырявым» программным обеспечением. - Chuck Burgess
@SoapBox - если бы кто-нибудь мог помочь мне найти способ снизить требования PHPExcel (не замедляя его до невозможности использования), я бы с радостью его реализовал - Mark Baker
8 Answers 8
There's plenty been written about the memory usage of PHPExcel on the PHPExcel forum; so reading through some of those previous discussions might give you a few ideas. PHPExcel holds an "in memory" representation of a spreadsheet, and is susceptible to PHP memory limitations.
The physical size of the file is largely irrelevant. it's much more important to know how many cells (rows*columns on each worksheet) it contains.
The "rule of thumb" that I've always used is an average of about 1k/cell, so a 5M cell workbook is going to require 5GB of memory. However, there are a number of ways that you can reduce that requirement. These can be combined, depending on exactly what information you need to access within your workbook, and what you want to do with it.
If you have multiple worksheets, but don't need to load all of them, then you can limit the worksheets that the Reader will load using the setLoadSheetsOnly() method. To load a single named worksheet:
Or you can specify several worksheets with one call to setLoadSheetsOnly() by passing an array of names:
If you only need to access part of a worksheet, then you can define a Read Filter to identify just which cells you actually want to load:
Using read filters, you can also read a workbook in "chunks", so that only a single chunk is memory-resident at any one time:
If you don't need to load formatting information, but only the worksheet data, then the setReadDataOnly() method will tell the reader only to load cell values, ignoring any cell formatting:
Use cell caching. This is a method for reducing the PHP memory that is required for each cell, but at a cost in speed. It works by storing the cell objects in a compressed format, or outside of PHP's memory (eg. disk, APC, memcache). but the more memory you save, the slower your scripts will execute. You can, however, reduce the memory required by each cell to about 300bytes, so the hypothetical 5M cells would require about 1.4GB of PHP memory.
Cell caching is described in section 4.2.1 of the Developer Documentation
EDIT
Looking at your code, you're using the iterators, which aren't particularly efficient, and building up an array of cell data. You might want to look at the toArray() method, which is already built into PHPExcel, and does this for you. Also take a look at this recent discussion on SO about the new variant method rangeToArray() to build an associative array of row data.
мой 128m PHP memory limit быстро исчерпывается, даже когда я пытаюсь открыть небольшой файл excel ~350 KB с PHPExcel.
хотя я могу увеличить лимит памяти в конфигурации, но будет здорово увидеть, есть ли какие-либо альтернативы для исправления этот.
размер файла не является хорошей мерой для файлов книги при работе с PHPExcel. Количество строк и столбцов (т. е. ячеек) является более важным.
сам код PHPExcel имеет размер от 10 до 25 МБ, в зависимости от того, к каким компонентам осуществляется доступ.
в настоящее время каждая ячейка в книге занимает в среднем 1k памяти (без кэширования) или 1.6 k на 64-битном PHP - я буду считать 32-битный PHP на данный момент-так (например) рабочий лист 8000 строк с 31 столбец (248,000 ячеек) будет около 242 МБ. При кэшировании ячеек (например, php://temp или DiskISAM) это может быть уменьшено примерно до трети, поэтому для 8000 строк по 31 столбцу потребуется около 80 МБ.
есть несколько вариантов, которые помогут вам уменьшить использование памяти:
вы используете кэширование ячеек с помощью PHPExcel?
Если вам нужно только получить доступ к данных в таблицах, и не нужен доступ к ячейки, то вы можете отключить чтение информации о форматировании из книги:
Если вам нужно получить доступ только к некоторым, но не ко всем листам в книге, вы можете загрузить только эти листы:
если вы хотите читать только определенные ячейки в листах, вы можете добавить фильтр:
все эти методы могут значительно уменьшить требования к памяти.
PHPExcel известен утечками памяти. Я советую вам использовать следующее, что нужно часть памяти, которую использует PHPExcel.:
только потому, что файл данных только X байт, не означает, что он использует X байт ОЗУ. Например, только 4K данных в массиве $_SESSION использует 64K ОЗУ при загрузке. Все зависит от того, что код делает с этими данными. Правильный ответ-увеличить объем ОЗУ.
также, если это XLSX-файл, они являются ZIP-документами XML. Текстовые файлы zip вверх гораздо плотнее, чем 1/2, так что ваш 350K XLSX файл легко 1 МБ файл Excel.
Xdebug является профилировщиком / отладчиком для php и может помочь вам отслеживать использование памяти и функциональные вызовы, чтобы выяснить, где проблема. И его легко установить, большинство дистрибутивов linux имеют его в репозитории, "yum install xdebug", "apt-get install xdebug".
одна вещь, которую вы должны отметить, тоже пустые ячейки.
У меня была такая же проблема, только 800 строк данных и скрипт даже не смог прочитать. Как тайм-ауты, так и ошибки из памяти, когда либо был исправлен.
Что-то @Mark Baker сказал о клетках заставил меня задуматься. Я заметил, что у меня было много пустых ячеек, и копирование только ячеек с данными в новую книгу заставило ее работать за секунду.
надеюсь, это кому-то поможет.
Если вы хотите узнать, вы можете использовать xhprof. Согласно этой ссылке, вы можете отслеживать использование памяти с ним.
8 ответы
Размер файла не является хорошим показателем для файлов книги при работе с PHPExcel. Количество строк и столбцов (т.е. ячеек) более важно.
Сам код PHPExcel занимает от 10 до 25 МБ, в зависимости от того, к каким компонентам осуществляется доступ.
В настоящее время каждая ячейка в книге занимает в среднем 1 КБ памяти (без какого-либо кеширования) или 1.6 КБ на 64-битном PHP - на данный момент я предполагаю 32-битный PHP - так (например) рабочий лист из 8000 строк с 31 столбцом (248,000 242 ячеек) будет около 8000 МБ. С помощью кеширования ячеек (например, php: // temp или DiskISAM) его можно уменьшить примерно до трети, поэтому для 31 строк по 80 столбцу потребуется около XNUMX МБ.
Есть несколько вариантов, которые помогут вам уменьшить использование памяти:
Вы используете кеширование ячеек с PHPExcel?
Если вам нужен только доступ к данным на ваших листах и вам не нужен доступ к форматированию ячеек, вы можете отключить чтение информации о форматировании из книги:
Если вам нужно получить доступ только к некоторым, но не ко всем листам в книге, вы можете загрузить только эти листы:
если вы хотите читать только определенные ячейки на листах, вы можете добавить фильтр:
PHPExcel — отличная библиотека с огромным функционалом по работе с форматами xls, xlsx. Можно считывать, записывать, менять форматирование, задавать формулы, а из xlsx можно и картинки вытаскивать.
На хабре уже был пост про эту библиотеку - Универсальное чтение ячеек в PHPExcel. Я остановлюсь только лишь на главном минусе PHPExcel — вечно памяти не хватает, все время сыпятся ошибки «Fatal error: Out of memory». Этот пост о том, как это обойти.
Чтение файла
Для чтения большого файла (~25 000 строк) я использовал комплексное решение.
Во-первых, считываем файл не целиком, а по несколько строк. Это делается так:
import_xls.php
Собственно класс chunkReadFilter — это то, что нам нужно. Устанавливаем его в качестве фильтра для чтения файла, и файл будет загружаться не целиком, а лишь определенное количество строк.
Во-вторых, кроме него еще используем такую полезную опцию как ReadDataOnly. Как следует из названия, она позволяет не загружать форматирование документа, высвобождая место для данных.
И, в-третьих, конечно же используем unset(). Это так же поможет высвободить память.
Но помимо нехватки памяти возникает другая проблема. На большинстве shared-хостингов у php-скриптов помимо ограничения на использование памяти, еще стоит ограничение на время выполнения. И крайне вероятно, что этого времени хватать не будет. Лично я обошел эту проблему при помощи сессий и повторяющихся ajax запросов. В коде представленном выше вы уже увидели использование сессий и завершающее «The End».
А вот код клиентской части.
import_xls.html
import-xls.js
Т.е. мы отправляем ajax-запрос нашему скрипту import_xls.php, ждем ответа, и, если ответ нас не устраивает, посылаем новый ajax-запрос. Встречал еще в сети решение без использования AJAX — при помощи редиректа в самом php. Обработка файла делится на малое количество строк и после этого вставляется код:
Но лично мне больше нравится решение с AJAX, потому что тут можно легко и просто добавить прогресс-бар и какие-нибудь другие рюшечки и плюшечки. Кстати, внимательный читатель заметил, что в моем коде простейший прогресс-бар уже реализован. Дополнительный немаловажный момент: в решение с AJAX не нужно заранее знать сколько строк скрипт сможет обработать за один проход.
Запись файла
Для записи файла в формате xls величиной так же около 25 000 строк крайне полезно использовать следующий код:
Можно еще поиграться с методами кеширования. Помимо кеширования во временной директории php еще поддерживается memcache:
А так же cache_to_discISAM.
UPDATE
Прошу у всех прощения за то, что код тут был представлен ужасно. Сразу не разобрался с подсветкой синтаксиса на Хабре (видимо, позднее время суток сказалось) и поторопился. Впредь не буду торопиться и буду умнее.
UPDATE2
Переделал JavaScript. Теперь он посылает запрос по таймауту в случае, если сервер не ответил.
My 128M PHP memory limit quickly gets exhausted even when I am only trying to open a small excel file of ~350 KB with PHPExcel.
Although, I can increase the memory limit in the configuration but it'll be great to see if there are any alternatives to fix this.
@SoapBox: In short, PHPExcel - it's a real memory hog :( Also, it's kind of a complex library, so finding (let alone fixing) a bug there is rather non-trivial. In the worst case, it may be easier to sidestep the issue and find some alternative library.
Become Microsoft Certified, it only requires knowing how to reboot the machine. This will free up the memory being used by leaky software.
@SoapBox - if anybody could help me find a way of reducing the requirements of PHPExcel (without slowing it down to the point of being unusable), then I'd happily implement it
8 Answers 8
File size isn't a good measure for workbook files when working with PHPExcel. The number of rows and columns (ie cells) is more important.
The PHPExcel code itself has a footprint of between 10 and 25MB, depending on which components are being accessed.
At present, each cell in a workbook takes on average 1k of memory (without any caching) or 1.6k on 64-bit PHP - I'll assume 32-bit PHP for the moment - so (for example) a worksheet of 8000 lines with 31 columns (248,000 cells) will be about 242MB. With cell cacheing (such as php://temp or DiskISAM), that can be reduced to about a third, so the 8000 lines by 31 columns will require about 80MB.
There are a number of options available to help you reduce the memory usage:
Are you using cell caching with PHPExcel?
If you only need to access data in your worksheets, and don't need access to the cell formatting, then you can disable reading the formatting information from the workbook:
If you only need to access some, but not all of the worksheets in the workbook, you can load only those worksheets:
if you only want to read certain cells within worksheets, you can add a filter:
All of these techniques can significantly reduce the memory requirements.
I don't understand it. The XSLX table is about 3MB large yet even 1024MB of RAM is not enough for PHPExcel to load it into memory?
The function above reads data from an excel table to an array.
At first, I allowed PHP to use 256MB of RAM. It was not enough. I then doubled the amount and then also tried 1024MB. It still runs out of memory with this error:
Читайте также: