Upload de arquivo em angular?

Respostas:

375

O Angular 2 fornece um bom suporte para o upload de arquivos. Nenhuma biblioteca de terceiros é necessária.

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

usando @ angular / core ":" ~ 2.0.0 "e @ angular / http:" ~ 2.0.0 "

Eswar
fonte
5
não está funcionando, pelo menos no meu caso. servidor sailsJs receber vazia variedade de arquivos / object
Kaleem Ullah
20
Funcionou para mim, exceto - eu tive que trabalhar nessa linha - headers.append('enctype', 'multipart/form-data');(usei 'enctype' para substituir 'Content-Type'). Talvez isso dependa do código do lado do servidor. (ie api)
Ariful Islam
29
Seja ótimo se a equipe do Angular escrever alguma documentação sobre o assunto, não consigo encontrar uma única linha sobre isso nos documentos deles. Este exemplo de código está desatualizado e não funciona com a v4 +.
Rob B
10
Nota para alguns servidores de aplicativos, a configuração do tipo de conteúdo será rejeitada. Você precisa deixar em branco: deixe headers = new Headers (); O navegador resolverá tudo para você.
PeterS
6
O LMFAO lutou 20 minutos com essa porcaria até perceber que não precisava definir os cabeçalhos. Nota para outras pessoas que usam o angular 4.xx com o .net webapi, não tente definir os cabeçalhos! Thx por apontar isso @PeterS
Jota.Toledo
76

A partir das respostas acima, crio isso com o Angular 5.x

Basta ligar uploadFile(url, file).subscribe()para acionar um upload

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

Use-o assim em seu 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
fonte
6
Funciona bem com Angular6. Obrigado. E você precisa dessas bibliotecas para importar. import {HttpClient, HttpParams, HttpRequest, HttpEvent, HttpEventType, HttpResponse} de '@ angular / common / http';
Bharathiraja
1
no meu caso eu estava usando autorização portador e acrescentou 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 a pena notar que as importações Observablee HttpEventpodem ser totalmente omitidas se você estiver bem em usar a inferência de tipo para fornecer o tipo de retorno da função uploadFile()! this.http.request()já retorna um tipo de Observable<HttpEvent<{}>>, portanto, se você der a solicitação para chamar um tipo genérico (ou seja this.http.request<any>(), toda a função funcionará com os tipos certos.
wosevision
2
A parte html é assim 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

Graças a @Eswar. Este código funcionou perfeitamente para mim. Quero adicionar algumas coisas à solução:

Eu estava recebendo erro: java.io.IOException: RESTEASY007550: Unable to get boundary for multipart

Para solucionar esse erro, você deve remover o "Content-Type" "multipart / form-data". Isso resolveu meu problema.

heman123
fonte
5
+1. Se você remover o Tipo de conteúdo, ele será gerado corretamente. Por exemplo: multipart/form-data; boundary=---------------------------186035562730765173675680113. Consulte também stackoverflow.com/a/29697774/1475331 e github.com/angular/angular/issues/11819 .
Turdus merula-
1
Estou recebendo esse erro java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found"que é semelhante ao seu, no entanto, quando removo o Content-Typecabeçalho, recebo um 404 do back-end. Estamos usando Spring e Angular 2. Qualquer ajuda apreciada.
Helen Helen
Isso deveria ser apenas um comentário sobre a resposta dele, não deveria?
MMalke
19

Como o exemplo de código está um pouco desatualizado, pensei em compartilhar uma abordagem mais recente, usando o Angular 4.3 e a nova (e) 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
fonte
1
você tem o html para isso? Eu gosto que isso está usando HttpParams. Basta saber se você tem um exemplo completo de trabalho em algum lugar. Graças
Maddy
Dessa forma, como posso carregar vários arquivos juntos como uma matriz? como ele deve ser anexado ao objeto de dados do formulário?
SSR
dê uma olhada nos dados do formulário multipartes webdavsystem.com/javaserver/doc/resumable_upload/multipart_post
jsaddwater
15

No Angular 2+, é muito importante deixar o Tipo de Conteúdo vazio. Se você definir o 'Tipo de conteúdo' como 'dados de várias partes / formulário', o upload não 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
fonte
8

Eu usei a seguinte ferramenta de preparação com sucesso. Não tenho skin no jogo com o primeNg, apenas passando a minha sugestão.

http://www.primefaces.org/primeng/#/fileupload

John Baird
fonte
Por favor, deixe-nos saber se isso é compatível com NG2 ??
G1P 20/04/19
@ G1P É Angular 4 compatível, com certeza. primefaces.org/primeng/#/setup
makkasi
7

Esta solução simples funcionou para mim: file-upload.component.html

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

E, em seguida, faça o upload no componente diretamente com 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);
    }
  }

}

Se você estiver usando o dotnet core, o nome do parâmetro deve corresponder ao nome do campo from. arquivos neste caso:

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

Esta resposta é um plágio de http://blog.teamtreehouse.com/uploading-files-ajax

Editar : após o upload, é necessário limpar o upload do arquivo para que o usuário possa selecionar um novo arquivo. E, em vez de usar XMLHttpRequest, talvez seja melhor usar o 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
fonte
3

Este é um tutorial útil , como fazer upload de arquivo usando o ng2-file-upload e SEM ng2-file-upload.

Para mim, isso ajuda muito.

No momento, o tutorial contém alguns erros:

1- O cliente deve ter o mesmo URL de upload que um servidor, portanto, na app.component.tslinha de alteração

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

para

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

2- O servidor envia resposta como 'text / html', portanto, em app.component.tsmudança

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

para

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

Para carregar imagem com campos de formulário

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

No meu caso, eu precisava do .NET Web Api em 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
fonte
3

Hoje eu fui integrado ao ng2-file-upload pacote ao meu aplicativo angular 6. Era bem simples. Encontre o código de alto nível abaixo.

importar o NG2-file-upload módulo

app.module.ts

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

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

Importação de arquivo ts do componente 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);

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

Tag de arquivo de adição de HTML do componente

app.component.html

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

Working Online stackblitz Link: https://ng2-file-upload-example.stackblitz.io

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

Link para documentação oficial https://valor-software.com/ng2-file-upload/

Raja Rama Mohan Thavalam
fonte
1

Tente não definir o optionsparâmetro

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

e verifique se você não está configurando globalHeadersna sua fábrica Http.

thxmxx
fonte
1

Na forma mais simples, o código a seguir funciona no Angular 6/7

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

Aqui está a implementação completa

shaheer shukur
fonte
1

jspdf e Angular 8

Gero um pdf e quero fazer o upload do pdf com solicitação POST, é assim que faço (para maior clareza, excluo parte da camada de código e serviço)

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

Carreguei o arquivo usando referência. Nenhum pacote é necessário para fazer upload do arquivo dessa maneira.

// código a ser escrito no arquivo .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 a ser gravado no arquivo 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 a ser escrito em html

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