Auparavant, la gestion des fichiers était extrêmement limitée avec le JavaScript, les actions possibles étaient peu intéressantes, à la fois pour le développeur et l'utilisateur. En revanche, le HTML5 fournit maintenant une API nommée « File ». Celle-ci est nettement plus intéressante que ce à quoi nous étions limités avant son implémentation. Il est maintenant possible de manipuler un ou plusieurs fichiers afin de les uploader ou d'obtenir des informations, comme leur poids par exemple.
L'objectif de ce chapitre est de vous fournir un tour d'horizon de l'API File.
L'API que nous allons découvrir n'est pas utilisable seule. Autrement dit, elle nécessite d'être appelée par diverses technologies permettant son accès et lui fournissant les fichiers qu'elle peut manipuler. Cette API a été conçue de cette manière afin d'éviter que ce ne soit vous, développeurs, qui choisissiez quel fichier lire sur l'ordinateur du client. Si cette sécurité n’existait pas, les conséquences pourraient être désastreuses.
Afin de pouvoir utiliser
notre API, il va nous falloir définir comment les fichiers vont pouvoir être choisis par l'utilisateur. La solution
la plus simple pour commencer est l'utilisation d'une balise<input type="file"
/>
, qui va nous permettre d'accéder aux propriétés des fichiers sélectionnés par l'utilisateur. Ces
propriétés constituent une partie de l'API File.
Prenons donc une balise toute simple :
id="file" type="file"
Et ajoutons-lui un événement :
document.querySelector('#file').addEventListener('change', function() {
// Du code…
});
Pour accéder au fichier il va nous
falloir passer par la propriétéfiles
de notre
balise<input>
. Celle-ci va nous permettre d'accéder à une collection
d'objets utilisables de la même manière qu'un tableau, chaque objet représentant un fichier.
Pourquoi une collection d'objets, alors que
notreinput
ne nous permet de sélectionner qu'un seul fichier ?
Eh bien, parce que le HTML5 a ajouté la possibilité de choisir
plusieurs fichiers pour un seul et mêmeinput
! Il vous suffit d'y ajouter
l'attributmultiple
pour que cela soit autorisé au client :
id="file" type="file" multiple
La
propriétéfiles
est la même pour tous, que la sélection de fichiers
soit multiple ou non. Si vous voulez lire le fichier d'un<input>
ne
gérant qu'un seul fichier, alors vous utiliserezfiles[0]
et non
pasfile
.
Maintenant que ce point est éclairci, essayons d'obtenir le nom du fichier sélectionné grâce à la
propriéténame
contenue dans chaque objet de
typeFile
:
document.querySelector('#file').addEventListener('change', function() {
alert(this.files[0].name);
});
Alors, certes, l'utilité est vraiment moindre, mais vous allez vite découvrir de nombreuses possibilités d'utilisation au cours des paragraphes suivants. Ici, nous allons tâcher de vous faire découvrir l'API File, nous aurons donc beaucoup de théorie, mais la mise en pratique viendra après.
Actuellement, notre utilisation de l'API File s'est limitée à
l'obtention du nom du fichier. Cependant, il existe bien plus d'informations que cela grâce aux
objetsBlob
etFile
!
Blob
Un objet de typeBlob
est
une structure représentant des données binaires disponibles uniquement en lecture seule. La plupart du temps, vous
rencontrerez ces objets uniquement lorsque vous manipulerez des fichiers, car ces objets représentent les données
binaires du fichier ciblé.
Concrètement, que pouvons-nous faire avec
unBlob
? Eh bien, pas grand-chose au final… Enfin, pas en l'utilisant seul
tout du moins. Car, bien qu'il soit possible de créer unBlob
par nous-mêmes
(avec l'objet
BlobBuilder
), nous ne le ferons quasiment jamais puisque nous utiliserons ceux créés lors de
la manipulation de fichiers, ce que nous verrons par la suite.
Les objetsBlob
possèdent
deux propriétés nomméessize
ettype
qui
permettent respectivement de récupérer la taille en octets des données manipulées par
leBlob
ainsi que leur
type MIME
.
Il existe également une méthode nomméeslice()
, mais c'est un
sujet bien trop avancé et peu utile. Si vous souhaitez en savoir plus sur cette fonction, nous vous invitons à
consulter
la documentation du MDN
.
File
Les objetsFile
possèdent
un nom bien représentatif puisqu'ils permettent de manipuler les fichiers. Leur particularité est qu'ils héritent
des propriétés et méthodes des objetsBlob
, voilà pourquoi nous ne
créerons quasiment jamais ces derniers par nous-mêmes.
Donc,
en plus des propriétés et méthodes des objetsBlob
, les
objetsFile
possèdent deux propriétés supplémentaires qui
sontname
pour obtenir le nom du fichier
etlastModifiedDate
pour obtenir la date de la dernière modification du
fichier (sous forme d'objetDate
bien évidemment).
Et c'est tout ?
Heureusement que non ! Bien que les
objetsFile
ne soient pas intéressants en terme d'informations, ils le
deviennent soudainement bien plus lorsque l'on commence à aborder leur lecture, grâce aux objets de
typeFileReader
!
Comme précisé précédemment, nous allons aborder la lecture des
fichiers grâce à l'objet
FileReader
. Son instanciation s'effectue sans aucun argument :
var reader = new FileReader();
Cet objet permet la lecture asynchrone de fichiers, et ce grâce à trois méthodes différentes :
Nom |
Description |
---|---|
|
Stocke les données dans un objet de
type |
|
Les données sont converties dans un format
nommé |
|
Les données ne subissent aucune modification, elles sont tout simplement lues puis stockées sous forme d'une chaîne de caractères. |
Ces trois méthodes prennent chacune en paramètre un argument de
typeBlob
ouFile
. La
méthodereadAsText()
possède un argument supplémentaire (et facultatif)
permettant de spécifier l'encodage du fichier, qui s'utilise comme ceci :
reader.readAsText(file, 'UTF-8');
reader.readAsText(file, 'ISO-8859-1');
Avant d'utiliser l'une de ces
méthodes, rappelez-vous que nous avons bien précisé que la lecture d'un fichier est asynchrone ! Il faut donc partir
du principe que vous allez avoir plusieurs événements à votre disposition. Ces événements diffèrent peu de ceux que
l'on rencontre avec la seconde version de l'objetXMLHttpRequest
:
Nom |
Description |
---|---|
|
La lecture vient de commencer. |
|
Tout
comme avec les objets XHR, l'événement |
|
La lecture vient de se terminer avec succès. |
|
La lecture vient de se terminer (avec ou sans succès). |
|
Se
déclenche quand la lecture est interrompue (avec la méthode |
|
Se déclenche quand une erreur a été rencontrée. La
propriété |
Une fois les données lues, il ne vous reste plus qu'à les
récupérer dans la propriétéresult
. Ainsi, afin de lire un fichier
texte, vous n'avez qu'à faire comme ceci :
id="file" type="file"
var fileInput = document.querySelector('#file');
fileInput.addEventListener('change', function() {
var reader = new FileReader();
reader.addEventListener('load', function() {
alert('Contenu du fichier "' + fileInput.files[0].name + '" :\n\n' + reader.result);
});
reader.readAsText(fileInput.files[0]);
});
Pour finir sur la lecture des
fichiers, sachez que l'objetFileReader
possède aussi une
propriétéreadyState
permettant de connaître l'état de la lecture. Il
existe trois états différents représentés par des constantes spécifiques aux
objetsFileReader
:
Constante |
Valeur |
Description |
---|---|---|
EMPTY |
0 |
Aucune donnée n'a encore été chargée. |
LOADING |
1 |
Les données sont en cours de chargement. |
DONE |
2 |
Toutes les données ont été chargées. |
Tout comme avec un objet XHR, vous pouvez vérifier l'état de la lecture, soit avec la constante :
if (reader.readyState === reader.LOADING) {
// La lecture est en cours...
}
soit directement avec la valeur de la constante :
if (reader.readyState === 1) {
// La lecture est en cours...
}
L'étude de l'API File est maintenant terminée. Il est probable que
vous vous demandiez encore ce que nous lui trouvons d'exceptionnel... Eh bien, il est vrai que, si nous l'utilisons
uniquement avec des balises<input>
, alors nous sommes assez limités
dans son utilisation. Ce chapitre couvre la base de l'API File, son utilisation seule, mais il faut savoir que le
principal intérêt de cette API réside en fait dans son utilisation avec d'autres ressources. Ainsi, un petit peu
plus loin dans ce chapitre, nous allons étudier comment l'utiliser conjointement avec
l'objetXMLHttpRequest
afin d'effectuer des uploads ; nous verrons
aussi, dans un chapitre ultérieur, comment s'en servir efficacement avec le Drag & Drop. Bref, ne vous en faites
pas, nous n'en avons pas encore terminé avec cette fameuse API.
Nous allons ici faire une mise en pratique plutôt sympathique de cette API. Le scénario est le suivant : vous souhaitez créer un site d'hébergement d'images interactif. Le principe est simple, l'utilisateur sélectionne les images qu'il souhaite uploader, elles sont alors affichées en prévisualisation sur la page et l'utilisateur n'a plus qu'à cliquer sur le bouton d'upload une fois qu'il aura vérifié qu'il a bien sélectionné les bonnes images.
Notre objectif, ici, est de créer la partie concernant la sélection et la prévisualisation des images, l'upload ne nous intéresse pas. Afin d'obtenir le résultat escompté, nous allons devoir utiliser l'API File, qui va nous permettre de lire le contenu des fichiers avant même d'effectuer un quelconque upload.
Commençons par construire la page HTML qui va accueillir notre script :
id="file" type="file" multiple
id="prev"
Il n'y a pas besoin de plus, nous
avons notre balise<input>
pour sélectionner les fichiers (avec
l'attributmultiple
afin de permettre la sélection de plusieurs fichiers)
ainsi qu'une balise<div>
pour y afficher les images à uploader.
Il nous faut maintenant passer au JavaScript. Commençons par mettre en place la structure principale de notre script :
(function() {
var allowedTypes = ['png', 'jpg', 'jpeg', 'gif'],
fileInput = document.querySelector('#file'),
prev = document.querySelector('#prev');
fileInput.addEventListener('change', function() {
// Analyse des fichiers et création des prévisualisations
});
})();
Ce code déclare les variables et les
événements nécessaires. Vous constaterez qu'il existe une variableallowedTypes
,
celle-ci contient un tableau listant les extensions d'images dont nous autorisons l'upload. L'analyse des fichiers
peut maintenant commencer. Sachant que nous avons autorisé la sélection multiple de fichiers, nous allons devoir
utiliser une boucle afin de parcourir les fichiers sélectionnés. Il nous faudra aussi vérifier quels sont les
fichiers à autoriser :
fileInput.addEventListener('change', function() {
var files = this.files,
filesLen = files.length,
imgType;
for (var i = 0; i < filesLen; i++) {
imgType = files[i].name.split('.');
imgType = imgType[imgType.length - 1].toLowerCase(); // On utilise toLowerCase() pour éviter les extensions en majuscules
if (allowedTypes.indexOf(imgType) != -1) {
// Le fichier est bien une image supportée, il ne reste plus qu'à l'afficher
}
}
});
Les fichiers sont parcourus puis
analysés. Sur les lignes 9 et 10 nous faisons l'extraction de l'extension du fichier en faisant un découpage de la
chaîne de caractères grâce à unsplit('.')
et nous récupérons le
dernier élément du tableau après l'avoir passé en caractères minuscules. Une fois l'extension obtenue, nous
vérifions sa présence dans le tableau des extensions autorisées (ligne 12).
Il nous faut maintenant afficher l'image, comment allons-nous nous y
prendre ? L'affichage d'une image, en HTML, se fait grâce à la
balise<img>
, or celle-ci n'accepte qu'une URL en guise de valeur pour
son attributsrc
. Nous pourrions lui fournir l'adresse du fichier à
afficher, mais nous ne connaissons que son nom, pas son chemin. La réponse se trouve dans
lesDataURL
! Rappelez-vous, nous avions bien précisé que
lesDataURL
permettaient de stocker des données dans une URL, c’est
exactement ce qu'il nous faut ! Tout d'abord, avant de commencer cet affichage, plaçons un appel vers une
fonctioncreateThumbnail()
à la 14e ligne de notre précédent code :
if (allowedTypes.indexOf(imgType) != -1) {
createThumbnail(files[i]);
}
Nous pouvons maintenant passer à la
création de notre fonctioncreateThumbnail()
:
function createThumbnail(file) {
var reader = new FileReader();
reader.addEventListener('load', function() {
// Affichage de l'image
});
reader.readAsDataURL(file);
}
Comme vous pouvez le constater, il n'y
a rien de compliqué là-dedans, nous instancions un objetFileReader
,
lui attribuons un événementload
, puis lançons la lecture du fichier
pour uneDataURL
. Une fois la lecture terminée,
l'événementload
se déclenche si tout s'est terminé correctement, il ne
nous reste donc plus qu'à afficher l'image :
reader.addEventListener('load', function() {
var imgElement = document.createElement('img');
imgElement.style.maxWidth = '150px';
imgElement.style.maxHeight = '150px';
imgElement.src = this.result;
prev.appendChild(imgElement);
});
Et voilà, notre script est terminé ! Vous pouvez l'essayer en ligne et voir les codes complets :
id="file" type="file" multiple
id="prev"
(function() {
function createThumbnail(file) {
var reader = new FileReader();
reader.addEventListener('load', function() {
var imgElement = document.createElement('img');
imgElement.style.maxWidth = '150px';
imgElement.style.maxHeight = '150px';
imgElement.src = this.result;
prev.appendChild(imgElement);
});
reader.readAsDataURL(file);
}
var allowedTypes = ['png', 'jpg', 'jpeg', 'gif'],
fileInput = document.querySelector('#file'),
prev = document.querySelector('#prev');
fileInput.addEventListener('change', function() {
var files = this.files,
filesLen = files.length,
imgType;
for (var i = 0; i < filesLen; i++) {
imgType = files[i].name.split('.');
imgType = imgType[imgType.length - 1];
if (allowedTypes.indexOf(imgType) != -1) {
createThumbnail(files[i]);
}
}
});
})();
Il était auparavant impossible d'uploader des données binaires avec
l'objetXMLHttpRequest
, car celui-ci ne supportait pas l'utilisation
de l'objetFormData
(qui, de toute manière, n'existait pas à cette
époque). Cependant, depuis l'arrivée de ce nouvel objet ainsi que de la deuxième version
duXMLHttpRequest
, cette « prouesse » est maintenant réalisable
facilement.
Ainsi, il est maintenant très simple de créer des
données binaires (grâce à unBlob
) pour les envoyer sur un serveur. En
revanche, il est bien probable que créer vos propres données binaires ne vous intéresse pas, l'upload de fichiers
est nettement plus utile, non ? Alors, ne tardons pas et étudions cela !
Afin d'effectuer un upload de fichiers, il vous faut tout d'abord
récupérer un objet de typeFile
, il nous faut donc
un<input>
:
id="file" type="file"
À cela, ajoutons un code JavaScript
qui récupère le fichier spécifié et s'occupe de créer une requêteXMLHttpRequest
:
var fileInput = document.querySelector('#file');
fileInput.addEventListener('change', function() {
var xhr = new XMLHttpRequest();
xhr.open('POST', 'http://exemple.com'); // Rappelons qu'il est obligatoire d'utiliser la méthode POST quand on souhaite utiliser un FormData
xhr.addEventListener('load', function() {
alert('Upload terminé !');
});
// Upload du fichier…
});
Maintenant, que fait-on ? C'est très
simple, il nous suffit de passer notre objetFile
à un
objetFormData
et d'uploader ce dernier :
var form = new FormData();
form.append('file', fileInput.files[0]);
xhr.send(form);
Et ? C'est tout ?
Plus ou moins. L'upload de fichiers par le biais d'un objet XHR ne va
pas révolutionner votre façon de coder. Il permet juste de simplifier les choses puisque l'on n'a plus à s'embêter à
passer par le biais d'une<iframe>
.
En revanche, il nous reste
encore un petit quelque chose en plus à étudier et cela va sûrement vous intéresser : afficher la progression de
l'upload ! L'objet XHR est déjà nettement plus intéressant, non ?
Nous n'avions pas étudié cela plus tôt, car vous n'auriez pas été
capables de vous en servir de manière utile, mais sachez que l'objet XHR possède une
propriétéupload
donnant accès à plusieurs événements dont
l'événementprogress
. Ce dernier fonctionne exactement de la même
manière que le précédent événementprogress
que nous avions étudié dans
le chapitre consacré à l'objet XHR :
xhr.upload.addEventListener('progress', function(e) {
e.loaded; // Nombre d'octets uploadés
e.total; // Total d'octets à uploader
});
Ainsi, il est facile de faire une
barre de progression avec cet événement et la balise HTML5<progress>
:
id="file" type="file"
id="progress"
var fileInput = document.querySelector('#file'),
progress = document.querySelector('#progress');
fileInput.addEventListener('change', function() {
var xhr = new XMLHttpRequest();
xhr.open('POST', 'upload.html');
xhr.upload.addEventListener('progress', function(e) {
progress.value = e.loaded;
progress.max = e.total;
});
xhr.addEventListener('load', function() {
alert('Upload terminé !');
});
var form = new FormData();
form.append('file', fileInput.files[0]);
xhr.send(form);
});
L'API File
permet de manipuler les fichiers au travers d'un objetFile
qui
hérite lui-même de l'objetBlob
, conçu pour manipuler les données
binaires.
Il est maintenant possible de lire le contenu d'un fichier sans avoir à passer par un quelconque serveur.
Les fichiers
peuvent être utilisés au travers de plusieurs autres technologies telles que l'AJAX ou la
balise<canvas>
.