Téléchargement de fichiers en angulaire?

173

Je sais que c'est une question très générale mais je ne parviens pas à télécharger un fichier dans Angular 2. J'ai essayé

1) http://valor-software.com/ng2-file-upload/ et

2) http://ng2-uploader.com/home

...mais a échoué. Quelqu'un a-t-il téléchargé un fichier dans Angular? Quelle méthode avez-vous utilisée? Comment faire cela? Si un exemple de code ou un lien de démonstration est fourni, il sera vraiment apprécié.

heman123
la source

Réponses:

375

Angular 2 fournit un bon support pour le téléchargement de fichiers. Aucune bibliothèque tierce n'est requise.

<input type="file" (change)="fileChange($event)" placeholder="Upload file" accept=".pdf,.doc,.docx">
fileChange(event) {
    let fileList: FileList = event.target.files;
    if(fileList.length > 0) {
        let file: File = fileList[0];
        let formData:FormData = new FormData();
        formData.append('uploadFile', file, file.name);
        let headers = new Headers();
        /** In Angular 5, including the header Content-Type can invalidate your request */
        headers.append('Content-Type', 'multipart/form-data');
        headers.append('Accept', 'application/json');
        let options = new RequestOptions({ headers: headers });
        this.http.post(`${this.apiEndPoint}`, formData, options)
            .map(res => res.json())
            .catch(error => Observable.throw(error))
            .subscribe(
                data => console.log('success'),
                error => console.log(error)
            )
    }
}

en utilisant @ angular / core ":" ~ 2.0.0 "et @ angular / http:" ~ 2.0.0 "

Eswar
la source
5
cela ne fonctionne pas, du moins dans mon cas. Le serveur sailsJs reçoit un tableau / objet de fichiers vide
Kaleem Ullah
20
Cela a fonctionné pour moi sauf - j'ai dû travailler sur cette ligne - headers.append('enctype', 'multipart/form-data');(utilisé «enctype» pour remplacer «Content-Type»). Cela dépend peut-être du code côté serveur. (ie api)
Ariful Islam
29
Soyez génial si l'équipe Angular écrivait de la documentation sur le sujet, je ne trouve pas une seule ligne à ce sujet dans leur documentation. Cet exemple de code est obsolète et ne fonctionne pas avec v4 +.
Rob B
10
Remarque pour certains serveurs d'applications, la définition du type de contenu sera rejetée. Vous devez le laisser vide: let headers = new Headers (); Le navigateur réglera tout pour vous.
PeterS
6
LMFAO a eu du mal 20 minutes avec cette merde jusqu'à ce que je réalise que je n'avais pas du tout besoin de définir les en-têtes. Remarque pour les autres utilisant angular 4.xx avec .Net webapi, n'essayez pas de définir les en-têtes! Merci d'avoir souligné cela @PeterS
Jota.Toledo
76

À partir des réponses ci-dessus, je construis cela avec Angular 5.x

Appelez simplement uploadFile(url, file).subscribe()pour déclencher un téléchargement

import { Injectable } from '@angular/core';
import {HttpClient, HttpParams, HttpRequest, HttpEvent} from '@angular/common/http';
import {Observable} from "rxjs";

@Injectable()
export class UploadService {

  constructor(private http: HttpClient) { }

  // file from event.target.files[0]
  uploadFile(url: string, file: File): Observable<HttpEvent<any>> {

    let formData = new FormData();
    formData.append('upload', file);

    let params = new HttpParams();

    const options = {
      params: params,
      reportProgress: true,
    };

    const req = new HttpRequest('POST', url, formData, options);
    return this.http.request(req);
  }
}

