Sommario
< Home
Stampa

Upload di files con NodeJS

NodeJS fornisce accesso al filesystem e (in modo semplificato con Express) è possibile erogare qualsiasi tipo di applicazione Web lato server, quindi sia siti web statici (grazie ad Express.static) sia siti web dinamici (grazie a middleware specifici che eseguono la render di una pagina dinamica lato server) sia infine Web Services, creando un middleware di gestione dati direttamente in Express.

In questa lezione vedremo come NodeJs, Express e una nuova libreria, Multer, possiamo eseguire l’upload di file da una applicazione Javascript e quindi utilizzare il Web Server come file server.

Inizializzazione

Prima di tutto creiamo il progetto ed installiamo le librerie Express e Multer

npm init
npm install express
npm install multer

Lato client

Inserire una pagina web come questa (la creiamo in una cartella /public nel progetto):

<!DOCTYPE html>
<head>
   <title>UPLOAD</title>
   <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"
      integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
   <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.min.js"
      integrity="sha384-BBtl+eGJRgqQAUMxJ7pMwbEyER4l1g+O15P+16Ep7Q9Q+zqX6gSbd85u4mG4QzX+"
      crossorigin="anonymous"></script>
</head>
<body>
   <div class="container">
      <div class="row">
            <input id="file" name="file" type="file" single>
            <button id="button" type="button" class="btn btn-success">Upload</button>        
      </div>
      <div class="row">
         <a id="link"></a>
      </div>
   </div>
   <script src="./index.js"></script>
</body>
</html>

E’ previsto un tag di input di tipo file e un pulsante di submit. E’ prevista poi una ancora che conterrà il link al file di cui si è eseguito l’upload. Creiamo quindi l’applicazione Javascript che esegue l’upload.

(async () => {
  const inputFile = document.querySelector('#file');
  const button = document.querySelector("#button");
  const link = document.querySelector("#link");

  handleSubmit = async (event) => {
    const formData = new FormData();
    formData.append("file", inputFile.files[0]);
    const body = formData;
    body.description = inputDescription.value;
    const fetchOptions = {
      method: 'post',
      body: body
    };
    try {
      const res = await fetch("/upload", fetchOptions);
      const data = await res.json();
      link.setAttribute("href", data.url);
      link.innerText = data.url;
    } catch (e) {
      console.log(e);
    }
  }

  button.onclick = handleSubmit;
})();

La funzione è async perché prevede l’uso di async-await. Il formato della richiesta http è form data, e viene gestito internamente dalla fetch API. Il formato di risposta è JSON e sarà nel formato:

{
  "url": "./files/xxxxxx"
}

In pratica il web service di upload riceverà il file, lo salverà col suo nome, e lo renderà staticamente disponibile (vedremo sotto) alla URL indicata. Con questi dati la callback lato client è in grado di popolare nome e URL nel link generato e rendere quindi accedere al file appena caricato.

Lato server

Lato server creiamo una cartella files e nella root del progetto creiamo un file server.js.

const express = require("express");
const http = require('http');
const path = require('path');
const app = express();
const multer  = require('multer');
var storage = multer.diskStorage({
    destination: function (req, file, callback) {
        callback(null, path.join(__dirname, "files"));
    },
    filename: function (req, file, callback) {
        callback(null, file.originalname);
    }
});
const upload = multer({ storage: storage}).single('file');

app.use("/", express.static(path.join(__dirname, "public")));
app.use("/files", express.static(path.join(__dirname, "files")));
app.post('/upload', (req, res) => {
    upload(req, res, (err) => {
        
    console.log(req.file.filename);    
    res.json({url: "./files/" + req.file.filename});    
  })
});

const server = http.createServer(app);
server.listen(5600, () => {
  console.log("- server running");
});

Come si può vedere la libreria multer va configurata creando un oggetto “storage” che indica dove saranno salvati i files e una funzione upload che configura multer per accettare file in upload. A questo punto vengono creati due folder statici (uno per html/js e l’altro per i files di upload) e viene configurato il web service “/upload” che esegue la funzione di salvataggio del file contenuto nel body della Http Request.

Si ricorda che un upload di file in HTTP consiste in una richiesta POST che nel body contiene i dati (in formato binario) del contenuto del file in upload. Il formato del body prevede anche campi di metadati, come il nome del file. Lato server quindi l’oggetto req del middlware di Express contiene le informazioni mandate dal client. L’intero codice che crea il binario e lo salva è quindi implementato lato client dalla fetch API e lato server dalla libreria Multer.

Al nostro programma non resta altro che comporre la url dove sarà depositato il file ed inviarla al client sotto forma di JSON nel formato sopra indicato.

Conclusioni

A questo punto non resta che eseguire l’applicativo ed eseguire un upload di un file (es. una immagine o un PDF). Si vedrà che il file viene caricato nella cartella “/files” e sarà reso disponibile all’utente alla URL indicata.

Esercizio

Modificare il progetto sopra indicato nel seguente modo:

  • creare un web service “/filelist” che restituisce una lista (JSON array) con la url di tutti i files presenti nella cartella. Usare la libreria FS e Path.
  • Modificare la pagina web affinché all’avvio e dopo ogni upload carichi la lista dei files e la mostri in un elenco puntato (ul) di link.