Artikel Teknis

Menambal Satu Worksheet dalam XLSX Besar dari Delphi

HotXLS dapat menulis ulang satu worksheet di dalam paket XLSX yang sudah ada tanpa mem-parsing atau mengompresi ulang sisa berkasnya. TXLSDirectWriter.BeginPatch membuka paket sumber, menyalin setiap entri kecuali sheet target dengan byte terkompresinya secara verbatim, dan membiarkan Anda menulis ulang sheet tunggal itu melalui panggilan AddSheet, AddRow, dan Write* biasa. Chart, pivot cache, theme, style, dan shared string sama sekali tidak pernah didekompresi

Alur kerja yang diselesaikan ini muncul dalam pelaporan dan penyegaran data. Sebuah workbook datang dari tim bisnis membawa pivot table, slicer, conditional format, dan sedekade pemformatan yang terakumulasi. Setiap malam satu sheet data harus diganti dengan angka terbaru. Memuat dan menyimpan ulang seluruh workbook memakan biaya beberapa menit per berkas dan, yang lebih penting, mengambil risiko fidelitas pada fitur yang harus dibangun ulang oleh engine pemuatan. Patching menghindari kedua masalah itu dengan tidak menyentuh apa yang tidak perlu disentuh

Mengapa menyalin byte terkompresi menjadi bagian yang menarik?

Entri zip yang disalin pada level terkompresi berbiaya satu stream copy. Entri yang sama yang melewati jalur penulisan normal berbiaya satu inflate saat masuk dan satu deflate saat keluar, dan deflate adalah bagian yang mahal. Pada workbook dengan pivot cache besar dan beberapa lusin gambar yang disematkan, perbedaan itu adalah perbedaan antara patch yang selesai dalam waktu yang dibutuhkan untuk menulis sheet baru dan patch yang menghabiskan sebagian besar waktunya mengompresi ulang byte yang tidak pernah diperiksanya

HotXLS menggunakan CopyCompressedFrom untuk ini, yang menulis byte terkompresi entri sumber langsung ke arsip target. Ketika sebuah entri tidak dapat disalin dengan cara itu, karena menggunakan metode kompresi berbeda atau enkripsi lemah, writer jatuh kembali ke penyalinan stream terdekompresi alih-alih gagal. Entri marker direktori dilewati, karena writer menghasilkan miliknya sendiri

Ganti di tempat, atau tulis ke berkas baru

Dua overload mencakup dua bentuk yang diambil tugas ini. Bentuk in-place mementaskan hasilnya di berkas sementara di sebelah aslinya, menutup handle sumber, lalu menghapus dan mengganti nama, sehingga crash di tengah penulisan meninggalkan aslinya utuh. Bentuk target eksplisit membiarkan sumber tidak tersentuh dan dapat mengganti sheet atau menambahkan yang baru:

var
  W: TXLSDirectWriter;
begin
  W := TXLSDirectWriter.Create;
  try
    W.BeginPatch('monthly-dashboard.xlsx', 'Data');   // di tempat
    W.AddSheet('Data');
    W.AddRow(1);
    W.WriteString(1, 'Region');
    W.WriteString(2, 'Revenue');
    W.AddRow(2);
    W.WriteString(1, 'North');
    W.WriteNumber(2, 184320.55);
    W.AddRow(3);
    W.WriteFormula(1, '=SUM(B2:B2)');
    W.Close;
  finally
    W.Free;
  end;
end;

Varian sisip menerima path sumber dan target ditambah InsertSheet:

  // Sumber tetap tidak tersentuh; target mendapat worksheet tambahan bernama Extra
  W.BeginPatch('template.xlsx', 'output.xlsx', 'Extra', True);
  W.AddSheet('Extra');
  W.AddRow(1);
  W.WriteString(1, 'appended by the nightly job');
  W.Close;

Penyisipan adalah bagian yang memerlukan pembukuan yang benar-benar rumit. Writer mem-parsing registri sheet dalam xl/workbook.xml dan peta relationship yang mengikat setiap sheet ke bagiannya, lalu memilih nomor bagian, identifier sheet, dan identifier relationship berikutnya yang bebas. Tipe relationship mengikuti konvensi paket sumber, sehingga menambal workbook ISO 29500 strict menerbitkan tipe relationship strict dan menambal workbook transitional menerbitkan tipe transitional

Apa yang secara sengaja dibuang dan dibatasi oleh patch