Utilisez-le comme ça dans votre composant

  // At the drag drop area
  // (drop)="onDropFile($event)"
  onDropFile(event: DragEvent) {
    event.preventDefault();
    this.uploadFile(event.dataTransfer.files);
  }

  // At the drag drop area
  // (dragover)="onDragOverFile($event)"
  onDragOverFile(event) {
    event.stopPropagation();
    event.preventDefault();
  }

  // At the file input element
  // (change)="selectFile($event)"
  selectFile(event) {
    this.uploadFile(event.target.files);
  }

  uploadFile(files: FileList) {
    if (files.length == 0) {
      console.log("No file selected!");
      return

    }
    let file: File = files[0];

    this.upload.uploadFile(this.appCfg.baseUrl + "/api/flash/upload", file)
      .subscribe(
        event => {
          if (event.type == HttpEventType.UploadProgress) {
            const percentDone = Math.round(100 * event.loaded / event.total);
            console.log(`File is ${percentDone}% loaded.`);
          } else if (event instanceof HttpResponse) {
            console.log('File is completely loaded!');
          }
        },
        (err) => {
          console.log("Upload Error:", err);
        }, () => {
          console.log("Upload done");
        }
      )
  }
Tarion
la source
6
Fonctionne très bien avec Angular6. Je vous remercie. Et vous avez besoin de ces bibliothèques pour importer. import {HttpClient, HttpParams, HttpRequest, HttpEvent, HttpEventType, HttpResponse} depuis '@ angular / common / http';
Bharathiraja
1
dans mon cas, j'utilisais le porteur d'autorisation et j'ai ajouté ce code supplémentairelet params = new HttpParams(); let headers = new HttpHeaders({ 'Authorization': 'Bearer ' + localStorage.getItem('accessToken'), }); const options = { headers: headers, params: params, reportProgress: true, };
Ciprian Dragoe
Il convient de noter que les importations pour Observableet HttpEventpourraient être entièrement omises si vous êtes d'accord avec l'utilisation de l'inférence de type pour fournir le type de retour de la fonction pour uploadFile()! this.http.request()renvoie déjà un type de Observable<HttpEvent<{}>>, donc si vous donnez à l'appel de requête un type générique (c'est-à-dire que this.http.request<any>()la fonction entière fonctionne simplement avec les bons types.
wosevision
2
La partie html va comme ça input type="file" (change)="addFiles($event)" style="display: none" #file multiple> <button mat-raised-button color="primary" (click)="selectFile($event)">Upload File </button>
Shantam Mittal
22

Merci à @Eswar. Ce code a parfaitement fonctionné pour moi. Je souhaite ajouter certaines choses à la solution:

J'obtenais une erreur: java.io.IOException: RESTEASY007550: Unable to get boundary for multipart

Afin de résoudre cette erreur, vous devez supprimer le "type de contenu" "multipart / form-data". Cela a résolu mon problème.

heman123
la source
5
+1. Si vous supprimez Content-Type, il est généré correctement. Par exemple: multipart/form-data; boundary=---------------------------186035562730765173675680113. Voir également stackoverflow.com/a/29697774/1475331 et github.com/angular/angular/issues/11819 .
turdus-merula
1
J'obtiens cette erreur java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found"qui est similaire à la vôtre, mais lorsque je supprime l'en- Content-Typetête, j'obtiens un 404 du backend à la place. Nous utilisons Spring et Angular 2. Toute aide appréciée.
Helen le
Cela devrait être juste un commentaire sur sa réponse, n'est-ce pas?
MMalke
19

Comme l'exemple de code est un peu obsolète, j'ai pensé partager une approche plus récente, en utilisant Angular 4.3 et la nouvelle (er) HttpClient API, @ angular / common / http

export class FileUpload {

@ViewChild('selectedFile') selectedFileEl;

uploadFile() {
let params = new HttpParams();

let formData = new FormData();
formData.append('upload', this.selectedFileEl.nativeElement.files[0])

const options = {
    headers: new HttpHeaders().set('Authorization', this.loopBackAuth.accessTokenId),
    params: params,
    reportProgress: true,
    withCredentials: true,
}

this.http.post('http://localhost:3000/api/FileUploads/fileupload', formData, options)
.subscribe(
    data => {
        console.log("Subscribe data", data);
    },
    (err: HttpErrorResponse) => {
        console.log(err.message, JSON.parse(err.error).error.message);
    }
)
.add(() => this.uploadBtn.nativeElement.disabled = false);//teardown
}
jsaddwater
la source
1
avez-vous le html pour cela? J'aime que cela utilise HttpParams. Je me demande simplement si vous avez un exemple de travail complet quelque part. Merci
Maddy
De cette façon, comment puis-je télécharger plusieurs fichiers ensemble en tant que tableau? comment il doit s'ajouter à l'objet de données de formulaire?
SSR
jetez un œil aux données de formulaire en plusieurs
parties
15

Dans Angular 2+, il est très important de laisser le Content-Type vide. Si vous définissez le 'Content-Type' sur 'multipart / form-data', le téléchargement ne fonctionnera pas!

upload.component.html

<input type="file" (change)="fileChange($event)" name="file" />

upload.component.ts

export class UploadComponent implements OnInit {
    constructor(public http: Http) {}

    fileChange(event): void {
        const fileList: FileList = event.target.files;
        if (fileList.length > 0) {
            const file = fileList[0];

            const formData = new FormData();
            formData.append('file', file, file.name);

            const headers = new Headers();
            // It is very important to leave the Content-Type empty
            // do not use headers.append('Content-Type', 'multipart/form-data');
            headers.append('Authorization', 'Bearer ' + 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9....');
            const options = new RequestOptions({headers: headers});

            this.http.post('https://api.mysite.com/uploadfile', formData, options)
                 .map(res => res.json())
                 .catch(error => Observable.throw(error))
                 .subscribe(
                     data => console.log('success'),
                     error => console.log(error)
                 );
        }
    }
}
abahet
la source
7

Cette solution simple a fonctionné pour moi: file-upload.component.html

<div>
  <input type="file" #fileInput placeholder="Upload file..." />
  <button type="button" (click)="upload()">Upload</button>
</div>

Et puis faites le téléchargement dans le composant directement avec XMLHttpRequest .

import { Component, OnInit, ViewChild } from '@angular/core';

@Component({
  selector: 'app-file-upload',
  templateUrl: './file-upload.component.html',
  styleUrls: ['./file-upload.component.css']
})
export class FileUploadComponent implements OnInit {

  @ViewChild('fileInput') fileInput;

  constructor() { }

  ngOnInit() {
  }

  private upload() {
    const fileBrowser = this.fileInput.nativeElement;
    if (fileBrowser.files && fileBrowser.files[0]) {
      const formData = new FormData();
      formData.append('files', fileBrowser.files[0]);
      const xhr = new XMLHttpRequest();
      xhr.open('POST', '/api/Data/UploadFiles', true);
      xhr.onload = function () {
        if (this['status'] === 200) {
            const responseText = this['responseText'];
            const files = JSON.parse(responseText);
            //todo: emit event
        } else {
          //todo: error handling
        }
      };
      xhr.send(formData);
    }
  }

}

Si vous utilisez dotnet core, le nom du paramètre doit correspondre au nom du champ from. fichiers dans ce cas:

[HttpPost("[action]")]
public async Task<IList<FileDto>> UploadFiles(List<IFormFile> files)
{
  return await _binaryService.UploadFilesAsync(files);
}

Cette réponse est un plagiate de http://blog.teamtreehouse.com/uploading-files-ajax

Modifier : Après le téléchargement, vous devez effacer le téléchargement de fichier afin que l'utilisateur puisse sélectionner un nouveau fichier. Et au lieu d'utiliser XMLHttpRequest, il est peut-être préférable d'utiliser fetch:

private addFileInput() {
    const fileInputParentNative = this.fileInputParent.nativeElement;
    const oldFileInput = fileInputParentNative.querySelector('input');
    const newFileInput = document.createElement('input');
    newFileInput.type = 'file';
    newFileInput.multiple = true;
    newFileInput.name = 'fileInput';
    const uploadfiles = this.uploadFiles.bind(this);
    newFileInput.onchange = uploadfiles;
    oldFileInput.parentNode.replaceChild(newFileInput, oldFileInput);
  }

  private uploadFiles() {
    this.onUploadStarted.emit();
    const fileInputParentNative = this.fileInputParent.nativeElement;
    const fileInput = fileInputParentNative.querySelector('input');
    if (fileInput.files && fileInput.files.length > 0) {
      const formData = new FormData();
      for (let i = 0; i < fileInput.files.length; i++) {
        formData.append('files', fileInput.files[i]);
      }

      const onUploaded = this.onUploaded;
      const onError = this.onError;
      const addFileInput = this.addFileInput.bind(this);
      fetch('/api/Data/UploadFiles', {
        credentials: 'include',
        method: 'POST',
        body: formData,
      }).then((response: any) => {
        if (response.status !== 200) {
          const error = `An error occured. Status: ${response.status}`;
          throw new Error(error);
        }
        return response.json();
      }).then(files => {
        onUploaded.emit(files);
        addFileInput();
      }).catch((error) => {
        onError.emit(error);
      });
    }

https://github.com/yonexbat/cran/blob/master/cranangularclient/src/app/file-upload/file-upload.component.ts

yonexbat
la source
3

Ceci est un tutoriel utile , comment télécharger un fichier en utilisant ng2-file-upload et SANS ng2-file-upload.

Pour moi, cela m'aide beaucoup.

Pour le moment, le didacticiel contient quelques erreurs:

1- Le client doit avoir la même URL de téléchargement qu'un serveur, donc dans la app.component.tsligne de changement

const URL = 'http://localhost:8000/api/upload';

à

const URL = 'http://localhost:3000';

2- Le serveur envoie la réponse en tant que 'text / html', donc en app.component.tschangement

.post(URL, formData).map((res:Response) => res.json()).subscribe(
  //map the success function and alert the response
  (success) => {
    alert(success._body);
  },
  (error) => alert(error))

à

.post(URL, formData)  
.subscribe((success) => alert('success'), (error) => alert(error));
Sandre
la source
3

Pour télécharger une image avec des champs de formulaire

SaveFileWithData(article: ArticleModel,picture:File): Observable<ArticleModel> 
{

    let headers = new Headers();
    // headers.append('Content-Type', 'multipart/form-data');
    // headers.append('Accept', 'application/json');

let requestoptions = new RequestOptions({
  method: RequestMethod.Post,
  headers:headers
    });



let formData: FormData = new FormData();
if (picture != null || picture != undefined) {
  formData.append('files', picture, picture.name);
}
 formData.append("article",JSON.stringify(article));

return this.http.post("url",formData,requestoptions)
  .map((response: Response) => response.json() as ArticleModel);
} 

Dans mon cas, j'avais besoin de .NET Web Api en C #

// POST: api/Articles
[ResponseType(typeof(Article))]
public async Task<IHttpActionResult> PostArticle()
{
    Article article = null;
    try
    {

        HttpPostedFile postedFile = null;
        var httpRequest = HttpContext.Current.Request;

        if (httpRequest.Files.Count == 1)
        {
            postedFile = httpRequest.Files[0];
            var filePath = HttpContext.Current.Server.MapPath("~/" + postedFile.FileName);
            postedFile.SaveAs(filePath);
        }
        var json = httpRequest.Form["article"];
         article = JsonConvert.DeserializeObject <Article>(json);

        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        article.CreatedDate = DateTime.Now;
        article.CreatedBy = "Abbas";

        db.articles.Add(article);
        await db.SaveChangesAsync();
    }
    catch (Exception ex)
    {
        int a = 0;
    }
    return CreatedAtRoute("DefaultApi", new { id = article.Id }, article);
}
Charlie
la source
3

Aujourd'hui, j'ai intégré le package ng2-file-upload à mon application angular 6, c'était assez simple, veuillez trouver le code de haut niveau ci-dessous.

importer le -upload NG2 fichier Module

app.module.ts

    import { FileUploadModule } from 'ng2-file-upload';

    ------
    ------
    imports:      [ FileUploadModule ],
    ------
    ------

Importation de fichier TS de composant FileUploader

app.component.ts

    import { FileUploader, FileLikeObject } from 'ng2-file-upload';
    ------
    ------
    const URL = 'http://localhost:3000/fileupload/';
    ------
    ------

     public uploader: FileUploader = new FileUploader({
        url: URL,
        disableMultipart : false,
        autoUpload: true,
        method: 'post',
        itemAlias: 'attachment'

        });

      public onFileSelected(event: EventEmitter<File[]>) {
        const file: File = event[0];
        console.log(file);

      }
    ------
    ------

Balise d'ajout de fichier HTML du composant

app.component.html

 <input type="file" #fileInput ng2FileSelect [uploader]="uploader" (onFileSelected)="onFileSelected($event)" />

Lien de stackblitz en ligne fonctionnant: https://ng2-file-upload-example.stackblitz.io

Exemple de code Stackblitz: https://stackblitz.com/edit/ng2-file-upload-example

Lien vers la documentation officielle https://valor-software.com/ng2-file-upload/

Raja Rama Mohan Thavalam
la source
1

Essayez de ne pas définir le optionsparamètre

this.http.post(${this.apiEndPoint}, formData)

et assurez-vous que vous ne configurez pas le globalHeadersdans votre usine Http.

thxmxx
la source
1

Dans la forme la plus simple, le code suivant fonctionne dans Angular 6/7

this.http.post("http://destinationurl.com/endpoint", fileFormData)
  .subscribe(response => {
    //handle response
  }, err => {
    //handle error
  });

Voici l' implémentation complète

shaheer shukur
la source
1

jspdf et Angular 8

Je génère un pdf et je veux télécharger le pdf avec la demande POST, voici comment je fais (pour plus de clarté, je supprime une partie du code et de la couche de service)

import * as jsPDF from 'jspdf';
import { HttpClient } from '@angular/common/http';

constructor(private http: HttpClient)

upload() {
    const pdf = new jsPDF()
    const blob = pdf.output('blob')
    const formData = new FormData()
    formData.append('file', blob)
    this.http.post('http://your-hostname/api/upload', formData).subscribe()
}
Brady Huang
la source
0

J'ai téléchargé le fichier en utilisant la référence. Aucun package n'est requis pour télécharger le fichier de cette façon.

// code à écrire dans le fichier .ts

@ViewChild("fileInput") fileInput;

addFile(): void {
let fi = this.fileInput.nativeElement;
if (fi.files && fi.files[0]) {
  let fileToUpload = fi.files[0];
    this.admin.addQuestionApi(fileToUpload)
      .subscribe(
        success => {
          this.loading = false;
          this.flashMessagesService.show('Uploaded successfully', {
            classes: ['alert', 'alert-success'],
            timeout: 1000,
          });
        },
        error => {
          this.loading = false;
          if(error.statusCode==401) this.router.navigate(['']);
          else
            this.flashMessagesService.show(error.message, {
              classes: ['alert', 'alert-danger'],
              timeout: 1000,
            });
        });
  }

}

// code à écrire dans le fichier service.ts

addQuestionApi(fileToUpload: any){
var headers = this.getHeadersForMultipart();
let input = new FormData();
input.append("file", fileToUpload);

return this.http.post(this.baseUrl+'addQuestions', input, {headers:headers})
      .map(response => response.json())
      .catch(this.errorHandler);

}

// code à écrire en html

<input type="file" #fileInput>
Sheena Singla
la source