File Upload In Angular?
Asked Answered
E

14

207

I know this is very a general question but I am failing to upload a file in Angular 2. I have tried

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

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

...but failed. Has anyone uploaded a file in Angular? What method did you use? How to do so? If any sample code or demo link is provided it will be really appreciated.

Excite answered 24/10, 2016 at 9:18 Comment(0)
U
425

Angular 2 provides good support for uploading files. No third party library is required.

<input type="file" (change)="fileChange($event)" placeholder="Upload file" accept=".pdf,.doc,.docx">
fileChange(event) {
    let fileList: FileList = event.target.files;

    if (fileList.length < 1) {
      return;
    }
    
    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)
        );
}

using @angular/core": "~2.0.0" and @angular/http: "~2.0.0"

Unfrequented answered 24/10, 2016 at 10:52 Comment(20)
${this.apiEndPoint} is showing error " unresolved variable" ! how to solve this ? Where can i read about file upload in angular2? which site ? which doc provide about the detailed information?Excite
it is not working, at least in my case. sailsJs server receive empty file array/objectHemimorphite
It worked for me except - I had to work on this line- headers.append('enctype', 'multipart/form-data'); (used 'enctype' to replace 'Content-Type'). Perhaps it depends on the server-side code. (i.e. api)Fridlund
@Unfrequented how would I add a progress to this solution? It seems nicer than native XHR approach.Subaqueous
Be great if the Angular team would write some documentation on the subject, I can't find a single line about it in their docs. This code sample is out of date and doesn't work with v4+.Vogeley
Note for some application servers, setting the content-type will be rejected. You need to let it be blank: let headers = new Headers(); The browser will sort it all out for you.Lenka
Yes. Just let headers = new Headers(); worked in my case with Angular 4Odontalgia
Any idea why angular2 need api to save file to the server?Resendez
Trying to do same but getting my coder is getting error - can anyone help us here - #44472332Jarredjarrell
LMFAO struggled 20mins with this crap until I realized that I didnt needed to set the headers at all. Note to others using angular 4.x.x with .Net webapi, dont try to set the headers! Thx for pointing that out @LenkaNapery
How can i upload a large file like 500MB using chunk? any ideaGlobuliferous
I need to upload it as application/json , is it possible?Bordelon
Wih Rails as back-end and Angular v4 (using HttpClient) I didn't need to set nothing, just call like this: this.httpClient.post(URL, formData)...;Torn
there's a helpful post at Medium.com: medium.com/codingthesmartway-com-blog/… which describes the new HttpClient API a bitLengthy
It works perfect but the only issue is that i cannot disable multipart data with this.Overdraw
I am not able to get data in formData. its giving me blank object {}. O.S(Linux).Look
Ohmighosh--two developers wasted a day and a half because we were using Angular 4 and trying to supply headers.Suttles
You saved me on the "/** In Angular 5, including the header Content-Type can invalidate your request */" - Was having a big problem because of this.Chantay
For IE 10+ it is important to add the filename seperately as @Unfrequented didMonazite
this post does a lot you don't need, and also in almost any case this would be split into a service file, and a component that subscribes to itPropagandist
M
88

From the answers above I build this with Angular 5.x

Just call uploadFile(url, file).subscribe() to trigger an 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 it like this in your component

  // 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");
        }
      )
  }
Moderation answered 1/12, 2017 at 16:25 Comment(5)
Works fine with Angular6. Thank you. And you need these libraries to import. import {HttpClient, HttpParams, HttpRequest, HttpEvent, HttpEventType, HttpResponse} from '@angular/common/http';Alejandraalejandrina
in my case I was using authorization bearer and added this extra code let params = new HttpParams(); let headers = new HttpHeaders({ 'Authorization': 'Bearer ' + localStorage.getItem('accessToken'), }); const options = { headers: headers, params: params, reportProgress: true, };Doud
It's worth noting that the imports for Observable and HttpEvent could be omitted entirely if you're okay with using type inference to provide the function's return type for uploadFile()! this.http.request() already returns a type of Observable<HttpEvent<{}>>, so if you give the request call a generic type (i.e. this.http.request<any>() then the whole function just works out with the right types.Antiphonary
The html part goes like this input type="file" (change)="addFiles($event)" style="display: none" #file multiple> <button mat-raised-button color="primary" (click)="selectFile($event)">Upload File </button>Suannesuarez
What is 'upload' in -> formData.append('upload', file); in UploadService ?Moslemism
E
26

