Subir archivo en angular?

Respuestas:

375

Angular 2 proporciona un buen soporte para cargar archivos. No se requiere una biblioteca de terceros.

<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)
            )
    }
}

utilizando @ angular / core ":" ~ 2.0.0 "y @ angular / http:" ~ 2.0.0 "

Eswar
fuente
55
no está funcionando, al menos en mi caso. servidor sailsJs recibir vacía variedad archivo / objeto
Kaleem Ullah
20
Funcionó para mí, excepto que tuve que trabajar en esta línea headers.append('enctype', 'multipart/form-data');(usé 'enctype' para reemplazar 'Content-Type'). Tal vez depende del código del lado del servidor. (es decir, api)
Ariful Islam
29
Sea genial si el equipo de Angular escribiera alguna documentación sobre el tema, no puedo encontrar una sola línea al respecto en sus documentos. Este ejemplo de código está desactualizado y no funciona con v4 +.
Rob B
10
Nota para algunos servidores de aplicaciones, se rechazará la configuración del tipo de contenido. Debe dejarlo en blanco: let headers = new Headers (); El navegador lo resolverá todo por usted.
PeterS
66
LMFAO luchó 20 minutos con esta basura hasta que me di cuenta de que no necesitaba configurar los encabezados. Nota para otros que usan angular 4.xx con .Net webapi, ¡no intentes configurar los encabezados! Gracias por señalar eso @PeterS
Jota.Toledo
76

A partir de las respuestas anteriores, construyo esto con Angular 5.x

Solo llame uploadFile(url, file).subscribe()para activar una carga

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);
  }
}

Úselo así en su componente

  // 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
fuente
66
Funciona bien con Angular6. Gracias. Y necesita estas bibliotecas para importar. importar {HttpClient, HttpParams, HttpRequest, HttpEvent, HttpEventType, HttpResponse} desde '@ angular / common / http';
Bharathiraja
1
en mi caso estaba usando el portador de autorización y agregué este código extralet params = new HttpParams(); let headers = new HttpHeaders({ 'Authorization': 'Bearer ' + localStorage.getItem('accessToken'), }); const options = { headers: headers, params: params, reportProgress: true, };
Ciprian Dragoe
¡Vale la pena señalar que las importaciones Observabley HttpEventpodrían omitirse por completo si está de acuerdo con el uso de la inferencia de tipos para proporcionar el tipo de retorno de la función uploadFile()! this.http.request()ya devuelve un tipo de Observable<HttpEvent<{}>>, por lo que si le da a la solicitud, llame a un tipo genérico (es decir this.http.request<any>(), toda la función simplemente funciona con los tipos correctos.)
wosevision
2
La parte html es así 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

Gracias a @Eswar. Este código funcionó perfectamente para mí. Quiero agregar ciertas cosas a la solución:

Estaba recibiendo un error: java.io.IOException: RESTEASY007550: Unable to get boundary for multipart

Para resolver este error, debe eliminar el "Contenido-Tipo" "multipart / form-data". Resolvió mi problema.

heman123
fuente
55
+1. Si elimina Content-Type, se genera correctamente. Por ejemplo: multipart/form-data; boundary=---------------------------186035562730765173675680113. Consulte también stackoverflow.com/a/29697774/1475331 y github.com/angular/angular/issues/11819 .
turdus-merula
1
Recibo este error java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found"que es similar al tuyo, sin embargo, cuando elimino el Content-Typeencabezado, obtengo un 404 del backend. Estamos usando Spring y Angular 2. Cualquier ayuda apreciada.
Helen
Esto debería ser solo un comentario sobre su respuesta, ¿no?
MMalke
19

Dado que el código de muestra está un poco desactualizado, pensé en compartir un enfoque más reciente, utilizando Angular 4.3 y la nueva (er) API HttpClient, @ 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
fuente
1
¿tienes el html para esto? Me gusta que esto esté usando HttpParams. Me pregunto si tiene un ejemplo de trabajo completo en alguna parte. Gracias
Maddy
De esta manera, ¿cómo puedo cargar varios archivos juntos como una matriz? ¿Cómo debería anexarse ​​al objeto de datos de formulario?
SSR
eche un vistazo a los datos de formulario multiparte webdavsystem.com/javaserver/doc/resumable_upload/multipart_post
jsaddwater
15

En Angular 2+, es muy importante dejar vacío el Tipo de contenido . Si configura 'Content-Type' en 'multipart / form-data', la carga no funcionará.

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
fuente
7

Esta solución simple funcionó para mí: file-upload.component.html

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

Y luego realice la carga en el componente directamente con 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 está utilizando dotnet core, el nombre del parámetro debe coincidir con el nombre del campo from. archivos en este caso:

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

Esta respuesta es un plagio de http://blog.teamtreehouse.com/uploading-files-ajax

Editar : después de cargar, debe borrar la carga del archivo para que el usuario pueda seleccionar un nuevo archivo. Y en lugar de usar XMLHttpRequest, tal vez sea mejor usar 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
fuente
3

Este es un tutorial útil. cómo cargar archivos usando ng2-file-upload y SIN ng2-file-upload.

Para mí me ayuda mucho.

Por el momento, el tutorial contiene un par de errores:

1- El cliente debe tener la misma URL de carga que un servidor, por lo que en la app.component.tslínea de cambio

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

a

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

2- Respuesta de envío del servidor como 'texto / html', por lo que a app.component.tscambio

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

a

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

Para cargar una imagen con campos de formulario

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);
} 

En mi caso, necesitaba .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
fuente
3

Hoy me integraron el paquete ng2-file-upload a mi aplicación angular 6, fue bastante simple, encuentra el siguiente código de alto nivel.

importar el NG2-carga de archivos del módulo

app.module.ts

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

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

Componente ts importación de archivos 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);

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

Componente HTML agregar etiqueta de archivo

app.component.html

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

Trabajando en línea stackblitz Link: https://ng2-file-upload-example.stackblitz.io

Ejemplo de código de Stackblitz: https://stackblitz.com/edit/ng2-file-upload-example

Enlace de documentación oficial https://valor-software.com/ng2-file-upload/

Raja Rama Mohan Thavalam
fuente
1

Intenta no configurar el optionsparámetro

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

y asegúrese de no configurarlo globalHeadersen su fábrica Http.

thxmxx
fuente
1

En la forma más simple, el siguiente código funciona en Angular 6/7

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

Aquí está la implementación completa

Shaheer Shukur
fuente
1

jspdf y Angular 8

Genero un pdf y quiero cargar el pdf con la solicitud POST, así es como lo hago (para mayor claridad, elimino parte del código y la capa de servicio)

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
fuente
0

He subido el archivo usando la referencia. No se requiere ningún paquete para cargar el archivo de esta manera.

// código que se escribirá en el archivo .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,
            });
        });
  }

}

// código que se escribirá en el archivo 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);

}

// código que se escribirá en html

<input type="file" #fileInput>
Sheena Singla
fuente