Node.js File system

Uvod

Razumevanje file sistema u Node.js ključno je za razvoj aplikacija koje zahtevaju čitanje, pisanje i manipulaciju fajlovima. Node.js nudi fs modul, koji omogućava sinhrono i asinhrono upravljanje fajlovima. Ovaj članak detaljno objašnjava kako koristiti fs modul u Node.js.

fs modul je deo Node.js API-ja koji omogućava rad sa fajl sistemom. Modul se može učitati korišćenjem require funkcije:

const fs = require('fs');

file sistem

Čitanje fajlova

Da biste čitali sadržaj fajla, možete koristiti fs.readFile za asinhrono čitanje ili fs.readFileSync za sinhrono čitanje. Na primer:

Asinhrono čitanje sa callback-om:
const fs = require('fs');
fs.readFile('/putanja/do/fajla.txt', 'utf8', (err, data) => {
  if (err) {
    console.error(err);
    return;
  }
  console.log(data);
});
Asinhrono čitanje sa promisima:
const fsPromise = require('fs/promises');
(async () => {
  try {
    const data = await fsPromise.readFile('/putanja/do/fajla.txt', 'utf8');
    console.log(data);
  } catch (err) {
    console.error(err);
  }
})();
Sinhrono čitanje:
try {
  const data = fs.readFileSync('/putanja/do/fajla.txt', 'utf8');
  console.log(data);
} catch (err) {
  console.error(err);
}

Pisanje u fajlove

Za pisanje u fajlove, fs modul nudi fs.writeFile za asinhrono pisanje i fs.writeFileSync za sinhrono pisanje. Na primer:

Asinhrono pisanje sa callback-om:
const fs = require('fs');
fs.writeFile('/putanja/do/fajla.txt', 'Sadržaj koji treba zapisati', (err) => {
  if (err) {
    console.error(err);
    return;
  }
  console.log('Fajl je uspešno zapisan!');
});
Asinhrono pisanje sa promisima:
const fsPromise = require('fs/promises');
(async () => {
  try {
    await fsPromise.writeFile('/putanja/do/fajla.txt', 'Sadržaj koji treba zapisati');
    console.log('Fajl je uspešno zapisan!');
  } catch (err) {
    console.error(err);
  }
})();
Sinhrono pisanje:
try {
  fs.writeFileSync('/putanja/do/fajla.txt', 'Sadržaj koji treba zapisati');
  console.log('Fajl je uspešno zapisan!');
} catch (err) {
  console.error(err);
}
Pisanje otvorenog fajla

Ukoliko želimo da imamo detaljnu kontrolu nad procesom pisanja, uključujući mogućnost da odredimo kako će se pisanje odvijati (kreiranje novog fajla, dodavanje na kraj postojećeg, itd.) i mogućnost upravljanja resursima onda možemo primeniti sledeći pristup:

const fs = require('fs');
const content = 'Ovo je tekst koji želimo da zapišemo u fajl.';

// Otvorite fajl za pisanje
fs.open('/putanja/do/fajla.txt', 'w', (err, fd) => {
  if (err) {
    return console.error('Greška pri otvaranju fajla:', err);
  }
  console.log(`Fajl otvoren za pisanje, file descriptor: ${fd}`);

  // Pišite u fajl koristeći file descriptor
  fs.write(fd, content, (err) => {
    if (err) {
      console.error('Greška pri pisanju u fajl:', err);
      return;
    }
    console.log('Tekst uspešno zapisan.');

    // Zatvorite fajl nakon pisanja
    fs.close(fd, (err) => {
      if (err) {
        console.error('Greška pri zatvaranju fajla:', err);
        return;
      }
      console.log('Fajl zatvoren uspešno.');
    });
  });
});

Rad sa direktorijumima

fs modul takođe omogućava kreiranje, čitanje i brisanje direktorijuma. Na primer:

Kreiranje direktorijuma sa callback-om:
const fs = require('fs');
fs.mkdir('/putanja/do/novog/direktorijuma', { recursive: true }, (err) => {
  if (err) {
    console.error(err);
    return;
  }
  console.log('Direktorijum je kreiran!');
});
Kreiranje direktorijuma sa promisima:
const fsPromise = require('fs/promises');
(async () => {
  try {
    await fsPromise.mkdir('/putanja/do/novog/direktorijuma', { recursive: true });
    console.log('Direktorijum je kreiran!');
  } catch (err) {
    console.error(err);
  }
})();
Čitanje sadržaja direktorijuma sa callback-om:
const fs = require('fs');
fs.readdir('/putanja/do/direktorijuma', (err, files) => {
  if (err) {
    console.error(err);
    return;
  }
  console.log(files);
});
Čitanje direktorijuma sa promisima:
const fsPromise = require('fs/promises');
(async () => {
  try {
    const files = await fsPromise.readdir('/putanja/do/direktorijuma');
    console.log(files);
  } catch (err) {
    console.error(err);
  }
})();
Brisanje direktorijuma sa callback-om:
const fs = require('fs');
fs.rmdir('/putanja/do/direktorijuma', { recursive: true }, (err) => {
  if (err) {
    console.error(err);
    return;
  }
  console.log('Direktorijum je obrisan!');
});
Čitanje otvorenog fajla

Ukoliko ipak želimo da imamo finu kontrolu nad čitanjem, kao što je specifikacija tačne pozicije odakle želite početi čitanje unutar fajla i koliko podataka želite pročitati onda možemo koristiti sledeći pristup

const fs = require('fs');

