Bienvenue sur CodeWise ! Si vous êtes nouveau ici, vous voudrez sans doute découvrir notre Starter Kit Angular : il contient une roadmap du parcours d'un dev Angular, ainsi qu'un cookbook des commandes les plus utiles et un extrait de formation. Cliquez ici pour le télécharger (c’est gratuit !)
Dans cet article découvrez comment gérer l'upload de fichier avec Angular
Comment uploader des fichiers depuis une app Angular ?
Pour commencer à créer un composant d'upload de fichier, vous devez d'abord comprendre ce qu'il se passe lors d'un upload en HTML et javascript.
Pour uploader un fichier depuis le navigateur en HTML vous devez créez un input de type file :
1
<input type="file" class="file-upload" onchange="console.log(event.target.files)">
Cela permettra d'ouvrir une boîte de dialogue à l'utilisateur pour qu'il selectionne un ou plusieurs fichiers (un seul par défaut).
Le problème avec cette méthode c'est qu'il est très difficile de styliser l'input, en effet certains style qui lui sont appliqués ne peuvent pas être modifié.
Le bouton et l'input ne représentant qu'une balise HTML il est quasi-impossible de customiser cet input avec du CSS. Ce comportement par défaut vient des navigateurs et comme dit précédemment il ne peut être modifié. C'est pourquoi vous ne voyez généralement pas de file input sur les interfaces web que vous utilisez quotidiennement.
L'option la plus courante consiste à masquer l'input à l'utilisateur et construire son propre composant d'upload.
Construire l'interface d'un composant d'upload de fichiers
Comme dit précédemment, ayant l'impossibilité de styliser l'input file, vous devez le cacher à l'utilisateur pour créer un composant à vous qui utilise l'input file dans les coulisses.
1
2
3
4
5
6
7
8
9
10
11
12
<input type="file" class="file-input"
(change)="onFileSelected($event)" #fileUpload>
<div class="file-upload">
{{fileName || "Pas de fichier sélectionné."}}
<button mat-mini-fab color="primary" class="upload-btn"
(click)="fileUpload.click()">
<mat-icon>attach_file</mat-icon>
</button>
</div>
1
2
3
4
.file-input {
display: none;
}
...
En dessous de l'input de fichier caché, il y a une balise div qui contient l'interface que l'utilisateur verra à l'écran.
Pour cet exemple la librairie d'Angular Material est utilisé mais vous pouvez bien entendu styliser comme vous le souhaitez.
Voici un exemple de rendu :
Remarquez que quand vous appuyez sur le bouton, cela ouvre la boîte de dialogue de selection de fichier via fileUpload.click()
.
Uploader un fichier sur le backend
Maintenant vous allez voir comment envoyer une requête HTTP au backend :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Component({
selector: 'file-upload',
templateUrl: "file-upload.component.html",
styleUrls: ["file-upload.component.scss"]
})
export class FileUploadComponent {
fileName = '';
constructor(private http: HttpClient) {}
onFileSelected(event) {
const file: File = event.target.files[0];
if (file) {
this.fileName = file.name;
const formData = new FormData();
formData.append("file", file);
const upload$ = this.http.post("/api/file-upload", formData);
upload$.subscribe();
}
}
}
Dans la méthode onFileSelected()
vous pouvez voir que tout d'abord vous obtenez une référence aux fichiers que l'utilisateur à sélectionné en accédant à la propriété event.target.files
.
Ensuite, voyez la création d'une charge utile de type FormData.
Et pour terminer, l'appel HTTP avec la charge utile à envoyer au backend.
Désormais avec ceci vous avez un composant d'upload de fichier fonctionnel, maintenant voyez dans les prochaines parties comment vous pouvez ajouter de la valeur à ce composant.
Afficher la progression de l'upload de fichier
Vous allez voir comment ajouter une barre de progression de l'upload du fichier. Voyez cet exemple :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<input type="file" class="file-input"
[accept]="requiredFileType"
(change)="onFileSelected($event)" #fileUpload>
<div class="file-upload">
{{fileName || 'No file uploaded yet.'}}
<button mat-mini-fab color="primary" class="upload-btn"
(click)="fileUpload.click()">
<mat-icon>attach_file</mat-icon>
</button>
</div>
<div class="progress">
<mat-progress-bar class="progress-bar" mode="determinate"
[value]="uploadProgress" *ngIf="uploadProgress">
</mat-progress-bar>
<mat-icon class="cancel-upload" (click)="cancelUpload()"
*ngIf="uploadProgress">
delete_forever
</mat-icon>
</div>
Ici, vous remarquez l'implémentation d'une progress-bar Material et d'un bouton d'annulation d'upload.
La progress bar utilise le reportProgress du client HTTP d'Angular. Grâce à ceci vous êtes informé de la progression de l'upload du fichier via les events émis par l'Observable HTTP.
Voici comment cela s'articule côté TS :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
@Component({
selector: 'file-upload',
templateUrl: "file-upload.component.html",
styleUrls: ["file-upload.component.scss"]
})
export class FileUploadComponent {
@Input()
requiredFileType: string;
fileName = '';
uploadProgress: number;
uploadSub: Subscription;
constructor(private http: HttpClient) {}
onFileSelected(event): void {
const file:File = event.target.files[0];
if (file) {
this.fileName = file.name;
const formData = new FormData();
formData.append("file", file);
const upload$ = this.http.post("/api/file-upload", formData, {
reportProgress: true,
observe: 'events'
})
.pipe(
finalize(() => this.reset())
);
this.uploadSub = upload$.subscribe(event => {
if (event.type == HttpEventType.UploadProgress) {
this.uploadProgress = Math.round(100 * (event.loaded / event.total));
}
})
}
}
cancelUpload(): void {
this.uploadSub.unsubscribe();
this.reset();
}
reset(): void {
this.uploadProgress = null;
this.uploadSub = null;
}
}
Dans l'appel HTTP, remarquez que la propriété reportProgress est défini sur true
et 'events' sur la propriété observe. Donc au fur et a mesure que la requête POST se poursuit, vous allez recevoir des objets d'événements signalant la progression de l'envoi.
Il y aura deux types d'événements émis en tant que valeurs de l'observable http$
:
- De type
UploadProgress
qui rapporte le pourcentage de fichier uploadé. - De type
Response
pour signaler que l'upload est terminé.
Avec l'événement de type UploadProgress, le pourcentage d'upload en cours est enregistré dans la propriété uploadProgress
qui est ensuite utilisée pour mettre à jour la bar de progression.
L'opérateur RxJs finalize
va appeler la méthode reset()
dans les deux cas : succès ou échec de l'upload.
Annuler l'upload de fichier en cours
Afin d'annuler l'upload du fichier en cours, il faut conserver une référence à l'objet d'abonnement RxJs que nous obtenons à l'abonnement de l'observable http$.
Dans le composant, cette référence est stockée dans la variable uploadSub
.
Pendant l'upload, l'utilisateur peut décider d'annuler en cliquant sur le bouton. La méthode cancelUpload()
sera appelée et la requête HTTP sera annulée via le désabonnement d'uploadSub.
Comment restreindre l'upload de fichier à un type spécifié
Si vous voulez spécifier un type de fichier à uploader à votre utilisateur, utilisez la propriété requiredFileType du composant :
1
<file-upload requiredFileType="image/png"></file-upload>
Cela sera transmis à la propriété accept
de l'input file. Cela obligera l'utilisateur à sélectionner un fichier png dans la boîte de dialogue.
Comment uploader plusieurs fichiers
Par défaut, la boîte de dialogue de sélection de fichier permet à l'utilisateur de sélectionner un seul fichier.
La propriété multiple
vous permet de sélectionner plusieurs fichiers :
1
<input type="file" class="file-upload" multiple>
Conclusion
La meilleure façon de gérer l'upload de fichier avec Angular est de créer un composant personnalisé. Pense également à utiliser les FormDatas pou envoyer la donnée à un serveur.
J'espère que cet article vous aidera et si vous avez des questions n'hésitez pas à les poser ci-dessous !
Téléchargez votre Starter Kit Angular
En souscrivant vous recevrez :
- Une roadmap d'un développeur Angular
- Un cookbook Angular des commandes les plus utiles
- Un extrait de formation
100% gratuit.