image auteur
Lucas BENTO-VERSACE 9min • 26-10-2022

Angular - Upload de fichier

Angular - Upload de fichier

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)">
File input standard

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é.

File input standard
File input standard

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>
file-upload.component.html
1
2
3
4
.file-input {
    display: none;
  }
  ...
file-upload.component.css

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 :

File input customisé
Composant personnalisé d'upload de fichier

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();
    }
  }
}
file-upload.component.ts

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>
file-upload.component.html

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;
  }
}
file-upload.component.html

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>
Appel du composant 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>
Ajout de la propriété 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

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.

Auteur

image auteur
Lucas BENTO-VERSACE Alternant chez Codewise / Étudiant en développement web lucas.bentoversace@ynov.com
"Alternant chez Codewise, passioné par l'informatique et les technologies depuis mon plus jeune âge. J'ai découvert le monde du web et depuis je ne m'en lasse pas ! Fan de jeux vidéos, de sports et de musiques. "