Lettura di file in JavaScript

La selezione e l'interazione con i file sul dispositivo locale dell'utente è una delle funzionalità di uso comune del web. Consente agli utenti di selezionare i file e caricarli su un server, ad esempio quando condividono foto o inviano documenti fiscali. Consente inoltre ai siti di leggerli e manipolarli senza dover mai trasferire i dati sulla rete. Questa pagina illustra come utilizzare JavaScript per interagire con i file.

La moderna API File System Access

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

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

Lavorare con i file, il modo classico

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

Selezionare file

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

Elemento di input HTML

Il modo più semplice per gli utenti di selezionare i file è utilizzare l' <input type="file"> elemento, supportato in tutti i principali browser. Quando viene fatto clic, consente a un utente di selezionare un file o più file, se è incluso l'attributo multiple, utilizzando l'interfaccia utente di selezione file integrata del sistema operativo. Quando l'utente finisce di selezionare uno o più file, viene attivato l'evento change dell'elemento. Puoi accedere all'elenco dei file da event.target.files, che è un FileList oggetto. 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 di selezione file integrata del sistema operativo e poi registra ogni file selezionato nella console.

Limitare i tipi di file che gli utenti possono selezionare

In alcuni casi, potresti voler limitare i tipi di file che gli utenti possono selezionare. Ad esempio, un'app di modifica delle immagini deve accettare solo immagini, non file di testo. Per impostare le limitazioni del tipo di file, aggiungi un accept attributo all'elemento di input per specificare i tipi di file 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 i file nella tua app. Tuttavia, questa destinazione di rilascio è piccola e può essere difficile da utilizzare. In alternativa, dopo aver fornito le funzionalità di base utilizzando un <input type="file"> elemento, puoi fornire una superficie di trascinamento personalizzata di grandi dimensioni.

Scegliere la zona di rilascio

La superficie di rilascio dipende dalla progettazione dell'applicazione. Potresti voler che solo una parte della finestra sia una superficie di rilascio, ma puoi utilizzare l'intera finestra.

Screenshot di Squoosh, un'app web per la compressione delle immagini.
Squoosh rende l'intera finestra una zona di rilascio.

L'app di compressione delle immagini Squoosh consente all'utente di trascinare un'immagine ovunque nella finestra e fare clic su Seleziona un'immagine per richiamare l'elemento <input type="file">. Qualunque cosa tu scelga come zona di rilascio, assicurati che sia chiaro all'utente che può trascinare i file su quella superficie.

Definire la zona di rilascio

Per abilitare 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 sulla superficie. Come per l'elemento di input, puoi accedere all'elenco dei file da event.dataTransfer.files, che è un FileList oggetto. Ogni elemento in FileList è un File oggetto.

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 codice. In caso contrario, il browser uscirebbe dalla pagina e aprirebbe i file rilasciati dall'utente nella finestra del browser.

Per una dimostrazione dal vivo, consulta Trascinamento personalizzato.

E le directory?

Purtroppo, non esiste un modo efficace per accedere a una directory utilizzando JavaScript.

L'webkitdirectory attributo dell'elemento <input type="file"> consente all'utente di scegliere una o più directory. È supportato nella maggior parte dei principali browser 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 viene attivato l'evento di rilascio, include un oggetto File per la directory, ma non fornisce l'accesso a nessuno dei file nella directory.

Leggere i metadati dei file

L'oggetto File contiene i metadati del file. La maggior parte dei browser fornisce il nome file, le dimensioni del file e il tipo MIME, anche se, a seconda della piattaforma, browser diversi 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 questa funzionalità in azione nella input-type-file demo.

Leggere i contenuti di un file

Utilizza FileReader per leggere i contenuti di un oggetto File in memoria. Puoi indicare a FileReader di leggere un file come un buffer di array, un URL 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 File fornito dall'utente, lo converte in un URL dati e utilizza questo URL dati per visualizzare l'immagine in un elemento img. Per scoprire come verificare che l'utente abbia selezionato un file immagine, consulta la read-image-file demo.

Monitorare l'avanzamento della lettura di un file

Quando si leggono file di grandi dimensioni, può essere utile fornire un'esperienza utente per comunicare all'utente l'avanzamento della lettura. A questo scopo, utilizza l' progress evento 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);
}