00
:
00
:
00
:
00
Corso SEO AI - Usa SEOEMAIL al checkout per il 30% di sconto

Accesso al File System

Uno dei principali vantaggi di Electron è l’accesso completo al file system tramite le API di Node.js. Questo permette di creare editor di testo, file manager, IDE e qualsiasi applicazione che necessiti di interagire con i file dell’utente.

Percorsi dell’Applicazione

Electron fornisce percorsi predefiniti per le cartelle comuni del sistema:

// main.js
const { app } = require('electron');

app.getPath('home');        // Home dell'utente: C:\Users\Nome
app.getPath('appData');     // Dati app: C:\Users\Nome\AppData\Roaming
app.getPath('userData');    // Dati utente: appData + nome app
app.getPath('documents');   // Documenti: C:\Users\Nome\Documents
app.getPath('downloads');   // Download: C:\Users\Nome\Downloads
app.getPath('desktop');     // Desktop: C:\Users\Nome\Desktop
app.getPath('temp');        // File temporanei
app.getPath('exe');         // Eseguibile dell'app

Leggere File

// main.js
const fs = require('node:fs');
const fsPromises = require('node:fs/promises');
const { ipcMain } = require('electron');

// Lettura asincrona (consigliata)
ipcMain.handle('fs:readFile', async (_event, filePath) => {
  try {
    const content = await fsPromises.readFile(filePath, 'utf-8');
    return { success: true, content };
  } catch (error) {
    return { success: false, error: error.message };
  }
});

// Leggere file binari (immagini, etc.)
ipcMain.handle('fs:readBinaryFile', async (_event, filePath) => {
  const buffer = await fsPromises.readFile(filePath);
  return buffer.toString('base64');
});

// Leggere file JSON
ipcMain.handle('fs:readJSON', async (_event, filePath) => {
  const content = await fsPromises.readFile(filePath, 'utf-8');
  return JSON.parse(content);
});

Scrivere File

// main.js
ipcMain.handle('fs:writeFile', async (_event, filePath, content) => {
  try {
    await fsPromises.writeFile(filePath, content, 'utf-8');
    return { success: true };
  } catch (error) {
    return { success: false, error: error.message };
  }
});

// Aggiungere contenuto a un file
ipcMain.handle('fs:appendFile', async (_event, filePath, content) => {
  await fsPromises.appendFile(filePath, content, 'utf-8');
  return { success: true };
});

// Scrivere JSON formattato
ipcMain.handle('fs:writeJSON', async (_event, filePath, data) => {
  const content = JSON.stringify(data, null, 2);
  await fsPromises.writeFile(filePath, content, 'utf-8');
  return { success: true };
});

Gestire Cartelle

// Leggere il contenuto di una cartella
ipcMain.handle('fs:readDir', async (_event, dirPath) => {
  const entries = await fsPromises.readdir(dirPath, { withFileTypes: true });
  return entries.map((entry) => ({
    name: entry.name,
    isDirectory: entry.isDirectory(),
    isFile: entry.isFile(),
    path: path.join(dirPath, entry.name),
  }));
});

// Creare una cartella
ipcMain.handle('fs:mkdir', async (_event, dirPath) => {
  await fsPromises.mkdir(dirPath, { recursive: true });
  return { success: true };
});

// Eliminare una cartella (con contenuto)
ipcMain.handle('fs:rmdir', async (_event, dirPath) => {
  await fsPromises.rm(dirPath, { recursive: true, force: true });
  return { success: true };
});

Informazioni sui File

ipcMain.handle('fs:stat', async (_event, filePath) => {
  const stats = await fsPromises.stat(filePath);
  return {
    size: stats.size,
    isDirectory: stats.isDirectory(),
    isFile: stats.isFile(),
    created: stats.birthtime,
    modified: stats.mtime,
    accessed: stats.atime,
  };
});

// Verificare se un file esiste
ipcMain.handle('fs:exists', async (_event, filePath) => {
  try {
    await fsPromises.access(filePath);
    return true;
  } catch {
    return false;
  }
});

Rinominare e Spostare

// Rinominare un file
ipcMain.handle('fs:rename', async (_event, oldPath, newPath) => {
  await fsPromises.rename(oldPath, newPath);
  return { success: true };
});

// Copiare un file
ipcMain.handle('fs:copy', async (_event, src, dest) => {
  await fsPromises.copyFile(src, dest);
  return { success: true };
});

// Eliminare un file
ipcMain.handle('fs:delete', async (_event, filePath) => {
  await fsPromises.unlink(filePath);
  return { success: true };
});

Monitorare i Cambiamenti (File Watcher)

const { watch } = require('node:fs');

ipcMain.handle('fs:watch', (_event, dirPath) => {
  const watcher = watch(dirPath, { recursive: true }, (eventType, filename) => {
    // Invia l'evento al renderer
    mainWindow.webContents.send('fs:changed', {
      type: eventType,     // 'rename' o 'change'
      filename: filename,
      path: path.join(dirPath, filename),
    });
  });

  // Salva il watcher per poterlo chiudere
  return 'watching';
});

ipcMain.on('fs:unwatch', () => {
  // Chiudi il watcher
});

Preload e Renderer

// preload.js
contextBridge.exposeInMainWorld('fileSystem', {
  readFile: (path) => ipcRenderer.invoke('fs:readFile', path),
  writeFile: (path, content) => ipcRenderer.invoke('fs:writeFile', path, content),
  readDir: (path) => ipcRenderer.invoke('fs:readDir', path),
  exists: (path) => ipcRenderer.invoke('fs:exists', path),
  stat: (path) => ipcRenderer.invoke('fs:stat', path),
  rename: (oldPath, newPath) => ipcRenderer.invoke('fs:rename', oldPath, newPath),
  delete: (path) => ipcRenderer.invoke('fs:delete', path),
  onFileChanged: (callback) => {
    ipcRenderer.on('fs:changed', (_event, data) => callback(data));
  },
});
// renderer.js
async function loadFile(filePath) {
  const result = await window.fileSystem.readFile(filePath);
  if (result.success) {
    editor.value = result.content;
  } else {
    showError(result.error);
  }
}

async function listFiles(dirPath) {
  const entries = await window.fileSystem.readDir(dirPath);
  entries.forEach((entry) => {
    const icon = entry.isDirectory ? '📁' : '📄';
    console.log(`${icon} ${entry.name}`);
  });
}

Gestione dei Dati dell’Applicazione

Per salvare configurazioni e dati dell’applicazione, usa la cartella userData:

// main.js
const userDataPath = app.getPath('userData');
const configPath = path.join(userDataPath, 'config.json');

// Leggere la configurazione
function loadConfig() {
  try {
    const data = fs.readFileSync(configPath, 'utf-8');
    return JSON.parse(data);
  } catch {
    return {}; // Config di default se il file non esiste
  }
}

// Salvare la configurazione
function saveConfig(config) {
  fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
}

Best Practice

  1. Usa sempre percorsi assoluti — costruiscili con path.join()
  2. Gestisci gli errori — il file system può fallire per permessi, file inesistenti, disco pieno
  3. Preferisci le API asincrone — non bloccare il Main Process con operazioni sincrone
  4. Valida i percorsi — assicurati che i percorsi dal renderer non escano dalla cartella prevista
  5. Usa app.getPath() per i percorsi di sistema — non costruire percorsi hardcoded