Lettura di file in JavaScript

La selezione e l'interazione con i file sul dispositivo locale dell'utente è una delle funzionalità più comunemente utilizzate del web. Consente agli utenti di selezionare i file e di caricarli su un server, ad esempio per condividere foto o inviare documenti fiscali. Consente inoltre ai siti di leggerli e manipolarli senza dover trasferire i dati attraverso la rete. In questa pagina viene spiegato come usare JavaScript per interagire con i file.

La moderna API File System Access

L'API File System Access fornisce un modo per leggere e scrivere su file e directory nel sistema locale dell'utente. È disponibile nella maggior parte dei browser basati su Chromium, come Chrome ed Edge. Per scoprire di più, consulta L'API File System Access.

Poiché l'API File System Access non è compatibile con tutti i browser, consigliamo di utilizzare browser-fs-access, una libreria helper che utilizza la nuova API ovunque sia disponibile e, quando non lo è, torna agli approcci precedenti.

Lavora con i file nel modo classico

Questa guida mostra come interagire con i file utilizzando i metodi JavaScript precedenti.

Seleziona file

Esistono due modi principali per selezionare i file: utilizzando l'elemento di input HTML e utilizzando una zona di trascinamento.

Elemento di input HTML

Il modo più semplice per gli utenti di selezionare i file è utilizzare l'elemento <input type="file">, supportato in tutti i principali browser. Una volta selezionato, consente a un utente di selezionare uno o più file se è incluso l'attributo multiple, utilizzando l'interfaccia utente di selezione dei file integrata del suo sistema operativo. Quando l'utente completa la selezione di uno o più file, l'evento change dell'elemento viene attivato. Puoi accedere all'elenco dei file da event.target.files, che è un oggetto FileList. Ogni elemento in FileList è un oggetto File.

<!-- The `multiple` attribute lets users select multiple files. -->
<input type="file" id="file-selector" multiple>
<script>
  const fileSelector = document.getElementById('file-selector');
  fileSelector.addEventListener('change', (event) => {
    const fileList = event.target.files;
    console.log(fileList);
  });
</script>

L'esempio seguente consente a un utente di selezionare più file utilizzando l'interfaccia utente integrata di selezione dei file del sistema operativo, quindi registra ogni file selezionato nella console.

Limita i tipi di file che gli utenti possono selezionare

In alcuni casi è consigliabile limitare i tipi di file che gli utenti possono selezionare. Ad esempio, un'app di modifica di immagini deve accettare solo immagini, non file di testo. Per impostare limitazioni per i tipi di file, aggiungi un attributo accept all'elemento di input per specificare quali tipi di file sono accettati:

<input type="file" id="file-selector" accept=".jpg, .jpeg, .png">

Trascinamento personalizzato

In alcuni browser, l'elemento <input type="file"> è anche una destinazione di rilascio, che consente agli utenti di trascinare file nell'app. Tuttavia, questo target di rilascio è piccolo e può essere difficile da utilizzare. Dopo aver fornito le funzionalità principali utilizzando un elemento <input type="file">, puoi fornire un'ampia superficie di trascinamento personalizzata.

Scegli la tua zona di rilascio

La superficie di rilascio dipende dalla progettazione dell'applicazione. Solo una parte della finestra deve essere una superficie di caduta, ma puoi utilizzare l'intera finestra.

Uno screenshot di Squoosh, un&#39;app web di compressione delle immagini.
Squoosh trasforma l'intera finestra in una zona di rilascio.

L'app di compressione delle immagini Squoosh consente all'utente di trascinare un'immagine in qualsiasi punto della finestra e fare clic su seleziona un'immagine per richiamare l'elemento <input type="file">. Qualunque sia la tua zona di rilascio, assicurati che sia chiaro all'utente che può trascinare i file su quella superficie.

Definisci la zona di rilascio

