Bir sunucunun tek işi Excel dosyası üretmekse, Excel çalıştırmakla hiçbir işi yoktur. Bir derleme aracısına ya da rapor hizmetine Office kurup onu COM otomasyonuyla sürmek yanlış tasarımdır ve bu uygulama var olduğundan beri yanlış tasarımdır. Bunu Microsoft kendisi söylüyor, üstelik yirmi yıldır yumuşamayan bir kılavuzda: Office, gözetimsiz bir sunucu tarafı süreçten otomatikleştirilmek üzere ne inşa edilmiştir ne de lisanslanmıştır. Doğru yanıt, tabloda hiç Excel olmadan BIFF ve OOXML baytlarını doğrudan yazmaktır. HotXLS kütüphanesinin tüm önermesi budur: elektronik tablo biçimlerini kendisi okuyup yazan yerel bir Object Pascal kütüphanesi, dolayısıyla takılacak, sızdıracak ya da kişi başı ücret ödenecek bir masaüstü uygulaması yok
Bir hizmetten EXCEL.EXE sürmek neden başarısız olur
COM otomasyonu bir masaüstü programını uzaktan kumanda eder ve bir masaüstü programı, bir Windows hizmetinin ona veremeyeceği üç şeyi sessizce varsayar: yüklenmiş bir kullanıcı profili, etkileşimli bir pencere istasyonu ve ekranı izleyen bir insan. Bunları çekip alın; başarısızlıklar hiçbir geliştirici makinesinin yeniden üretemediği bir biçimde gelir. Kimsenin göremediği bir masaüstünde bir dosya kurtarma istemi, bir eklenti hatası ya da bir lisans etkinleştirme iletişim kutusu açılır ve bunu tetikleyen otomasyon çağrısı hiç geri dönmez. Çağıran taraf sonunda zaman aşımına uğrayıp ölür; Excel örneği ise çoğu zaman ölmez, dosya kilitlerini tutan ve bir sonraki çalıştırmayı zehirleyen bir öksüz olarak kalır. Bir hizmet hesabının altında on bir başıboş EXCEL.EXE sürecinin biriktiğini görmüş olan herkes hikâyenin gerisini bilir
Hiçbir şey çökmese bile ölçekleme hikâyesi daha iyi değildir. Bir Excel örneği tek çalışma kitaplık bir hattır, her özellik erişimi süreçler arası COM sıralamasının bedelini öder ve kodu çalıştıran makine, koşulları tam da bu kullanımı dışlayan bir Office lisansı taşır. Çoğu ekip bu sınırlarla her seferinde bir kesinti üzerinden tanışır ki "COM katmanını emekliye ayır" maddesinin yol haritasına kabaca böyle girdiği söylenebilir
O yeniden yazım başlamadan önce bir kapsam sorusunu netleştirin, çünkü işin ne kadarının gerçek olduğuna o karar verir. COM kodu neredeyse hiçbir zaman yalnızca hücre değerleri ayarlamaz. Biçim sabitleriyle Workbook.SaveAs çağırır, yeniden hesaplamayı zorlar, yazdırma ayarlarını iter, bazen panoya uzanır. Eski kodu dolaşın ve bu davranışlardan hangilerinin gerçekten çıktıya yansıdığını not edin; çünkü her biri yerel bir kütüphanenin farklı bir köşesine düşer ve birkaçının (pano birlikte çalışması bunun en bariz örneğidir) sunucu tarafında hiçbir anlamı yoktur, taşınmak yerine düşürülmelidir
İki yerel motor, iki sahiplik modeli
HotXLS, Excel sürecini iki doğrudan biçim uygulamasıyla değiştirir. Bir BIFF8 kayıt akışı motoru (TXLSWorkbook, lxHandle birimi) .xls dosyalarını ele alır. Bir OOXML paket yazıcısı (TXLSXWorkbook, lxHandleX birimi) ECMA-376 / ISO/IEC 29500 uyumlu .xlsx üretir. Sunucuya kaydedilecek ve kurulacak hiçbir şey yoktur ve bellek elverdiği sürece aynı anda istediğiniz kadar çalışma kitabını açık tutabilirsiniz
İnsanları erkenden tökezleten şey, iki cephenin belleğine farklı sahip olması ve bu farkın çökene kadar sessiz kalmasıdır:
var
Book: IXLSWorkbook; // arayüz başvurusu: kendiliğinden serbest bırakılır
Sheet: IXLSWorksheet;
BookX: TXLSXWorkbook; // düz nesne: siz serbest bırakırsınız
SheetX: TXLSXWorksheet;
begin
// BIFF8 .xls çıktısı - Free yok; sahibi arayüz başvuru sayacıdır
Book := TXLSWorkbook.Create;
Sheet := Book.Sheets.Add;
Sheet.Name := 'Report';
Sheet.Cells.Item[1, 1].Value := 'Generated without Excel';
Book.SaveAs('report.xls');
// OOXML .xlsx çıktısı - açık yaşam süresi
BookX := TXLSXWorkbook.Create;
try
SheetX := BookX.Sheets.Add('Report');
SheetX.Cells[1, 1].Value := 'Generated without Excel';
BookX.SaveAs('report.xlsx');
finally
BookX.Free;
end;
end;
XLS cephesi IXLSWorkbook arayüzü üzerinden başvuru sayımlıdır. Değişkeni arayüz türünde bildirin ve onun üzerinde asla Free çağırmayın; aynı nesneyi düz bir nesne değişkeninde tutup kendiniz serbest bırakırsanız, başvuru sayacı onu ikinci kez serbest bırakır. XLSX cephesi ise sıradan bir try..finally isteyen sıradan bir nesnedir. Hücre adresleme her iki tarafta da 1 tabanlıdır ki ikisinin anlaştığı tek yer burasıdır. Sayfa koleksiyonları anlaşmaz: XLS tarafındaki Entries 1 tabanlı, XLSX tarafındaki Items dizinleyicisi 0 tabanlıdır ve bu bir fark, hangi yönde yanlış yaparsanız yapın temiz derlenir ve kendini yalnızca çalışma zamanında gösterir
Bir çalışma kitabını doğrudan HTTP yanıtına yazmak
Sunucu tarafındaki bir dışa aktarmanın diske dokunmak için genellikle hiçbir nedeni yoktur. Geçici dosyalar bir temizlik politikası ister, eşzamanlı isteklerde çakışır ve müşteri verisini kimsenin denetlemeyi düşünmediği birimlerde bırakır. Her iki cephe de SaveAs aşırı yüklemeleriyle bir TStream alır, dolayısıyla çalışma kitabı doğrudan yanıta gidebilir:
Mem := TMemoryStream.Create;
Book := TXLSXWorkbook.Create;
try
Sheet := Book.Sheets.Add('Data');
Sheet.Cells[1, 1].Value := 'Generated ' + DateTimeToStr(Now);
Book.SaveAs(Mem); // GEÇERLİ akış konumundan yazar
Mem.Position := 0; // akışı devretmeden önce başa sar
Response.ContentType :=
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
Response.ContentStream := Mem; // artık Mem sahibi çerçevedir
finally
Book.Free;
end;
Açıklamasını hak eden satır, başa sarma satırıdır. SaveAs(Stream) akışın geçerli konumundan yazar ve sonrasında asla sıfıra geri aramaz. Mem.Position := 0 satırını unutun; istemci sıfır baytlık bir indirme alır ya da Excel dosyaya bozuk der. Bu, web cepheli çalışma kitabı kodundaki en yaygın ve en zalim hatadır, çünkü yalnızca akışın uzunluğunun sıfırdan farklı olduğunu doğrulayan her birim testinin yanından süzülüp geçer
Tek bir çalışma kitabı kurma yordamı, yapıyı değiştirmeden diğer bütün teslim biçimlerine ulaşır. SaveAsCSV, "bana sadece ham veriyi ver" isteğini yanıtlar, SaveAsHTML "bunu bir portal sayfasına bırak" durumunu karşılar, SaveAsRTF belge hatlarını besler ve SaveAsODS bir OpenDocument zorunluluğunu kapsar; hepsi hem dosya hem akış aşırı yüklemeleriyle gelir. Tek bir dışa aktarma yordamı artı bir biçim parametresi, eskiden dört ayrı COM makrosu olan şeyin yerini alır. HTML dışa aktarıcısının TXLSXHtmlExportOptions yapısı başlık, CSS sınıfı ve parça mı tam belge mi anahtarını taşır ki bu da portal durumunu, dışa aktarılmış işaretlemeyi düzenli ifadeyle düzenleme işinden uzak tutar
Hesaplayacak bir Excel süreci olmadan formül değerleri
COM otomasyonu altında Excel her şeyi bedavaya yeniden hesaplıyordu ve COM katmanını düşürmek bunu sessizce geri alır. SaveAs formülleri değerlendirmeden metin olarak saklar; sayılar ancak Excel dosyayı açıp yeniden hesapladığında belirir ve XLS cephesi bu davranışı RecalcOnSave ile CalculationMode üzerinden ayarlamanıza izin verir. Bir insana gidecek bir dosya için bu tam olarak doğrudur. Bir toplamı göndermeden önce doğrulamak zorunda olan bir hizmet için yanlıştır ve formülün sonucunu değil metnini yazan CSV dışa aktarımı için de yanlıştır. Her iki durumda da değerlendirmenin sunucuda yerleşik motorla yapılması gerekir:
SheetX.Cells[1, 1].Value := 1200;
SheetX.Cells[2, 1].Value := 950;
SheetX.Cells[3, 1].Formula := 'SUM(A1:A2)'; // XLSX cephesi: '=' öneki yok
Total := BookX.Calculate('SUM(A1:A2)'); // hemen sunucuda değerlendir
if Total <> 2150 then
raise Exception.Create('reconciliation failed before delivery');
Cephe geleneği burada yine ısırır. XLSX tarafı ifadeleri eşittir işareti olmadan Cell.Formula üzerinden atar; XLS tarafı ise onları başta bir '=' ile Cell.Value üzerinden yazar. Kodu birinden diğerine değiştirmeden taşıyın; yanlış gelenek, yalnızca formüle benzeyen bir metin dizesi saklar ve bunu işaretleyecek hiçbir hata olmaz. Bir çalışma kitabının formüllerinin kendi iş mantığınıza uzanması gerektiğinde, OnUserFunction geri çağrısı motorun bilinmeyen işlev adlarını değerlendirme anında Delphi koduna devretmesini sağlar. Bu, bir COM otomasyon sisteminin etrafında büyüdüğü elektronik tabloların içinde saklanma eğiliminde olan UDF eklentilerinin yerel karşılığıdır
Yalnızca sunucuda yüzeye çıkan dağıtım köşeleri
Birkaç ayrıntı yayılımın temiz mi yoksa kafa karıştırıcı mı olacağına karar verir ve ilki birim çizgesidir. Sürükle bırak veri kümesi dışa aktarıcısı TDataToXLS, VCL tarafındaki Forms, Controls ve Dialogs birimlerini içeri çeker. Bir masaüstü aracında zararsızdır; bir konsol hizmetinde ise bütün VCL yapısını peşinden sürükler. Çekirdek birimler lxHandle ve lxHandleX yalnızca Windows, Classes, SysUtils ve Variants birimlerine uzanır, dolayısıyla saf bir hizmet için kolaylık olsun diye bileşeni içeri almaktansa çekirdek API karşısında kendi veri kümesi döngüsünü yazmak daha iyidir
Sonra iş parçacığı meselesi var. Çalışma kitabı örnekleri iş parçacığı güvenli değildir, ama küresel durum da paylaşmazlar; dolayısıyla ölçeklenen desen en basit olanıdır: iş başına ya da çalışan iş parçacığı başına bir çalışma kitabı nesnesi. Bu size paralel rapor üretimi kazandırır ki tek bir paylaşımlı Excel örneği bunu hiçbir zaman yapamaz. Kendi çalışma kitabını oluşturan, dolduran, kaydeden ve serbest bırakan bir istek işleyicisi hiç kilit gerektirmez ve bir başarısızlığın patlama yarıçapı "paylaşımlı Excel örneği herkes için sıkıştı" düzeyinden "bu tek istek bir istisna yükseltti" düzeyine iner ki var olan hata işleyiciniz bununla ne yapacağını zaten bilir
Sonuncusu biçim hedeflemedir. TXLSWorkbook.SaveAs varsayılan olarak BIFF (xlExcel97) yazar ve XLS içeriğini .xlsx biçimine itmek, düşük sadakatle SaveXLSWorkbookAsXLSX köprüsünden geçer. Cepheyi, birinde kurup hattın sonunda dönüştürmek yerine, göndermeyi düşündüğünüz biçime göre tasarım anında seçin
Tipik bir değiştirme projesinin veri yükleme yarısı için veritabanından çalışma kitabına aktarma desenleri hem bileşeni hem elle yazılmış döngüyü kapsar ve satır sayıları altı haneye ulaştığında büyük çalışma kitabı başarım teknikleri, dakikalarla saniyeler arasındaki farkı belirler. Tasarımcı elinden çıkma düzenlerden kurulan raporlar şablon rapor üretimi anlatımında ele alınmaktadır
HotXLS, Delphi ve C++Builder için Object Pascal kaynak kodu olarak gelir; sürümler, lisanslama ve eksiksiz API başvurusu HotXLS Delphi Bileşeni ürün sayfasında yer alır