Thanks to @Eswar. This code worked perfectly for me. I want to add certain things to the solution :

I was getting error : java.io.IOException: RESTEASY007550: Unable to get boundary for multipart

In order to solve this error, you should remove the "Content-Type" "multipart/form-data". It solved my problem.

Excite answered 25/10, 2016 at 8:34 Comment(4)
+1. If you remove Content-Type, it gets generated correctly. E.g.: multipart/form-data; boundary=---------------------------186035562730765173675680113. Also see https://mcmap.net/q/129078/-warning-missing-boundary-in-multipart-form-data-post-data-in-unknown-on-line-0 and github.com/angular/angular/issues/11819.Ingamar
I'm getting this error java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found" which is similar to yours, however when I remove the Content-Type header I get a 404 from the backend instead. We're using Spring and Angular 2. Any help appreciated.Crowell
This should be just a comment on his answer, shouldn't it?Aimo
Thanks, but why doesn't it work with the "Content-type" header?Ortensia
L
20

Since the code sample is a bit outdated I thought I'd share a more recent approach, using Angular 4.3 and the new(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
}
Lengthy answered 26/10, 2017 at 13:7 Comment(3)
do you have the html for this? I like that this is using HttpParams. Just wondering if you have a complete working example somewhere. ThanksWillettawillette
In this way how can I upload multiple files together as an array? how it should append to the form data object?Witticism
have a look at multipart form data webdavsystem.com/javaserver/doc/resumable_upload/multipart_postLengthy
E
19

In Angular 2+, it is very important to leave the Content-Type empty. If you set the 'Content-Type' to 'multipart/form-data' the upload will not work !

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)
                 );
        }
    }
}
Eclair answered 26/10, 2017 at 15:5 Comment(0)
H
8

I have used the following tool from priming with success. I have no skin in the game with primeNg, just passing on my suggestion.

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

Heartsome answered 24/10, 2016 at 10:49 Comment(2)
Please can you let us know if this is NG2 compatible??Funicle
@Funicle It's Angular 4 compatible for sure. primefaces.org/primeng/#/setupOdontalgia
V
8

This simple solution worked for me: file-upload.component.html

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

And then do the upload in the component directly with 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);
    }
  }

}

If you are using dotnet core, the parameter name must match the from field name. files in this case:

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

This answer is a plagiate of http://blog.teamtreehouse.com/uploading-files-ajax

Edit: After uploading, you have to clear the file-upload so that the user can select a new file. And instead of using XMLHttpRequest, maybe it is better to use 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

Vaginitis answered 21/9, 2017 at 19:3 Comment(0)
C
3

This is useful tutorial, how to upload file using the ng2-file-upload and WITHOUT ng2-file-upload.

For me it helps a lot.

At the moment, tutorial contains a couple of mistakes:

1- Client should have same upload url as a server, so in app.component.ts change line

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

to

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

2- Server send response as 'text/html', so in app.component.ts change

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

to

.post(URL, formData)  
.subscribe((success) => alert('success'), (error) => alert(error));
Caecilian answered 30/6, 2017 at 16:3 Comment(0)
Q
3

To upload image with form fields

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

In my case I needed .NET Web Api in 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);
}
Qoph answered 13/12, 2017 at 23:6 Comment(0)
L
3

Today I was integrated ng2-file-upload package to my angular 6 application, It was pretty simple, Please find the below high-level code.

import the ng2-file-upload module

app.module.ts

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

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

Component ts file import 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);

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

Component HTML add file tag

app.component.html

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

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

Stackblitz Code example: https://stackblitz.com/edit/ng2-file-upload-example

Official documentation link https://valor-software.com/ng2-file-upload/

Langlauf answered 24/9, 2018 at 7:25 Comment(0)
O
2

Try not setting the options parameter

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

and make sure you are not setting the globalHeaders in your Http factory.

Ostrander answered 28/6, 2018 at 13:32 Comment(0)
L
1

jspdf and Angular 8

I generate a pdf and want to upload the pdf with POST request, this is how I do (For clarity, I delete some of the code and service layer)

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()
}
Lymn answered 18/12, 2019 at 1:51 Comment(0)
A
0

I've upload file using reference. No package is required to upload file this way.

// code to be written in .ts file

@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 to be written in service.ts file

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 to be written in html

<input type="file" #fileInput>
Angary answered 23/11, 2017 at 5:23 Comment(0)
T
-1

In the simplest form, the following code works in Angular 6/7

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

Here is the complete implementation

Theodicy answered 4/5, 2019 at 9:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.