Calculation chain dibuang di kedua mode. Dalam mode ganti, entrinya menjelaskan cell dalam sheet yang tidak lagi ada dalam bentuk itu; dalam mode sisip, pergeseran indeks sheet membuatnya tidak valid sama sekali. Excel membangun ulang chain pada rekalkulasi berikutnya, sehingga membuangnya adalah tindakan yang benar, bukan lossy. Bagian ini ditinggalkan dari salinan, dan entri relationship serta content-type override-nya dihapus secara bedah

Dua semantik authoring berubah di dalam sebuah patch, dan keduanya mengikuti prinsip yang sama: patch tidak boleh mengganggu bagian yang tidak ditulis ulangnya. String ditulis inline ke dalam sheet alih-alih ditambahkan ke shared string table, karena tabel sumbernya diteruskan tanpa tersentuh. Dan StyleIndex merujuk ke entri dalam cellXfs paket sumber, bukan ke tabel style yang dibangun writer. Itu berarti Anda dapat merujuk format yang sudah didefinisikan workbook asli, yang biasanya persis apa yang diinginkan penyegaran data, tetapi juga berarti Anda harus tahu indeks mana yang membawa format mana

// Di dalam patch, StyleIndex mengindeks cellXfs paket SUMBER.
// Tanggal memerlukan indeks eksplisit yang memetakan ke format tanggal di sana:
W.WriteDateTime(3, EncodeDate(2026, 8, 22), DateStyleIndexFromTemplate);

// Overload WriteDateTime tanpa style ditolak dalam mode patch,
// karena ia mengasumsikan tabel style milik writer sendiri, yang
// tidak pernah dibuat oleh sebuah patch

Enam entry point authoring dibatasi: menambahkan table, chart, gambar, comment, defined name, dan cell style semuanya memunculkan exception dalam mode patch, dengan jaring pengaman kedua pada saat penutupan yang gagal jika salah satu counter-nya bukan nol. Masing-masing fitur ini memerlukan pengeditan bagian yang disalin patch secara verbatim, dan paket yang setengah teredit lebih buruk daripada operasi yang ditolak. Tepat satu sheet dapat ditambal per operasi

Kapan menambal dan kapan memuat

Patching adalah alat yang tepat ketika workbook besar, perubahan terbatas pada satu sheet, dan sisa berkasnya harus bertahan bit demi bit. Ini adalah alat yang salah ketika perubahan mencakup beberapa sheet, ketika pemformatan atau objek baru diperlukan, atau ketika berkasnya cukup kecil sehingga pemuatan dan penyimpanan normal tidak memakan biaya apa pun. Untuk pembuatan massal dari nol, jalur streaming yang dijelaskan dalam direct writer streaming tetap lebih cocok, dan ia berbagi API AddRow dan Write* yang sama, sehingga berpindah antara keduanya bersifat mekanis

Manipulasi level sheet di dalam workbook yang dimuat, ketika Anda memang menginginkan model objek lengkap, dibahas dalam menduplikasi worksheet dalam paket XLSX. Dan jika alasan Anda mempertimbangkan patch adalah karena pemrosesan seluruh workbook menjadi lambat, pengukuran dan perilaku memori dalam performa workbook besar layak dibaca sebelum memilih pendekatan

Memverifikasi bahwa patch benar-benar melakukan apa yang Anda pikirkan

Tiga pemeriksaan menangkap hampir semua kesalahan. Konfirmasikan bahwa bagian yang Anda harapkan bertahan memang masih ada dalam arsip, bahwa xl/calcChain.xml sudah hilang, dan bahwa membuka kembali berkas melalui TXLSXWorkbook melaporkan jumlah sheet yang Anda harapkan, tidak berubah untuk penggantian dan bertambah satu untuk penyisipan. Membaca kembali sheet yang ditambal dan membandingkan beberapa value dan formula menutup lingkarannya

Satu detail implementasi dari pengembangan fitur ini layak diulang, karena dapat menggigit siapa pun yang menulis kode level zip serupa. Nama bagian worksheet dicocokkan berdasarkan prefix, dan kesalahan off-by-one pada panjang prefix berarti predikat tidak pernah cocok, sehingga bagian yang baru ditulis bertabrakan dengan nama yang sudah ada dan pembaca yang mengambil entri terakhir dengan nama tertentu secara diam-diam memilih sheet yang salah. Jika sebuah patch tampak menukar isi dua sheet, periksa pencocokan nama sebelum memeriksa XML-nya

Patching di tempat, penulisan streaming, dan model objek workbook lengkap dikirimkan dalam library yang sama untuk Delphi dan C++Builder; daftar fiturnya ada di halaman komponen spreadsheet Delphi HotXLS