cffile: baca, tulis, salin dan pindah fail
Ketahui cara guna cffile dalam Lucee CFML untuk baca, tulis, salin dan pindah fail dengan selamat, lengkap dengan contoh kod yang diuji dan tip keselamatan.

Kandungan
- Apa itu cffile
- cffile tag lawan fungsi fileRead dan fileWrite
- Membaca fail
- Menulis dan menambah fail
- Menyalin, memindah, menamakan semula dan memadam
- Jadual tindakan cffile
- Mod teks lawan mod binari
- Pengekodan UTF-8 secara eksplisit
- Penulisan atomik
- Path traversal dan nama fail daripada borang
- Di mana menyimpan fail
- Soalan Lazim
- Sumber
cffile ialah cara utama CFML membaca dan menulis fail pada pelayan. Ia satu tag dengan banyak action, daripada read dan write sehingga copy, move, rename dan delete. Artikel ini menunjukkan cara menggunakannya dengan selamat dalam Lucee, apa bezanya dengan fungsi fileRead dan fileWrite, dan kenapa fail data tidak sepatutnya diletakkan dalam webroot. Semua contoh kod di bawah telah dijalankan pada Lucee 7.1 dan lulus.
Apa itu cffile
cffile ialah tag CFML yang menguruskan fail di sisi pelayan. Setiap operasi dipilih melalui atribut action, dan atribut lain berubah mengikut tindakan itu. Untuk menulis fail anda guna action="write", untuk membaca action="read", dan untuk menyalin action="copy". Ia bekerja pada sistem fail tempatan, jadi ia sesuai untuk log, fail konfigurasi, pengeksportan CSV, dan penyimpanan muat naik yang diproses oleh pelayan.
Berbeza dengan cfhttp yang berurusan dengan fail di pelayan lain, cffile hanya menyentuh cakera pelayan sendiri. Ini menjadikannya alat asas untuk sebarang aplikasi yang perlu menyimpan atau membaca data di luar pangkalan data.
cffile tag lawan fungsi fileRead dan fileWrite
CFML menawarkan dua laluan untuk kerja fail: tag cffile dan fungsi skrip seperti fileRead, fileWrite, fileCopy dan fileMove. Kedua-duanya berakhir pada enjin yang sama, tetapi bentuknya berbeza.
Tag cffile ditulis sebagai tag XML dan biasanya meletakkan hasil ke dalam pemboleh ubah melalui atribut. Fungsi skrip pula memulangkan nilai secara terus, jadi ia lebih padat dan lebih mudah digabungkan dalam logik. Perbezaan ini sama seperti perbezaan cfquery tag dengan fungsi pertanyaan dalam skrip. Untuk penjelasan lengkap bila memilih bentuk tag atau skrip, lihat tag lawan sintaks skrip.
Tidak ada operasi yang hanya boleh dibuat oleh satu bentuk sahaja. cffile action="write" setara dengan fileWrite, dan cffile action="read" setara dengan fileRead. Pilih bentuk yang paling mudah dibaca dalam konteks anda: tag untuk templat yang kebanyakannya markup, fungsi untuk logik skrip.
Membaca fail
Membaca fail teks menggunakan action="read" dengan atribut variable untuk menyimpan kandungan. Kod berikut mencipta direktori contoh di /tmp, menulis satu fail, kemudian membacanya semula:
<cfscript>
if (!directoryExists("/tmp/uji-cffile")) {
directoryCreate("/tmp/uji-cffile");
}
</cfscript>
<cffile action="write"
file="/tmp/uji-cffile/data.txt"
output="Selamat datang ke Lucee CFML"
charset="utf-8"
addnewline="true">
<cffile action="read"
file="/tmp/uji-cffile/data.txt"
variable="kandungan"
charset="utf-8">
<cfoutput>#kandungan#</cfoutput>
Atribut charset memastikan fail ditulis dan dibaca sebagai UTF-8, bukan pengekodan lalai pelayan. Ini penting apabila kandungan mengandungi aksara selain ASCII, contohnya huruf Jawi, aksen atau emoji.
Menulis dan menambah fail
action="write" mencipta fail baharu atau menimpa fail sedia ada. Jika anda mahu mengekalkan kandungan lama dan menambah teks di hujungnya, gunakan action="append". Contoh biasa ialah fail log yang bertambah dari semasa ke semasa tanpa membuang entri lama.
Dalam bentuk fungsi skrip, kerja yang sama menjadi lebih padat:
if (!directoryExists("/tmp/uji-cffile")) {
directoryCreate("/tmp/uji-cffile");
}
// Fungsi skrip: padat, balik nilai secara terus
fileWrite("/tmp/uji-cffile/pesan.txt", "baris satu" & chr(10) & "baris dua", "utf-8");
teks = fileRead("/tmp/uji-cffile/pesan.txt", "utf-8");
// Mod binari: baca bait mentah, bukan rentetan
fileWrite("/tmp/uji-cffile/bin.dat", "abc", "utf-8");
bait = fileReadBinary("/tmp/uji-cffile/bin.dat");
writeOutput("TEKS=[" & teks & "] BAIT=" & arrayLen(bait));
Ambil perhatian pada fileAppend untuk fungsi setara action="append", dan fileReadBinary untuk mod binari.
Menyalin, memindah, menamakan semula dan memadam
Empat tindakan ini menguruskan fail yang sudah wujud. copy menghasilkan salinan dan mengekalkan fail asal. move memindahkan fail dan menghilangkan yang asal. rename menukar nama dalam direktori yang sama. delete membuang fail secara kekal, jadi ia tidak boleh dipulihkan tanpa sandaran.
<cfscript>
dir = "/tmp/uji-cffile";
if (!directoryExists(dir)) {
directoryCreate(dir);
}
fileWrite(dir & "/asal.txt", "kandungan asal", "utf-8");
</cfscript>
<cffile action="copy"
source="/tmp/uji-cffile/asal.txt"
destination="/tmp/uji-cffile/salin.txt">
<cffile action="rename"
source="/tmp/uji-cffile/salin.txt"
destination="/tmp/uji-cffile/dinamakan.txt">
<cffile action="info"
file="/tmp/uji-cffile/dinamakan.txt"
variable="infoFail">
<cfoutput>#infoFail.name# : #infoFail.size# bait</cfoutput>
<cffile action="delete" file="/tmp/uji-cffile/dinamakan.txt">
<cfoutput>Wujud selepas padam: #fileExists('/tmp/uji-cffile/dinamakan.txt')#</cfoutput>
action="info" memulangkan struktur dengan metadata fail seperti name dan size, berguna sebelum anda memutuskan untuk memproses sesuatu fail.
Jadual tindakan cffile
Setiap action ada kegunaan tersendiri. Jadual ini meringkaskan sembilan tindakan yang paling kerap digunakan:
| Tindakan | Kegunaan praktikal |
|---|---|
read | Baca keseluruhan fail teks ke dalam pemboleh ubah untuk diproses |
readBinary | Baca bait mentah fail seperti imej, PDF atau ZIP tanpa tafsiran teks |
write | Cipta fail baharu atau timpa kandungan fail sedia ada |
append | Tambah teks ke hujung fail, contohnya log yang berkembang |
copy | Salin fail ke lokasi baharu sambil mengekalkan asal |
move | Pindah fail ke lokasi baharu dan buang yang asal |
rename | Tukar nama fail tanpa mengubah lokasi |
delete | Buang fail secara kekal |
info | Dapatkan metadata seperti nama, saiz dan tarikh ubah |
Mod teks lawan mod binari
Fail boleh dianggap sebagai teks atau sebagai jujukan bait, dan pilihan ini mempengaruhi hasil. action="read" dan fileRead membaca fail sebagai teks, mentafsirkan bait mengikut pengekodan yang anda tentukan. action="readBinary" dan fileReadBinary memulangkan bait mentah tanpa tafsiran.
Guna mod binari untuk fail yang bukan teks: imej, PDF, arkib ZIP, atau apa-apa yang mengandungi bait yang tidak sah dalam UTF-8. Jika anda membaca fail binari sebagai teks, anda berisiko mendapat rentetan rosak. Sebaliknya, membaca fail teks sebagai binari memulangkan tatasusunan bait yang perlu anda tafsirkan sendiri. Ini berkaitan dengan kerja arkib seperti cfzip untuk mampat dan ekstrak, yang memerlukan data binari utuh.
Pengekodan UTF-8 secara eksplisit
Jangan bergantung pada pengekodan lalai pelayan. Fail yang ditulis oleh satu pelayan dengan pengekodan lalai Windows-1252 akan menjadi rosak apabila dibaca oleh pelayan lain yang menjangkakan UTF-8. Sentiasa nyatakan charset="utf-8" pada tag atau hujah ketiga fungsi fileWrite dan fileRead.
Tanpa atribut ini, Lucee menggunakan pengekodan lalai platform, dan aksara di luar ASCII akan rosak secara senyap. Inilah jenis pepijat yang hanya kelihatan apabila pengguna sebenar memasukkan nama dengan aksen atau huruf bukan Latin.
Penulisan atomik
Satu masalah klasik ialah pembaca melihat fail separuh siap. Jika proses menulis perlahan dan proses lain membaca pada masa yang sama, pembaca boleh mendapat kandungan yang dipotong. Penyelesaiannya ialah penulisan atomik: tulis ke fail sementara dahulu, kemudian pindahkan ke nama akhir dalam satu operasi.
// Penulisan atomik: tulis ke fail sementara, kemudian rename
function tulisAtomik(required string laluan, required string kandungan) {
var sementara = laluan & ".tmp";
fileWrite(sementara, kandungan, "utf-8");
fileMove(sementara, laluan);
}
// Pertahanan path traversal: hanya nama fail dalam senarai putih
function namaFailSah(required string nama) {
return reFindNoCase("^[a-z0-9._-]+$", trim(nama)) > 0;
}
if (!directoryExists("/tmp/uji-cffile")) {
directoryCreate("/tmp/uji-cffile");
}
if (namaFailSah("laporan.txt")) {
tulisAtomik("/tmp/uji-cffile/laporan.txt", "baris lengkap dan atomik");
}
writeOutput(fileRead("/tmp/uji-cffile/laporan.txt", "utf-8"));
fileMove pada sistem fail yang sama ialah operasi rename yang atomik, jadi nama akhir hanya wujud apabila kandungannya lengkap. Pembaca tidak akan melihat fail separuh siap.
Path traversal dan nama fail daripada borang
Bahaya terbesar datang apabila nama fail diambil daripada input pengguna. Pengguna boleh menghantar nilai seperti ../../etc/passwd dan memujuk aplikasi anda menulis atau membaca fail di luar direktori yang dimaksudkan. Ini dipanggil path traversal, dan ia disenaraikan oleh OWASP sebagai serangan web yang biasa.
Pertahanan yang betul ialah senarai putih: terima hanya nama yang sepadan dengan corak yang ketat, seperti ^[a-z0-9._-]+$ dalam fungsi namaFailSah di atas. Jangan sekali-kali menyalin nama fail pengguna terus ke dalam laluan, dan jangan bina laluan dengan menyambungkan rentetan input pengguna. Isu ini sama dengan prinsip dalam keselamatan web XSS dan CSRF: input pengguna sentiasa dianggap tidak boleh dipercayai.
Di mana menyimpan fail
Fail dalam webroot boleh diminta terus oleh pelayar. Ini berbahaya untuk fail data dan fail muat naik, kerana sesiapa yang meneka URL boleh memuat turun kandungan yang tidak sepatutnya awam. Peraturannya mudah: simpan fail data dan muat naik di luar webroot, atau dalam direktori yang penghantaran statiknya disekat.
Jika fail mesti berada dalam webroot, pastikan pelayan web tidak menghidangkan direktori itu sebagai fail statik, atau lindungi dengan peraturan akses. Untuk muat naik khususnya, baca panduan muat naik fail dengan selamat yang membincangkan pengesahan jenis fail dan saiz. Direktori /tmp dalam contoh artikel ini dipilih kerana ia berada di luar webroot, mencontohkan amalan yang betul.
Soalan Lazim
Apakah beza cffile dengan fungsi fileRead dan fileWrite?
Kedua-duanya melakukan kerja yang sama pada enjin yang sama. Tag cffile menggunakan atribut action dan menyimpan hasil ke pemboleh ubah melalui atribut, manakala fungsi memulangkan nilai secara terus. Pilih bentuk yang paling mudah dibaca dalam konteks anda.
Adakah saya perlu nyatakan charset="utf-8" setiap kali?
Ya, untuk fail teks. Tanpa atribut ini Lucee menggunakan pengekodan lalai platform, dan aksara di luar ASCII boleh rosak secara senyap apabila fail dipindahkan antara pelayan.
Bila perlu guna readBinary berbanding read?
Guna readBinary atau fileReadBinary untuk fail bukan teks seperti imej, PDF dan ZIP. Guna read atau fileRead untuk fail teks yang anda mahu tafsirkan sebagai rentetan.
Di mana fail muat naik dan fail data patut disimpan?
Di luar webroot, atau dalam direktori yang penghantaran statiknya disekat. Fail dalam webroot boleh diminta terus oleh pelayar, jadi kandungannya mesti dianggap awam.
Bagaimana menulis fail tanpa pembaca nampak kandungan separuh siap?
Guna penulisan atomik: tulis ke fail sementara dahulu, kemudian pindahkan ke nama akhir dengan fileMove. rename pada sistem fail yang sama ialah operasi atomik, jadi nama akhir hanya wujud apabila kandungan lengkap.