Upload JS et File API

Alexandre Niveau
GREYC — Université de Caen
  • attention, les ex-L3 n'ont pas eu le cours de web service et donc CORS
  • expliquer intérêt des typedarray en pratique. les typedarray ne sont qu'une vue sur le arraybuffer.
  • formdata: on peut hériter d'un formulaire existant. ???
  • regarder les new tricks in XHR2 (et les docs de MDN) dans les liens: ajouter ce qui est intéressant au cours

Manipuler des fichiers en JS

  • L'API « File » permet de manipuler depuis le navigateur des fichiers fournis par l'utilisateur
  • On peut notamment les envoyer au serveur, mais aussi les modifier depuis le client

File et Blob

  • Le type Blob représente un tas de données (binaires)
    • on peut récupérer sa taille et son type MIME
    • pour le manipuler il faut utiliser d'autres outils
  • Le type File étend Blob en ajoutant le nom du fichier et la date de dernière modification

Construire un objet File ou Blob

  • Les widgets d'upload ont un attribut files qui contient une liste d'instances de File correspondant aux fichiers sélectionnés par l'internaute
  • On peut récupérer un fichier sur le serveur en Ajax
    • valeur blob pour l'attribut responseType de XHR
  • On peut utiliser le constructeur de Blob (voir docs)

FileReader

  • Pour accéder au contenu d'un fichier (ou d'un blob), on peut utiliser un FileReader
  • Le FileReader permet de récupérer le contenu sous trois formes :
    • sous forme de chaîne de caractères, avec readAsText. Si l'encodage n'est pas UTF-8, il faut le spécifier.
    • sous forme de ArrayBuffer avec readAsArrayBuffer (voir plus loin)
    • sous forme de data-URL avec readAsDataURL (voir plus loin)
  • Attention, ces trois méthodes sont asynchrones : quand le travail est terminé, le FileReader renvoie l'événement load et le résultat est récupérable dans son attribut result.

ArrayBuffer

  • Un tableau d'octets… sauf qu'on ne peut pas accéder directement au contenu
  • Il faut le convertir en un des TypedArray,
  • Un TypedArray est une vue sur le tableau d'octets qui dépend de comment les octets doivent être interprétés : entiers signés ou non, flottants, 8 bits, 16 bits, 32 bits…
  • Exemple de création de Uint8Array : let toto = new Uint8Array(buffer);, avec buffer une instance de ArrayBuffer.

Data-URL

  • Une data-URL est une URL vers un fichier dont le contenu (binaire) est défini par l'URL elle-même
  • L'URL ne pointe donc pas vers un emplacement : elle contient elle-même le fichier vers lequel elle « pointe »
  • Syntaxe :
    data:image/gif;base64,R0lGODlhCgAMAPABAAAAAP///yH5BAAAAAAAIf8LSW1hZ2VNYWdpY2sOZ2FtbWE9MC40NTQ1NDUALAAAAAAKAAwAAAIVBIIZpnpuGHqJNleb1bevK03esk0FADs=
  • Le type MIME est optionnel (text/plain;charset=US-ASCII par défaut)
  • L'encodage est optionnel, on peut mettre directement le contenu si celui-ci est textuel (en remplaçant les caractères spéciaux via l'encodage URL). Si le contenu est binaire, on utilise la base 64.
  • Exemples :
    • l'URL précédente représente une image GIF
    • l'URL data:,coucou représente un fichier texte dont le contenu est « coucou ».
    • l'URL data:text/html,<script>alert('hi');</script> représente un document HTML qui produit une alerte JS.
  • Les data-URL sont notamment utilisées pour embarquer de petites images (type icônes) directement dans du HTML
  • La méthode readAsDataURL du FileReader renvoie donc le fichier sous forme de data-URL. Ça peut être pratique pour des images, mais les data-URL deviennent vite très longues…
    • si besoin d'une URL vers le fichier, il est beaucoup plus adapté d'utiliser une blob-URL

Blob-URL

  • Pour récupérer le fichier manipulé en mémoire par JS, on peut utiliser une blob URL
  • window.URL.createObjectURL(mon_blob) : renvoie une URL ressemblant à blob:https://ensweb.users.info.unicaen.fr/a5748e7f-4213-4adc-a134-a206af015299, la dernière partie étant un identifiant unique
  • Cette URL fait référence au blob en question. On peut l'utiliser pour télécharger le fichier (en faisant un lien vers l'URL) ou pour ajouter l'image dans la page (si le blob est une image)
    • mêmes utilisations que les data-URL donc, sauf que la longueur de l'URL est fixée (elle ne contient pas de données, mais un simple identifiant)
    • … cependant, une blob-URL n'est valable que tant que la page n'est pas fermée
  • Attention, les blob-URL prennent de la mémoire : si vous en créez plusieurs, libérez les précédents avec window.URL.revokeObjectURL(mon_url)
    • Si vous avez généré l'URL juste pour ajouter l'image à la page, vous pouvez la révoquer immédiatement après — la révocation ne supprime pas l'image.

POST en Ajax

  • XHR permet de faire des requêtes POST
  • Le corps de la requête est passé en paramètre à send()
  • On peut envoyer une chaîne de caractères, mais aussi et surtout une instance de FormData, classe qui permet de construire facilement des couples clef-valeur (en partant ou non d'un formulaire HTML existant)
    On peut aussi envoyer un blob comme corps de la requête, mais il faut que le serveur sache quoi en faire (par défaut les serveurs attendent des POST formatés avec des formulaires)
  • Exemple : supposons que la page contienne un formulaire d'identifiant toto. Le code suivant le soumet via JS :
    let formElement = document.getElementById('toto');
    let formData = new FormData(formElement);
    let xhr = new XMLHttpRequest();
    xhr.open('POST', 'recup.php');
    xhr.send(formData);
  • Soumettre le formulaire avec XHR permet notamment de suivre la transmission : XHR a un attribut upload qui reçoit régulièrement des événements progress
  • Ces événements indiquent la proportion du formulaire qui a déjà été reçue par le serveur — utile si le formulaire contient un ou plusieurs fichiers
  • On peut utiliser l'élément HTML progress pour afficher la progression
  • Attention, il faut que le formulaire soit utilisable sans JS ! Pas question de rendre la page moins accessible juste pour ajouter une barre de progression…