// Prvo otvorite fajl da biste dobili file descriptor (fd)
fs.open('/putanja/do/fajla.txt', 'r', (err, fd) => {
  if (err) {
    return console.error('Greška pri otvaranju fajla:', err);
  }
  console.log(`Fajl otvoren, file descriptor: ${fd}`);

  // Alocirajte buffer za čitanje sadržaja fajla
  const bufferSize = 1024;
  let buffer = Buffer.alloc(bufferSize);

  // Čitajte sadržaj fajla
  fs.read(fd, buffer, 0, bufferSize, null, (err, num) => {
    if (err) {
      console.error('Greška pri čitanju fajla:', err);
      return;
    }
    console.log(`Pročitano ${num} bajtova.`);
    // Prikaz sadržaja fajla
    console.log(buffer.slice(0, num).toString());

    // Zatvorite fajl nakon čitanja
    fs.close(fd, (err) => {
      if (err) {
        console.error('Greška pri zatvaranju fajla:', err);
        return;
      }
      console.log('Fajl zatvoren uspešno.');
    });
  });
});

Bilo bi poželjno da buffer prilagodimo veličini fajla a to ćemo uraditi korišćenjem fs.fstat (ili fs.stat za asinhrono dobijanje informacija o fajlu pre čitanja), gde ćemo na taj način alocirati bafer tačno po meri sadržaja fajla, što čini čitanje efikasnijim, posebno za manje fajlove.

const fs = require('fs');

// Prvo otvorite fajl da biste dobili file descriptor (fd)
fs.open('/putanja/do/fajla.txt', 'r', (err, fd) => {
  if (err) {
    return console.error('Greška pri otvaranju fajla:', err);
  }
  console.log(`Fajl otvoren, file descriptor: ${fd}`);

  // Dobijte informacije o fajlu da biste utvrdili njegovu veličinu
  fs.fstat(fd, (err, stat) => {
    if (err) {
      console.error('Greška pri dobijanju informacija o fajlu:', err);
      return;
    }

    // Alocirajte buffer tačne veličine fajla
    const bufferSize = stat.size;
    let buffer = Buffer.alloc(bufferSize);

    // Čitajte sadržaj fajla
    fs.read(fd, buffer, 0, bufferSize, null, (err, num, readBuffer) => {
      if (err) {
        console.error('Greška pri čitanju fajla:', err);
        return;
      }
      console.log(`Pročitano ${num} bajtova.`);
      // Prikaz sadržaja fajla
      console.log(readBuffer.toString());

      // Zatvorite fajl nakon čitanja
      fs.close(fd, (err) => {
        if (err) {
          console.error('Greška pri zatvaranju fajla:', err);
          return;
        }
        console.log('Fajl zatvoren uspešno.');
      });
    });
  });
});
Brisanje direktorijuma sa promisima:
const fsPromise = require('fs/promises');
(async () => {
  try {
    await fsPromise.rm('/putanja/do/direktorijuma', { recursive: true, force: true });
    console.log('Direktorijum je obrisan!');
  } catch (err) {
    console.error(err);
  }
})();

Praćenje promena fajla

U Node.js, watch metoda iz fs modula omogućava praćenje promena na fajlovima ili direktorijumima. Ova funkcionalnost je korisna kada želite da automatski odgovorite na promene, kao što su ažuriranja fajlova, bez potrebe za ručnim osvežavanjem ili ponovnim pokretanjem aplikacije. Evo kako funkcioniše:

const fs = require('fs');

// Praćenje promena na fajlu
const filename = '/putanja/do/fajla.txt';

fs.watch(filename, (eventType, filename) => {
  console.log(`Dogodila se promena: ${eventType}`);
  if (filename) {
    console.log(`Fajl koji je promenjen je: ${filename}`);
  } else {
    console.log('Fajl nije specificiran');
  }
});

Ili uz korišćenje promisa:

const fs = require('fs');

(async () => {
    try {
        const watcher = await new Promise((resolve, reject) => {
            const watcher = fs.watch('/putanja/do/fajla.txt', (eventType, filename) => {
                console.log(`Dogodila se promena: ${eventType}`);
                if (filename) {
                    console.log(`Fajl koji je promenjen je: ${filename}`);
                } else {
                    console.log('Fajl nije specificiran');
                }
            });

            watcher.on('error', (error) => reject(error));
            resolve(watcher);
        });
        console.log('Nadgledanje je započeto.');
    } catch (error) {
        console.error('Došlo je do greške prilikom pokretanja nadgledanja:', error);
    }
})();

Kada koristite fs.watch, on može emitovati dva glavna tipa događaja: ‘change’ i ‘rename’. Događaj ‘change’ se odnosi na izmenu sadržaja fajla, dok ‘rename’ ukazuje na promenu u strukturi direktorijuma (npr. kreiranje novog fajla, preimenovanje ili brisanje fajla).

const fs = require('fs');

(async () => {
    const watcher = fs.watch('/putanja/do/fajla_ili_direktorijuma', (eventType, filename) => {
        if (eventType === 'rename') {
            console.log(`'rename' događaj detektovan za ${filename}.`);
            // Ovde implementirajte logiku specifičnu za 'rename' događaj.
            // Napomena: 'rename' može ukazivati na kreiranje, brisanje ili preimenovanje fajla.
        } else if (eventType === 'change') {
            console.log(`'change' događaj detektovan za ${filename}.`);
            // Ovde implementirajte logiku specifičnu za 'change' događaj.
            // Ovo se odnosi na promene unutar fajla.
        }
    });

    console.log('Nadgledanje je započeto.');

    // Opcionalno: zaustaviti nadgledanje nakon nekog vremena
    setTimeout(() => {
        watcher.close();
        console.log('Nadgledanje je zaustavljeno.');
    }, 10000); // zaustavlja nadgledanje nakon 10 sekundi
})();