Pretvaranje rezultata upita u Excel izveštaj sastoji se od triju problema u jednom. Svaki tip polja u Delphiju mora da sleti u ćeliju kao ispravan tip u Excelu, red zaglavlja mora da izgleda kao izveštaj umesto kao ispis sheme, a brojevi, datumi i novčani iznosi moraju da nose formate koji će preživeti putovanje. Preskočite bilo koji od njih i datoteka će se i dalje otvarati, i dalje izgledati verovatno, a ipak će zakazati onog trenutka kada korisnik iz finansija odabere kolonu i pričeka zbir koji se nikada ne pojavi. Vrednosti su zapisane kao tekst, Excel ih tretira kao oznake i nikakva izuzetak nije prijavljen da bi vas upozorio
HotXLS je izvorna biblioteka proračunskih tabela za Object Pascal koja direktno zapisuje XLS i XLSX datoteke iz Delphija i C++Buildera, bez ikakve Excel automatizacije. Nudi dva puta od TDataset do radne sveske: ugrađenu komponentu TDataToXLS i ručno napisanu petlju prema API-ju radne sveske. Oni se ne mogu međusobno zameniti. Komponenta je deo VCL familije izgrađen na XLS fasadi, pa ispravan odabir zavisi od toga gde se kod izvršava i koji format datoteke primalac očekuje. Ono što sledi su oba puta, granica gde komponenta prestaje biti pravi alat i kako očuvati tipove polja netaknutima bez obzira na to koji put odabrali
Tipovi polja su stvarni izvozni ugovor
Pre bilo kog poziva API-ja odlučite kako će koji tip polja iz Delphija sleteti u ćeliju. Ćelija koja primi znakovni niz iz Delphija ostaje znakovni niz. HotXLS ne nagađa da je '1,234.50' trebao biti broj, niti bi to trebao činiti, jer je raščlanjivanje zavisno od lokaliteta tačno onaj način na koji se nemački decimalni zarez pretvara u separator hiljada na engleskom serveru. Pouzdan obrazac je dodela putem tipisanih pristupnika (accessors): AsFloat ili AsCurrency za numerička polja, AsDateTime za datume kako bi ćelija sadržala stvarni Excelov serijski broj datuma umjesto oblikovanog niza znakova, te AsString samo za polja koja su doista tekst
Rukovanje NULL vrednostima zaslužuje izričitu odluku, a ne zadano ponašanje. Pretvaranje vrednosti polja pomoću VarToStr pretvara SQL NULL u prazan niz znakova, što je tekstualna ćelija, dok preskakanje dodele ostavlja ćeliju doista praznom, što je ono što potrošači funkcija AVERAGE, COUNT i zaokretnih tabela (pivot-tables) očekuju. Za kolone sa novčanim iznosima odlučite pre pisanja petlje znači li NULL nulu ili nepoznatu vrednost. Njih dvoje se prikazuju identično nakon što neko oblikuje kolonu, a ta razlika menja svaki agregat koji se izračunava u kasnijem toku
Put komponente: TDataToXLS u VCL aplikacijama
Za klasičnu VCL aplikaciju sa upitom koji je već spojen u podatkovni modul (data module), TDataToXLS je put sa jednim pozivom. On prolazi kroz bilo kog potomka klase TDataset, bilo FireDAC, ADO, IBX ili bilo šta drugo što implementira apstraktno sučelje skupa podataka, i proizvodi stilizovani radni list sa zaglavljima, fontovima, obrubima, opcionalnim međuzbirovima grupa (subtotals) i automatskim deljenjem listova za velike skupove rezultata
var
Exporter: TDataToXLS;
begin
Exporter := TDataToXLS.Create(nil);
try
Exporter.Dataset := OrdersQuery; // any TDataset descendant
Exporter.WorksheetName := 'Orders';
Exporter.HeaderSource := hsDisplayLabel; // captions, not raw column names
Exporter.GroupFields.Add('CustomerID'); // subtotal block per customer
Exporter.RowsPerSheet := 50000; // stay below the BIFF8 row ceiling
Exporter.OnlyVisible := True; // respect Field.Visible
Exporter.SaveDatasetAs('orders.xls');
finally
Exporter.Free;
end;
end;
Dva svojstva ovde nose većinu produkcijske težine. HeaderSource := hsDisplayLabel zapisuje DisplayLabel svakog polja umesto sirovog SQL naziva kolone, pa radna sveska prikazuje 'Customer Name' umesto CUST_NM. Svojstvo RowsPerSheet postoji jer komponenta piše BIFF8 format, čija se mreža zaustavlja na 65.536 redova i 256 kolona; njegovo postavljanje na 50.000 deli veliki skup rezultata na više listova pre nego što ga gornja granica formata skrati. Izgledom upravljaju svojstva HeaderFont, DetailFont, GroupColor i stilovi obruba, a skup DisableFormat isključuje cele kategorije oblikovanja kada primalac želi jednostavne ćelije. Za bilo šta prilagođeno, događaji AfterCell i AfterRow predaju vam upravo zapisani raspon za naknadnu obradu
Gde komponenta prestaje biti opcija
Tri su ograničenja ugrađena u TDataToXLS, a njihovo poznavanje unapred sprečava neprijatan redizajn dva sprinta kasnije
- To je VCL komponenta u punom smislu. Njena jedinica povlači
Forms,ControlsiDialogs, pa povezivanje iste u konzolni posao ili Windows servis uvlači VCL u binarnu datoteku. Jezgrene jedinice radne sveske nemaju takvu zavisnost. Potrebni su im samoWindows,Classes,SysUtilsiVariants, zbog čega bi poslužiteljski kod trebao da koristi petlju prikazanu u nastavku - Izgrađena je na XLS fasadi. Komponenta popunjava sučelje
IXLSWorkbooki zapisuje .xls (BIFF8). Ne postoji svojstvo koje je prebacuje na OOXML izlaz - Njene funkcije/događaji govore XLS dijalektom. Parametar
Cell: IXLSRangeuAfterCellpripada XLS objektom modelu, pa je prilagođavanje po ćeliji napisano tamo kod u XLS stilu čak i ako se datoteka nakon toga pretvori u .xlsx
Stvaranje .xlsx formata iz izlaza komponente
Kada primalac insistira na .xlsx formatu, ali logika izvoza već živi u komponenti TDataToXLS, funkcija mosta u jedinici lxXlsxExport pretvara popunjenu radnu svesku u jednom pozivu:
uses lxXlsxExport;
Exporter.SaveDatasetAs('orders.xls');
// the component exposes the IXLSWorkbook it populated
SaveXLSWorkbookAsXLSX(Exporter.Workbook, 'orders.xlsx');
Tretirajte taj most kao prenosnik tabelarnih podataka, a ne kao pretvarač pune vernosti. On kopira vrednosti, formule, formate brojeva, boje ispune, atribute fontova, širine kolona i postavke prikaza. Namerno ne kopira obrube, spojene raspone, komentare, grafikone niti uslovna oblikovanja. Za običnu mrežu zaglavlja i redova to je tačno dovoljno. Za stilizovani izveštaj nije, i ispravan način je direktno generisanje XLSX-a radije nego krpljenje pretvorene datoteke
Ručno napisana petlja za servise i serijske poslove
Poslužiteljski kod trebao bi direktno da cilja TXLSXWorkbook. Pripazite na razliku u životnom veku između ove dve fasade pre kopiranja bilo kog uzorka. TXLSWorkbook na XLS strani drži se kroz sučelje sa brojenjem referenci i ne sme se ručno oslobađati, dok je TXLSXWorkbook obična klasa koja zahteva try..finally Free. Mešanje ovih dviju konvencija je pouzdan način za stvaranje curenja memorije ili dvostrukog oslobađanja
procedure ExportOrders(Q: TDataSet; const FileName: string);
var
Book: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
Row: Integer;
begin
Book := TXLSXWorkbook.Create;
try
Sheet := Book.Sheets.Add('Orders');
Sheet.Cells[1, 1].Value := 'Order No';
Sheet.Cells[1, 2].Value := 'Customer';
Sheet.Cells[1, 3].Value := 'Ordered';
Sheet.Cells[1, 4].Value := 'Amount';
Row := 2;
Q.First;
while not Q.Eof do
begin
Sheet.Cells[Row, 1].Value := Q.FieldByName('OrderNo').AsInteger;
Sheet.Cells[Row, 2].Value := Q.FieldByName('Customer').AsString;
if not Q.FieldByName('Ordered').IsNull then
Sheet.Cells[Row, 3].Value := Q.FieldByName('Ordered').AsDateTime;
Sheet.Cells[Row, 4].Value := Q.FieldByName('Amount').AsFloat;
Inc(Row);
Q.Next;
end;
Book.StreamingWrite := True; // stream sheet XML straight into the zip
Book.SaveAs(FileName);
finally
Book.Free;
end;
end;
Linije koje su važne su tipisane dodele i provera IsNull. Datumi dolaze kao serijski brojevi datuma, iznosi dolaze kao dvostruki realni brojevi (doubles), a NULL datumi porudžbina ostaju doista prazni umesto da postanu prazni znakovni nizovi. Postavka StreamingWrite := True menja samo putanju spremanja: XML radnog lista struji direktno u zip kontejner umesto da se najpre sastavlja kao jedan veliki znakovni niz, što izravnava vršno opterećenje memorije u trenutku poziva SaveAs za šesterocifreni broj redova. Svaka metoda spremanja ima i preopterećenje TStream, pa radna sveska može ići direktno u HTTP odgovor bez doticaja sa diskom. Članak strujno pisanje i serijski poslovi objašnjava taj obrazac implementacije, a članak performanse velikih radnih sveski pokriva što učiniti kada broj redova dodatno poraste
Ova petlja je ujedno i put koji se dobro skalira kroz niti (threads). Oba pogona su izvorni Object Pascal pisci, tokovi zapisa BIFF8 na jednoj strani i OOXML zip plus XML na drugoj, pa nijedan deo izvoza ne dotiče COM automatizaciju niti treba Excel licencu na poslužitelju. To vam donosi paralelnost bez uskog grla jedne instance, pod uslovom da svaka nit gradi sopstvenu radnu svesku. Objekti radnih sveski nisu bezbedni za rad sa više niti (thread-safe) kod zajedničke upotrebe, pa je pravilo jedna instanca po izvozu, nikada zajednička zaštićena lokotom
Jednu granicu vredi znati pre nego što dizajnirate oko nje. XLSX mreža se zaustavlja na 1.048.576 redova i 16.384 kolona, pa podela listova kojom upravlja RowsPerSheet na XLS strani ovde retko zatreba. Radna sveska od milion redova ionako je retko ono što ljudski primalac želi. Kada je skup rezultata doista tako velik, datoteka sa graničnicima obično je bolji ugovor, a članak izvoz u CSV i TSV pokriva graničnike, ponašanje BOM-a i upozorenje o proceni formula koje se tamo primenjuje
Odabir početne točke
Ako izvoz živi u stonom VCL alatu i .xls izlaz je prihvatljiv, počnite sa komponentom TDataToXLS i njenom podrškom za grupisanje. To zahteva najmanje koda, a most kroz SaveXLSWorkbookAsXLSX je tu kada neko kasnije zatraži .xlsx, sve dok prihvatate već opisane granice vernosti. Ako kod radi bez nadzora, ili primalac od samog početka zahteva .xlsx format, napišite petlju. Oba puta dolaze sa funkcionalnim demo projektima i deo su paketa HotXLS Component