Przesyłanie pliku w Angular?

Odpowiedzi:

375

Angular 2 zapewnia dobrą obsługę przesyłania plików. Nie jest wymagana żadna biblioteka innej firmy.

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

używając @ angular / core ":" ~ 2.0.0 "i @ angular / http:" ~ 2.0.0 "

Eswar
źródło
5
to nie działa, przynajmniej w moim przypadku. Serwer sailsJs odebrał pustą tablicę plików / obiekt
Kaleem Ullah
20
U mnie zadziałało, z wyjątkiem tego - musiałem popracować nad tym headers.append('enctype', 'multipart/form-data');wierszem - (użyłem „enctype”, aby zastąpić „Content-Type”). Być może zależy to od kodu po stronie serwera. (tj. api)
Ariful Islam
29
Byłoby wspaniale, gdyby zespół Angular napisał jakąś dokumentację na ten temat, nie mogę znaleźć ani jednej linijki na ten temat w ich dokumentach. Ten przykładowy kod jest nieaktualny i nie działa z wersją 4+.
Rob B
10
Uwaga w przypadku niektórych serwerów aplikacji ustawienie typu zawartości zostanie odrzucone. Musisz pozostawić to pole puste: let headers = new Headers (); Przeglądarka załatwi to za Ciebie.
PeterS
6
LMFAO zmagał się z tym bzdurą przez 20 minut, dopóki nie zdałem sobie sprawy, że w ogóle nie musiałem ustawiać nagłówków. Uwaga dla innych używających kątowego 4.xx z .Net Webapi, nie próbuj ustawiać nagłówków! Dzięki za wskazanie tego @PeterS
Jota.Toledo
76

Z powyższych odpowiedzi buduję to za pomocą Angular 5.x

Po prostu zadzwoń, uploadFile(url, file).subscribe()aby uruchomić przesyłanie

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

Użyj go w ten sposób w swoim komponencie

  // 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
źródło
6
Działa dobrze z Angular6. Dziękuję Ci. Potrzebujesz tych bibliotek do importu. import {HttpClient, HttpParams, HttpRequest, HttpEvent, HttpEventType, HttpResponse} z „@ angular / common / http”;
Bharathiraja
1
w moim przypadku użyłem nośnika autoryzacji i dodałem ten dodatkowy kodlet params = new HttpParams(); let headers = new HttpHeaders({ 'Authorization': 'Bearer ' + localStorage.getItem('accessToken'), }); const options = { headers: headers, params: params, reportProgress: true, };
Ciprian Dragoe
Warto zauważyć, że importowanie dla Observablei HttpEventmoże zostać całkowicie pominięte, jeśli nie przeszkadza Ci używanie wnioskowania o typie w celu zapewnienia zwracanego typu funkcji dla uploadFile()! this.http.request()już zwraca typ Observable<HttpEvent<{}>>, więc jeśli podasz wywołanie żądania typ ogólny (tj. this.http.request<any>()wtedy cała funkcja działa tylko z odpowiednimi typami.
wosvision
2
Część html wygląda tak 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

Dzięki @Eswar. Ten kod działał idealnie dla mnie. Chcę dodać pewne rzeczy do rozwiązania:

Otrzymałem błąd: java.io.IOException: RESTEASY007550: Unable to get boundary for multipart

Aby rozwiązać ten błąd, należy usunąć parametr „Content-Type” „multipart / form-data”. To rozwiązało mój problem.

heman123
źródło
5
+1. Jeśli usuniesz Content-Type, zostanie on wygenerowany poprawnie. Np multipart/form-data; boundary=---------------------------186035562730765173675680113. : . Zobacz także stackoverflow.com/a/29697774/1475331 i github.com/angular/angular/issues/11819 .
turdus-merula
1
Otrzymuję ten błąd, java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found"który jest podobny do twojego, jednak kiedy usuwam Content-Typenagłówek, zamiast tego otrzymuję 404 z zaplecza. Używamy Spring i Angular 2. Doceniamy każdą pomoc.
Helen
To powinien być tylko komentarz do jego odpowiedzi, prawda?
MMalke
19

Ponieważ przykładowy kod jest nieco przestarzały, pomyślałem, że podzielę się nowszym podejściem, używając Angular 4.3 i nowego (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
źródło
1
czy masz do tego html? Podoba mi się, że to używa HttpParams. Zastanawiam się tylko, czy masz gdzieś kompletny przykład roboczy. Dzięki
Maddy,
W ten sposób, jak mogę przesłać wiele plików razem jako tablicę? jak powinien dołączyć do obiektu danych formularza?
SSR
spójrz na dane formularzy wieloczęściowych webdavsystem.com/javaserver/doc/resumable_upload/multipart_post
jsaddwater
15

W Angular 2+ bardzo ważne jest, aby pole Content-Type było puste. Jeśli ustawisz „Content-Type” na „multipart / form-data”, przesyłanie nie będzie działać!

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
źródło
7

To proste rozwiązanie zadziałało dla mnie: file-upload.component.html

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

Następnie prześlij komponent bezpośrednio za pomocą 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);
    }
  }

}