Per attivare un elemento come zona di trascinamento, crea listener per due eventi: dragover e drop. L'evento dragover aggiorna l'interfaccia utente del browser per indicare visivamente che l'azione di trascinamento sta creando una copia del file. L'evento drop viene attivato dopo che l'utente rilascia i file in superficie. Come per l'elemento di input, puoi accedere all'elenco di file da event.dataTransfer.files, che è un oggetto FileList. Ogni elemento in FileList è un oggetto File.

const dropArea = document.getElementById('drop-area');

dropArea.addEventListener('dragover', (event) => {
  event.stopPropagation();
  event.preventDefault();
  // Style the drag-and-drop as a "copy file" operation.
  event.dataTransfer.dropEffect = 'copy';
});

dropArea.addEventListener('drop', (event) => {
  event.stopPropagation();
  event.preventDefault();
  const fileList = event.dataTransfer.files;
  console.log(fileList);
});

event.stopPropagation() e event.preventDefault() interrompono il comportamento predefinito del browser e consentono l'esecuzione del tuo codice. Senza i cookie, il browser uscirà dalla pagina e aprirebbe i file che l'utente ha scaricato nella finestra del browser.

Per una dimostrazione dal vivo, consulta l'articolo Trascinamento personalizzato.

E le directory?

Sfortunatamente, non esiste un buon modo per accedere a una directory utilizzando JavaScript.

L'attributo webkitdirectory nell'elemento <input type="file"> consente all'utente di scegliere una o più directory. È supportata nella maggior parte dei browser principali, ad eccezione di Firefox per Android e Safari su iOS.

Se il trascinamento è abilitato, un utente potrebbe provare a trascinare una directory nella zona di rilascio. Quando si attiva l'evento di rilascio, include un oggetto File per la directory, ma non fornisce l'accesso a nessun file nella directory.

Leggi i metadati dei file

L'oggetto File contiene metadati relativi al file. La maggior parte dei browser fornisce il nome del file, le dimensioni del file e il tipo MIME; tuttavia, a seconda della piattaforma, diversi browser potrebbero fornire informazioni diverse o aggiuntive.

function getMetadataForFileList(fileList) {
  for (const file of fileList) {
    // Not supported in Safari for iOS.
    const name = file.name ? file.name : 'NOT SUPPORTED';
    // Not supported in Firefox for Android or Opera for Android.
    const type = file.type ? file.type : 'NOT SUPPORTED';
    // Unknown cross-browser support.
    const size = file.size ? file.size : 'NOT SUPPORTED';
    console.log({file, name, type, size});
  }
}

Puoi vedere come funziona nella demo di input-type-file.

Leggere i contenuti di un file

Utilizza FileReader per leggere i contenuti di un oggetto File in memoria. Puoi dire a FileReader di leggere un file come buffer di array, URL di dati o testo:

function readImage(file) {
  // Check if the file is an image.
  if (file.type && !file.type.startsWith('image/')) {
    console.log('File is not an image.', file.type, file);
    return;
  }

  const reader = new FileReader();
  reader.addEventListener('load', (event) => {
    img.src = event.target.result;
  });
  reader.readAsDataURL(file);
}

Questo esempio legge un valore File fornito dall'utente e poi lo converte in un URL dei dati e utilizza questo URL per visualizzare l'immagine in un elemento img. Per scoprire come verificare che l'utente abbia selezionato un file immagine, guarda la demo di read-image-file.

Monitorare l'avanzamento della lettura di un file

Durante la lettura di file di grandi dimensioni, può essere utile fornire un'esperienza utente per indicare all'utente lo stato di avanzamento della lettura. In questo caso, utilizza l'evento progress fornito da FileReader. L'evento progress ha due proprietà: loaded (la quantità letta) e total (la quantità da leggere).

function readFile(file) {
  const reader = new FileReader();
  reader.addEventListener('load', (event) => {
    const result = event.target.result;
    // Do something with result
  });

  reader.addEventListener('progress', (event) => {
    if (event.loaded && event.total) {
      const percent = (event.loaded / event.total) * 100;
      console.log(`Progress: ${Math.round(percent)}`);
    }
  });
  reader.readAsDataURL(file);
}

Immagine hero di Vincent Botta da Unsplash