Čítanie súborov
Obe čítačky sú súčasťou balíka a streamujú: v pamäti je len aktuálny riadok a zdieľané reťazce XLSX sa presúvajú do streamu php://temp (v pamäti do xlsx.shared_strings_memory, potom na disk) — zošit s 200 000 reťazcami sa prečíta s výrazne menej než 16 MB. Nie je potrebná žiadna externá knižnica na tabuľky, ktorú by bolo treba inštalovať a aktualizovať.
Formát podľa obsahu
Prípona aj MIME typ od klienta sa ignorujú. Prázdny súbor sa odmietne (empty) a súbor väčší než limits.file_size sa odmietne ešte pred čítaním (too_large):
| Obsah | Výsledok |
|---|---|
| ZIP so zošitom SpreadsheetML | XLSX (.xlsm len s xlsx.allow_macro_enabled) |
| OpenDocument, binárny Excel, iný ZIP | open_document · xlsb · unsupported_format |
| zložený súbor OLE (.xls, zošity chránené heslom) | legacy_or_encrypted |
| značky poradia bajtov UTF-16 / UTF-32, bajt NUL v prvých 64 KB | utf16 · utf32 · binary |
| čokoľvek iné | CSV |
CSV
- UTF-8 s BOM aj bez neho. Celý súbor sa kontroluje po 64 KB blokoch; prvý neplatný bajt ho odmietne ako not_utf8 s uvedením riadka a odhadu (Windows-1250, Windows-1252, ISO-8859) a používateľovi poradí uložiť súbor ako „CSV UTF-8“. Čisté ASCII prejde.
- Prvý riadok sep=; z Excelu určí oddeľovač. Inak sa rozpozná spomedzi csv.delimiters (čiarka a bodkočiarka) podľa najkonzistentnejšieho rozdelenia, alebo ho nastavíte cez upload(…, delimiter: ';') či useDelimiter().
- Úvodzovky podľa RFC 4180 (oddeľovače, úvodzovky a nové riadky v úvodzovkách), bez escape znaku; súbory s LF, CRLF aj len CR.
- Čísla riadkov zodpovedajú tým, ktoré ukáže tabuľkový editor; prázdne záznamy sa rátajú, ale neimportujú; chýbajúce bunky na konci sú prázdne; neprázdna hodnota za posledným stĺpcom hlavičky je chyba riadka.
- Úvodný ' pred = + - @, tabulátorom alebo CR sa odstráni (csv.unescape_formula_prefix) — to pridáva vlastný CSV zapisovač balíka.
XLSX
| Oblasť | Podporované |
|---|---|
| Časti | Nájdené cez vzťahy balíka; transitional aj strict OOXML; absolútne názvy častí aj s inou veľkosťou písmen. |
| Hárky | Predvolene prvý viditeľný hárok; sheet: 'Name' (presne, potom bez ohľadu na veľkosť písmen — aj skryté) alebo sheet: 2 (od 1 medzi viditeľnými); hárky s grafmi sa preskočia. |
| Bunky | Zdieľané reťazce (rich text spojený, fonetické úseky vynechané, _xHHHH_ dekódované), inline reťazce, výsledky vzorcov (uložené hodnoty — vzorce sa nikdy nevyhodnocujú), logické hodnoty, chyby (chyba bunky pri overení), ISO dátumy, čísla. |
| Dátumy | Rozhodujú číselné formáty; systém 1900 (fiktívny 29. február 1900 z Lotusu sa odmietne) aj 1904; formáty len s časom sa čítajú ako H:i:s; miestny čas v časovom pásme relácie — pole DateTime ho potom prevedie do app.timezone. |
| Riedke riadky | Chýbajúce atribúty r sa čítajú postupne; formátované prázdne riadky sa preskočia; dáta pod viac než xlsx.max_blank_rows prázdnymi riadkami súbor odmietnu, namiesto toho, aby sa stratili. |
Bezpečnosť kontajnera a XML: ZIP sa otvára len na čítanie s kontrolou konzistencie lokálnych a centrálnych hlavičiek; počet položiek je obmedzený (xlsx.max_entries); šifrované položky sa odmietnu; žiadna časť nesmie deklarovať viac než xlsx.max_part_size; skutočne rozbalené bajty sa rátajú počas spracovania, pre každú časť aj za celé čítanie (xlsx.max_inflated), takže nepravdivá veľkosť sa zastaví uprostred streamu; položky sa spracúvajú priamo zo ZIP-u, nikdy sa nerozbaľujú; DOCTYPE v ktorejkoľvek spracúvanej časti súbor odmietne skôr, než by sa mohla deklarovať entita, a externá entita sa nikdy nerozrieši. Čítačka po zatvorení súboru obnoví predchádzajúci režim chýb libxml procesu, takže queue workery aj Octane si ponechajú svoj.
Hárky, oddeľovače a nadpisy
// A title row above the headers, a named sheet, a forced delimiter
$session = Imports::for($team)->by($user)->upload(
'assets',
$request->file('file'),
sheet: 'Assets', // a name (exact, then case-insensitive) or a 1-based position among visible sheets
delimiter: ';',
headerRow: 2,
locale: 'sk', // labels, messages and the decimal separator of this session
);
// Change it later — the preview is dropped (preview again)
$session->useSheet('Assets'); // XLSX: another sheet
$session->useDelimiter(';'); // CSV: override the sniffed delimiter
$session->useHeaderRow(2); // a title row above the headersLimity
limits.file_size (KB, predvolene 10 240), limits.rows (dátové riadky, predvolene 10 000, strop 100 000), limits.columns (predvolene 100 — len neprázdne bunky) a limits.cell_length. Importér môže každý z nich prepísať cez limits() — dĺžku bunky cez withCellLength().
Odmietnutia
Odmietne sa celé nahratie a nič sa neuloží. ImportFileRejectedException (HTTP 422) nesie reason (RejectionReason) a context; jej správa je preložená do angličtiny a slovenčiny a hovorí, čo urobiť:
use RoundlyConsulting\Imports\Exceptions\ImportFileRejectedException;
try {
$session = Imports::for($team)->by($user)->upload('assets', $request->file('file'));
} catch (ImportFileRejectedException $e) {
$e->reason; // RejectionReason::NotUtf8
$e->context; // the values in the message (line, encoding guess)
$e->getMessage(); // translated, and says how to fix the file
}| RejectionReason | Kedy |
|---|---|
empty | Súbor nemá žiadne bajty. |
too_large | Nad limits.file_size — kontroluje sa pred čítaním. |
macro_enabled | Zošit .xlsm, kým je xlsx.allow_macro_enabled vypnuté. |
open_document · xlsb · unsupported_format | Súbor .ods, binárny zošit, iný ZIP. |
legacy_or_encrypted | Súbor OLE: .xls alebo zošit chránený heslom. |
utf16 · utf32 · binary · not_utf8 | Kontroly kódovania; not_utf8 uvedie riadok a odhad (Windows-1250, Windows-1252, ISO-8859) a požiada o uloženie ako „CSV UTF-8“. |
no_header | Riadok hlavičky je prázdny. |
too_many_columns · too_many_rows | Nad limitmi importéra. |
malformed_spreadsheet | Poškodený alebo neplatný zošit, nekonzistentný ZIP, DOCTYPE v spracúvanej časti. |
data_after_blank_rows | XLSX: riadok s dátami pod viac než xlsx.max_blank_rows prázdnymi riadkami — riadky sa nikdy potichu nestratia. |
zip_too_large | Časť zošita sa rozbalí nad xlsx.max_part_size / xlsx.max_inflated (aj dávky súborov). |
format_not_accepted | formats() importéra vylučuje rozpoznaný formát. |
too_many_files · archive_too_large · zip_ratio · zip_unsafe_path · zip_symlink · zip_encrypted · zip_inconsistent · zip_entry_too_large | Dávky súborov — pozrite Dávky súborov. |
unreadable | Nahratý súbor sa nedal prečítať. |
Neexistujúci hárok vyhodí SheetNotFoundException (422) — available uvádza názvy hárkov.
Samostatná čítačka
Vlastná čítačka spracovania, použiteľná samostatne pre akýkoľvek CSV alebo XLSX súbor, ktorý vaša aplikácia potrebuje prečítať — bez relácie, nič sa neukladá, s rovnakými bezpečnostnými kontrolami:
use RoundlyConsulting\Imports\DataTransferObjects\ImportSource;
use RoundlyConsulting\Imports\Facades\Imports;
$file = Imports::read($request->file('file'), sheet: 'Assets', delimiter: null, headerRow: 1);
// or Imports::read('/path/to/file.csv'), Imports::read(ImportSource::disk('s3', 'exports/assets.xlsx'))
try {
$file->format(); // SourceFormat::Csv | SourceFormat::Xlsx
$file->sheets(); // list<SheetInfo> (XLSX), [] for CSV
$file->sheet(); // the sheet read (XLSX) or null
$file->delimiter(); // CSV: the delimiter used; delimiterSource(): Sniffed | Hint | Override
$file->encoding(); // CSV: `utf-8` or `utf-8-bom`; null for XLSX
$file->headerRow(); // 1-based
$file->headers(); // list<string|null>, null for a blank header cell
$file->displayHeaders(); // list<string>, a blank header is `Column C`
foreach ($file->rows() as $rowNumber => $cells) { // keyed by the row number a user sees
// list<Cell|null> — null for an empty cell
}
$file->count(); // data rows, in one full pass (every limit enforced)
} finally {
$file->close(); // also closed on destruct
}rows() súbor odmietne (too_many_rows) pri prvom riadku nad limitom. Zdroj z disku sa skopíruje do dočasného adresára, ktorý vyčistí imports:sweep. SpreadsheetFile::open() číta lokálnu cestu priamo s ReadOptions:
use RoundlyConsulting\Imports\Reading\{Cell, ReadOptions, SpreadsheetFile};
$file = SpreadsheetFile::open('/path/to/file.xlsx', ReadOptions::fromConfig(sheet: 'Assets', headerRow: 2));
foreach ($file->rows() as $rowNumber => $cells) {
$cell = $cells[0]; // ?Cell
$cell?->type; // CellType: text, number, boolean, date, error
$cell?->value; // string, numeric string, bool, CarbonImmutable or an error code (#N/A)
$cell?->display();
}
// Accepted everywhere a file is
ImportSource::upload($uploadedFile); ImportSource::path('/tmp/a.csv'); ImportSource::disk('s3', 'exports/a.xlsx');Prejavte lásku k open source
Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.
Ďalšie spôsoby podpory vrátane kryptomienOdoslaním daru súhlasíte s našimi podmienkami prijímania darov.
Chcete to zabudovať do svojho produktu?
Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.