QR kod dalam CFML: jana, simpan dan sahkan
Menjana QR kod dalam CFML dengan pustaka ZXing dan interop Java: kawal pembetulan ralat, tetapkan margin, simpan sebagai PNG dan sahkan hasilnya dalam Lucee.

Kandungan
- QR kod dalam CFML: jana, simpan dan sahkan
- Apa itu ZXing dan kenapa ia sesuai untuk Lucee
- Sediakan pustaka ZXing pada pelayan
- Jana QR kod pertama anda
- Kawal pembetulan ralat dan margin
- Simpan hasil sebagai PNG
- Sahkan QR kod dengan menyahkod semula
- Bungkus penjanaan dalam fungsi boleh guna
- QR kod dalam aliran pembayaran Malaysia
- Soalan Lazim
QR kod dalam CFML: jana, simpan dan sahkan
QR kod, atau kod Quick Response, ialah kod bar dua dimensi yang menyimpan teks, pautan, nombor telefon dan pelbagai data lain dalam corak segi empat sama. Dalam Lucee CFML anda tidak memerlukan sambungan khas untuk menjananya: enjin ini berjalan di atas JVM, jadi anda boleh memanggil pustaka ZXing terus melalui interop Java. Artikel ini menunjukkan tiga langkah lengkap, iaitu menjana matriks QR, menyimpan hasil sebagai fail PNG, dan menyahkod semula fail itu untuk mengesahkan kandungannya benar-benar betul.
Apa itu ZXing dan kenapa ia sesuai untuk Lucee
ZXing, disebut "zebra crossing", ialah pustaka Java sumber terbuka untuk membaca dan menjana kod bar satu dan dua dimensi. Ia asalnya dibangunkan oleh Google dan kini diselenggara di GitHub, dan ia menjadi pilihan lalai untuk kerja QR kod dalam ekosistem Java kerana ia matang, stabil dan tidak memerlukan lesen berbayar. Pustaka ini terbahagi kepada dua fail penting: zxing-core.jar yang mengandungi logik pengekodan dan penyahkodan, dan zxing-javase.jar yang mengandungi kelas tambahan untuk persekitaran Java SE seperti MatrixToImageWriter dan BufferedImageLuminanceSource. Kedua-dua fail ini yang akan kita gunakan.
Lucee sesuai dengan ZXing kerana interop Java ialah ciri teras enjin ini. Fungsi createObject("java", "...") membolehkan templat CFML mencipta objek Java dan memanggil kaedahnya secara terus, tanpa lapisan tengah dan tanpa sambungan Lucee. Ini bermakna kod QR dijana oleh enjin yang sama yang menjalankan halaman, bukan oleh proses luar atau perkhidmatan pihak ketiga.
Sediakan pustaka ZXing pada pelayan
Sebelum menulis kod, letakkan kedua-dua fail jar pada classpath pelayan Lucee. Laluan yang berfungsi dalam pemasangan Lucee ialah folder context/lib di bawah direktori pelayan:
# Salin jar ke classpath Lucee (laluan dalam bekas rasmi), kemudian mulakan semula enjin.
sudo cp zxing-core.jar zxing-javase.jar /opt/lucee/server/lucee-server/context/lib/
sudo systemctl restart lucee
Selepas pelayan dimulakan semula, kelas com.google.zxing.qrcode.QRCodeWriter dan kelas lain dalam pustaka itu tersedia kepada createObject. Anda tidak perlu memasang sebarang sambungan Lucee untuk langkah ini, kerana jar pada classpath boleh dicapai terus oleh interop Java.
Jana QR kod pertama anda
Langkah paling asas ialah mencipta penulis QR, mengekod teks kepada matriks, menukar matriks itu kepada imej, dan menyimpannya. Empat objek Java yang terlibat ialah QRCodeWriter, BarcodeFormat, MatrixToImageWriter dan ImageIO, dan setiap satu dipanggil melalui createObject.
<cfscript>
QB = createObject("java", "com.google.zxing.qrcode.QRCodeWriter").init();
BF = createObject("java", "com.google.zxing.BarcodeFormat");
matriks = QB.encode("https://lucee.my", BF.QR_CODE, 300, 300);
img = createObject("java", "com.google.zxing.client.j2se.MatrixToImageWriter").toBufferedImage(matriks);
ImageIO = createObject("java", "javax.imageio.ImageIO");
ImageIO.write(img, "png", createObject("java", "java.io.File").init(expandPath("./qr-uji.png")));
writeOutput("saiz=" & getFileInfo(expandPath("./qr-uji.png")).size);
</cfscript>
Baris demi baris: QRCodeWriter.init() mencipta penulis, BarcodeFormat.QR_CODE memberitahu penulis jenis kod yang dikehendaki, dan encode() memulangkan objek BitMatrix yang mewakili corak hitam putih. MatrixToImageWriter.toBufferedImage() menukar matriks itu kepada BufferedImage, dan ImageIO.write() menyimpannya sebagai PNG di laluan yang diberi. Pada rig ujian artikel ini, blok itu memulangkan saiz=372, iaitu saiz fail PNG untuk kandungan https://lucee.my pada 300 kali 300 piksel.
Perhatikan getFileInfo().size dan bukan fileLen(). Fungsi fileLen() tidak wujud dalam Lucee, jadi saiz fail dibaca melalui struktur yang dipulangkan oleh getFileInfo().
Kawal pembetulan ralat dan margin
QR kod direka untuk kekal boleh dibaca walaupun sebahagian coraknya rosak atau tertutup, dan tahap daya tahan ini ditentukan oleh pembetulan ralat. ZXing menawarkan empat tahap melalui enum ErrorCorrectionLevel, daripada L yang paling rendah hingga H yang paling tinggi. Tahap yang lebih tinggi menambah lebih banyak modul pada corak, jadi fail menjadi lebih besar tetapi kod lebih tahan terhadap kerosakan. Satu lagi tetapan berguna ialah margin, iaitu kawasan senyap putih di sekeliling kod, yang lalainya empat modul.
Kedua-dua tetapan ini dihantar melalui HashMap dan enum EncodeHintType:
<cfscript>
QB = createObject("java", "com.google.zxing.qrcode.QRCodeWriter").init();
BF = createObject("java", "com.google.zxing.BarcodeFormat");
ECL = createObject("java", "com.google.zxing.qrcode.decoder.ErrorCorrectionLevel");
HM = createObject("java", "com.google.zxing.EncodeHintType");
petunjuk = createObject("java", "java.util.HashMap").init();
petunjuk.put(HM.ERROR_CORRECTION, ECL.H);
petunjuk.put(HM.MARGIN, 1);
matriks = QB.encode("https://lucee.my", BF.QR_CODE, 300, 300, petunjuk);
img = createObject("java", "com.google.zxing.client.j2se.MatrixToImageWriter").toBufferedImage(matriks);
ImageIO = createObject("java", "javax.imageio.ImageIO");
ImageIO.write(img, "png", createObject("java", "java.io.File").init(expandPath("./qr-h.png")));
writeOutput("saiz=" & getFileInfo(expandPath("./qr-h.png")).size);
</cfscript>
Blok ini memulangkan saiz=560 pada rig yang sama, lebih besar daripada versi asas kerana tahap H menambah lebih banyak modul. Jadual di bawah merumuskan empat tahap dan bila setiap satu sesuai:
| Tahap | Pemulihan ralat | Bila sesuai |
|---|---|---|
| L | Kira-kira 7% | Kod ringkas pada cetakan bersih |
| M | Kira-kira 15% | Lalai, kegunaan umum |
| Q | Kira-kira 25% | Kod yang mungkin tercemar separa |
| H | Kira-kira 30% | Logo ditindih atau cetakan kasar |
Pilih H apabila anda bercadang meletakkan logo di tengah kod atau mencetak pada permukaan yang tidak rata. Untuk pautan mudah yang dipaparkan pada skrin, L atau M sudah memadai dan menghasilkan fail yang lebih kecil.
Simpan hasil sebagai PNG
ImageIO.write() menerima tiga hujah: imej, nama format, dan objek File destinasi. Format yang biasa disokong ialah png, jpg dan gif, tetapi PNG ialah pilihan terbaik untuk QR kod kerana ia tanpa kehilangan dan mengekalkan tepi tajam yang diperlukan pembaca. expandPath("./qr-uji.png") menyelesaikan laluan relatif kepada direktori templat yang sedang berjalan, jadi fail tersimpan di sebelah templat itu sendiri.
Untuk kerja imej yang lebih lanjut selepas fail dijana, seperti mengubah saiz atau memampatkan, Lucee menyediakan fungsi imej teras yang dibincangkan dalam panduan imej CFML. Dan apabila fail PNG itu akan diterima daripada pengguna dan disimpan, patuhi peraturan dalam panduan muat naik fail dengan selamat supaya laluan dan jenis fail diperiksa sebelum apa-apa ditulis ke cakera.
Sahkan QR kod dengan menyahkod semula
Menjana imej tidak membuktikan kandungannya betul. Kesilapan senyap boleh berlaku, contohnya teks yang tersalah bina atau fail yang rosak separa semasa disalin. Langkah pengesahan yang paling jujur ialah membaca semula fail yang disimpan dan menyahkodnya dengan ZXing sendiri, kemudian membandingkan teks yang dipulangkan dengan teks asal.
<cfscript>
QB = createObject("java", "com.google.zxing.qrcode.QRCodeWriter").init();
BF = createObject("java", "com.google.zxing.BarcodeFormat");
matriks = QB.encode("https://lucee.my", BF.QR_CODE, 300, 300);
ImageIO = createObject("java", "javax.imageio.ImageIO");
img = createObject("java", "com.google.zxing.client.j2se.MatrixToImageWriter").toBufferedImage(matriks);
ImageIO.write(img, "png", createObject("java", "java.io.File").init(expandPath("./qr-sah.png")));
imgBaca = ImageIO.read(createObject("java", "java.io.File").init(expandPath("./qr-sah.png")));
sumber = createObject("java", "com.google.zxing.client.j2se.BufferedImageLuminanceSource").init(imgBaca);
bitmap = createObject("java", "com.google.zxing.BinaryBitmap").init(
createObject("java", "com.google.zxing.common.HybridBinarizer").init(sumber)
);
Reader = createObject("java", "com.google.zxing.qrcode.QRCodeReader").init();
hasil = Reader.decode(bitmap);
writeOutput("teks=" & hasil.getText());
</cfscript>
Blok ini memulangkan teks=https://lucee.my, membuktikan imej yang disimpan boleh dibaca semula dan kandungannya sepadan dengan teks asal. Rantaian penyahkodan ialah BufferedImageLuminanceSource yang menukar imej kepada sumber cahaya, HybridBinarizer yang menukarkannya kepada hitam putih, BinaryBitmap yang membungkusnya untuk pembaca, dan QRCodeReader.decode() yang memulangkan objek Result. Nilai yang dibaca diambil melalui hasil.getText().
Bungkus penjanaan dalam fungsi boleh guna
Selepas memahami setiap langkah, bungkus keseluruhan aliran dalam satu fungsi supaya ia boleh dipanggil berulang kali daripada mana-mana templat. Di dalam fungsi, pemboleh ubah tempatan diisytihar dengan var, dan itu sah di sini, walaupun var di peringkat atas templat ditolak oleh Lucee.
<cfscript>
function janaQR(teks, saiz) {
var QB = createObject("java", "com.google.zxing.qrcode.QRCodeWriter").init();
var BF = createObject("java", "com.google.zxing.BarcodeFormat");
var matriks = QB.encode(teks, BF.QR_CODE, saiz, saiz);
var img = createObject("java", "com.google.zxing.client.j2se.MatrixToImageWriter").toBufferedImage(matriks);
var ImageIO = createObject("java", "javax.imageio.ImageIO");
var laluan = expandPath("./qr-fungsi.png");
ImageIO.write(img, "png", createObject("java", "java.io.File").init(laluan));
return laluan;
}
writeOutput("disimpan=" & janaQR("https://lucee.my", 300));
</cfscript>
Fungsi ini memulangkan laluan fail yang disimpan, jadi pemanggil boleh terus merujuk fail itu untuk dipaparkan atau dihantar. Apabila kandungan QR kod itu dibina daripada data pengguna, contohnya pautan dengan parameter, gunakan fungsi rentetan CFML untuk menyambung dan membersihkan rentetan sebelum ia dihantar kepada encode().
QR kod dalam aliran pembayaran Malaysia
QR kod bukan sekadar hiasan di Malaysia, ia sebahagian daripada infrastruktur pembayaran. DuitNow QR menggunakan format muatan berstruktur yang berbeza daripada pautan biasa, dan menjananya dengan betul memerlukan pembinaan rentetan khas dengan medan tertentu. Artikel DuitNow QR dalam CFML menerangkan format itu secara terperinci. Untuk gerbang pembayaran berasaskan bank pula, integrasi FPX dan PayNet dalam Lucee menunjukkan cara menghantar transaksi dan mengendalikan respons, dan kedua-duanya boleh berpasangan dengan penjanaan QR di sini untuk menghasilkan kod bayaran terus daripada aplikasi anda.
Soalan Lazim
Adakah saya perlu memasang sambungan Lucee untuk menjana QR kod?
Tidak. Penjanaan QR kod menggunakan interop Java terus, iaitu createObject("java", "..."), dan ia tersedia dalam teras Lucee tanpa sebarang sambungan. Yang anda perlukan hanyalah dua fail jar ZXing pada classpath pelayan.
Kenapa fail QR kod saya lebih besar apabila saya pilih tahap H?
Tahap H menambah lebih banyak modul pembetulan ralat pada corak, jadi matriks mengandungi lebih banyak data dan imej PNG menjadi lebih besar. Saiz yang lebih besar itu ialah kos kepada daya tahan yang lebih tinggi terhadap kerosakan atau logo yang ditindih.
Bolehkah saya menjana QR kod dalam format selain PNG?
Ya, ImageIO.write() menyokong format lain seperti jpg dan gif, tetapi PNG ialah pilihan terbaik untuk QR kod kerana ia tanpa kehilangan dan mengekalkan tepi tajam yang diperlukan oleh pembaca kod.
Bagaimana saya tahu QR kod yang dijana mengandungi teks yang betul?
Jana imej, baca semula fail itu dengan QRCodeReader, dan bandingkan hasil.getText() dengan teks asal. Ini pengesahan sebenar dan bukannya tekaan, kerana ia menyahkod imej yang sama yang akan dibaca oleh pengguna.
Bolehkah saya meletakkan logo di tengah QR kod?
Boleh, tetapi gunakan tahap pembetulan ralat yang tinggi seperti H supaya kod masih boleh dibaca selepas sebahagian corak ditutup logo. Perlu diuji semula dengan menyahkod imej itu selepas logo ditindih, kerana logo yang terlalu besar akan mematahkan kod itu.