Jeśli używasz dotnet core, nazwa parametru musi być zgodna z nazwą pola od. pliki w tym przypadku:

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

Ta odpowiedź jest plagiatem http://blog.teamtreehouse.com/uploading-files-ajax

Edycja : Po przesłaniu musisz wyczyścić przesyłanie pliku, aby użytkownik mógł wybrać nowy plik. Zamiast używać XMLHttpRequest, może lepiej jest użyć funkcji pobierania:

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
źródło
3

Jest to przydatny samouczek , jak przesłać plik za pomocą przesyłania plików ng2 i BEZ przesyłania plików ng2.

Mnie to bardzo pomaga.

W tej chwili samouczek zawiera kilka błędów:

1- Klient powinien mieć taki sam adres URL przesyłania jak serwer, więc w app.component.tslinii zmiany

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

do

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

2- Serwer wysyła odpowiedź jako „tekst / html”, więc ze app.component.tszmianą

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

do

.post(URL, formData)  
.subscribe((success) => alert('success'), (error) => alert(error));
Sandre
źródło
3

Aby przesłać obraz z polami formularza

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

W moim przypadku potrzebowałem .NET Web Api w 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
źródło
3

Dzisiaj zostałem zintegrowany z pakietem wysyłania plików ng2 do mojej aplikacji angular 6. To było całkiem proste. Proszę znaleźć poniższy kod wysokiego poziomu.

zaimportuj moduł przesyłania plików ng2

app.module.ts

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

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

Import pliku komponentu ts 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);

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

Znacznik dodawania pliku HTML komponentu

app.component.html

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

Działający link stackblitz online: https://ng2-file-upload-example.stackblitz.io

Przykład kodu Stackblitz: https://stackblitz.com/edit/ng2-file-upload-example

Oficjalny link do dokumentacji https://valor-software.com/ng2-file-upload/

Raja Rama Mohan Thavalam
źródło
1

Spróbuj nie ustawiać optionsparametru

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

i upewnij się, że nie ustawiasz globalHeadersw fabryce HTTP.

thxmxx
źródło
1

W najprostszej formie poniższy kod działa w Angular 6/7

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

Oto pełna realizacja

shaheer shukur
źródło
1

jspdf oraz Angular 8

Generuję plik pdf i chcę przesłać plik pdf z żądaniem POST, tak to robię (dla jasności usuwam część kodu i warstwę usług)

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
źródło
0

Przesłałem plik za pomocą odwołania Do przesłania pliku w ten sposób nie jest wymagany żaden pakiet.

// kod do zapisania w pliku .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,
            });
        });
  }

}

// kod do zapisania w pliku 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);

}

// kod do napisania w html

<input type="file" #fileInput>
Sheena Singla
źródło