Chargez des images avec NodeJS

Vous savez sans doute charger des images en PHP, mais savez vous le faire en NodeJS? On petu être perdu dans on aborde une nouvelle technologie. Nous allons voir comment le faire avec NodeJs. Le principe est un peu le même, on envoit un binaire via hTTP depuis la pageHTML, et côté serveur on récupère le fichier, mais là vous devez faire un peu plus de choses.

Le code de la page HTML

On fera au plus simple, un formulaire, avec une chose d’importance, l’envoi doit se faire en multipart/form-data. Qu’est ce que c’est? Lorsque vous avez un formulaire simple qui envoit des caractères alphanumériques, vous ne spécifiez pas le type d’envoi (enctype)

<form action="/submit" method="POST">
  <input type="text" name="username" value="example_user">
  <input type="text" name="age" value="30">
  <button type="submit">Submit</button>
</form>

Le format urlencode

En fait il y a un enctype par défaut qui est application/x-www-form-urlencoded, cela encode les données des champs sous forme de paire clé-valeur comme dans une url, ce qui le fait ressembler à un querystring. Les caractères spéciaux sont remplacé, par exemple le caractère space est remplacé par %20, les successions de paries clé-valeur sont liées par le signe esperluette « & ». Le request body est :

id=3&nom=antoine

C’est le mode préféré quand on n’a pas de fichier à uploader.

Ensuite si on veut envoyer des fichier binaires, on choisit l’enctype multipart/form-data.

Le format multipart/form-data

Utilisez ce format pour l’nevoi de fichiers. Si vous faites un envoi en multipar, observez le header envoyé, il a la forme suivante :

POST /upload HTTP/1.1
Host: example.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

Boundary veut dire limite ou frontière en anglais. Vos données envoyée se font par troçon délimité par la frontière.

Le request body est le suivant

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="username"

example_user
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="example.jpg"
Content-Type: image/jpeg

<binary content of the file>
------WebKitFormBoundary7MA4YWxkTrZu0gW--

Les données envoyée sont délimitées par la chaine « boundary=–« . On utilise ce mode quand il y a u nmix de texte et de fichier binaire.

Le format text/plain

username=example_user
age=30

Ce format est plus rare, et vous n’avez pas besoin de l’utiliser. Il envoit les données sans encoder

Mise en oeuvre du projet

Il va falloir installer NodeJs, avec la librairie multer our récupérer les binaires. Le fichiet HTML

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css">
    <title>Document</title>
</head>

<body>
    <div class="container">
        <h1>File Upload</h1>
        <form id='form'>
            <div class="input-group">
                <label for='name'>Your name</label>
                <input name='name' id='name' placeholder="Enter your name" />
            </div>
            <div class="input-group">
                <label for='files'>Select files</label>
                <input id='files' type="file" multiple>
            </div>
            <button class="submit-btn" type='submit'>Upload</button>
        </form>
    </div>
    <script src='./script.js'></script>
</body>

</html>

Le fichier JS front end

const form = document.getElementById("form");

form.addEventListener("submit", submitForm);

function submitForm(e) {
    e.preventDefault();
    const name = document.getElementById("name");
    const files = document.getElementById("files");
    const formData = new FormData();
    formData.append("name", name.value);
    for (let i = 0; i < files.files.length; i++) {
        formData.append("files", files.files[i]);
    }
    fetch("http://localhost:3000/upload_files", {
        method: 'POST',
        body: formData
    })
        .then((res) => console.log(res))
        .catch((err) => console.log("Error occured", err));
}

Le fichier style

/* style.css */
body {
    background-color: rgb(6, 26, 27);
}

* {
    box-sizing: border-box;
}

.container {
    max-width: 500px;
    margin: 60px auto;
}

.container h1 {
    text-align: center;
    color: white;
}

form {
    background-color: white;
    padding: 30px;
}

form .input-group {
    margin-bottom: 15px;
}

form label {
    display: block;
    margin-bottom: 10px;
}

form input {
    padding: 12px 20px;
    width: 100%;
    border: 1px solid #ccc;
}

.submit-btn {
    width: 100%;
    border: none;
    background: rgb(37, 83, 3);
    font-size: 18px;
    color: white;
    border-radius: 3px;
    padding: 20px;
    text-align: center;
}

Le fichier server.js

//https://blog.logrocket.com/multer-nodejs-express-upload-file/

const express = require('express');
const app = express();
const multer = require('multer');
// configuration de multer
const storage = multer.diskStorage({
    destination: function (req, file, cb) {
        cb(null, 'uploads/')
    },
    filename: function (req, file, cb) {
        cb(null, file.originalname)
    }
})
const upload = multer({
    storage: storage
})


const path = require('path');
app.use(express.static(path.join(__dirname, './')))


app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// app.post('/upload_files', (req, res) => {
//     console.log(req.body);
// })
app.get('/', (req, res) => {
    res.sendFile(__dirname + '/index.html');
})

app.post('/upload_files', upload.array("files"), (req, res) => {
    console.log(req.files);
    console.log(req.body);
    res.json({ message: "Files uploaded successfully" });
})

app.listen(3000, () => {
    console.log("Server is running on port 3000");
})

J’ai fourni les scripts, je commenterai le fichier server.js puisque tout se joue dans ce fichier.

  • on utilise multer pour prendre en charge l’upload de fichier,
  • la variable storage paramètre le fonctionnement du multer, répertoire de sauvegarde des fichiers, nommage des fichiers
  • Pour servir index.html, on utiliser express.static, notez l’argument de path.join qui spécifie le répertoire du fichier index.html. express.static permet de servir les fichiers statiques (css, texte,html,javascript), il faut le dire explicitement.
  • app.use(express.urlencoded({ extended: true })); est un middleware, qui va attacher à req.body (le corps de la requête) la parsing des éléments envoyé par le formulaire avec un enctype=application/x-www-form-urlencoded. Concernant extended:true, c’est juste un choix de librairie de parsing, si à true, va utiliser la librairie qs qui supporte la récursivité des objets, alors que extende:false va utiliser la librairie querystring, qui ne supporte par la récursivité.
app.use(express.urlencoded({ extended: true}));
{
  "username": "JohnDoe",
  "details": {
    "age": "30"
  }
}

app.use(express.urlencoded({ extended: false }));
{
  "username": "JohnDoe",
  "details[age]": "30"
}

Facilitons nous la vie avec nodemon

J’utilise systématiquement nodemon pour redémarrer le serveur nodeJS quand il y a une modification de fichier, c’est très pratique pour développer, on n’ pas besoin de redémarrer manuellmenet à chaque fois en ligne de commande : node server.js, à la place on fait nodemon server.js.

La librairie multer

Il y a beaucoup plus d’option sur la librairie multer que je n’ai montré dans ce post, je vous renvois vers la documentation officielle de multer. Multer est un middleware d’express, qui permet de sauvegarder les fichiers binaires envoyés. Autres propriétés intéressant : mimetype, size, originalName, fileFilter pour filtrer les types de fichier.

Retour en haut