fs.readFile(path, 'utf8', callback) membaca seluruh file dan memberikan isinya ke callback sebagai string. fs.readFileSync(path, 'utf8') langsung mengembalikan string-nya dan menunggu sampai pembacaan selesai, sedangkan await readFile(path, 'utf8') dari fs/promises melakukan hal yang sama tanpa memblokir. Kalau 'utf8' dihilangkan, kamu mendapat Buffer berisi byte mentah, bukan teks.
readFile called tercetak lebih dulu: readFile hanya memulai pembacaan, dan callback-nya berjalan setelah isi file siap. Hapus 'utf8', lalu jalankan lagi: callback menerima <Buffer 42 75 79 20 6d 69 6c 6b 0a 43 61 6c 6c 20 41 64 61>, byte mentah dari file itu.
readFile vs readFileSync vs fs/promises
Ketiganya membaca file yang sama. Bedanya ada pada cara kamu mendapat hasilnya dan apakah sisa program ikut menunggu.
Baris sync tercetak lebih dulu karena tidak ada hal lain yang berjalan sampai ia kembali. Dua lainnya selesai setelah kode utama, dengan urutan sesuai pembacaan mana yang selesai duluan.
readFileSync | readFile (callback) | fs/promises readFile | |
|---|---|---|---|
| Mengembalikan | Isi file | undefined | Promise berisi isi file |
| Memblokir program | Ya | Tidak | Tidak |
| Error | Dilempar, pakai try/catch | Argumen pertama callback | Promise reject, try/catch dengan await |
| Dipakai di | Kode startup dan script | Kode callback lama | Kode baru |
Pakai readFileSync saat membaca config ketika startup atau di script pendek. Di server, pembacaan sync menghentikan setiap request lain selama disk bekerja, jadi pakai versi promise di sana. Halaman fs/promises membahas sisa API tersebut.
Menangani file yang tidak ada (ENOENT)
Saat file tidak ada, error-nya punya code: 'ENOENT' ("error, no entry"). Cek err.code untuk membedakan file yang tidak ada dari kegagalan lain, seperti masalah izin akses.
Panggilan pertama tidak menemukan file dan kembali ke default settings; panggilan kedua membaca dark mode. Melempar ulang error lainnya membuat masalah sungguhan tetap terlihat, alih-alih diam-diam memakai nilai default.
fs.existsSync: kenapa sebaiknya langsung coba membaca
fs.existsSync(path) mengembalikan true atau false, dan rasanya menggoda untuk mengecek dulu sebelum membaca:
if (fs.existsSync('data.txt')) {
const text = fs.readFileSync('data.txt', 'utf8');
}
Itu dua langkah padahal satu sudah cukup, dan file bisa saja dihapus di antara keduanya, jadi pembacaan tetap bisa melempar error. Coba baca langsung dan tangani ENOENT, seperti di atas. existsSync cocok saat kamu hanya perlu tahu apakah sesuatu ada dan tidak akan membukanya, misalnya untuk memutuskan apakah perlu membuat folder.
Membaca file JSON
Baca teksnya, lalu parse dengan JSON.parse. File dengan kesalahan sintaks melempar SyntaxError, jadi letakkan di dalam try yang sama.
Hasilnya 3001 dan boolean: setelah di-parse, nilai-nilainya punya tipe JSON masing-masing. require('./config.json') juga bisa, tetapi hasilnya di-cache, jadi require kedua mengembalikan object lama walaupun file-nya sudah berubah.
Membaca file per baris
Untuk file yang muat di memori, baca seluruhnya lalu split di karakter newline. Buang string kosong yang tersisa karena newline di akhir file.
Split menghasilkan 4 item, yang terakhir kosong, jadi perulangannya mencetak tiga baris bernomor. File yang ditulis di Windows mengakhiri baris dengan \r\n; split dengan /\r?\n/ untuk menangani keduanya.
Log berukuran beberapa gigabyte tidak muat di memori. Stream saja, supaya hanya satu baris yang disimpan dalam satu waktu:
const fs = require('fs');
const readline = require('readline');
async function main() {
const rl = readline.createInterface({
input: fs.createReadStream('big.log'),
crlfDelay: Infinity,
});
for await (const line of rl) {
console.log(line);
}
}
main();
Kesalahan umum
Memakai hasilnya di luar callback. const text = fs.readFile(...) bernilai undefined. Isi file hanya ada di dalam callback, atau setelah await.
Lupa encoding. Tanpa 'utf8' kamu mendapat Buffer, dan data.split('\n') melempar data.split is not a function. Berikan encoding setiap kali kamu menginginkan teks.
Path relatif ke folder yang salah. 'data.txt' dicari di tempat node dijalankan. Pakai path.join(__dirname, 'data.txt') untuk file yang disertakan di samping script-mu.
Pertanyaan yang sering diajukan
Kenapa fs.readFile mengembalikan undefined?
fs.readFile tidak mengembalikan isi file. Fungsi ini memulai pembacaan dan memanggil callback-mu belakangan dengan (err, data). Pakai datanya di dalam callback, panggil readFileSync, atau await versi promise-nya.
Di mana Node mencari path relatif seperti 'data.txt'?
Di current working directory, yaitu folder tempat kamu menjalankan node, bukan folder script-nya. Untuk membaca file yang ada di samping script, susun path-nya dengan path.join(__dirname, 'data.txt').
Bisakah fs.readFile membaca file dari URL?
Tidak, fungsi ini hanya membaca dari file system. Untuk mengambil file lewat HTTP, pakai fetch lalu await res.text().
Seberapa besar file yang bisa ditangani readFile?
Fungsi ini memuat seluruh file ke memori, jadi cocok untuk config, template, dan file data kecil. Untuk file berukuran ratusan megabyte, baca per bagian dengan fs.createReadStream.