在 Angular 中上传文件?

Posted

技术标签:

【中文标题】在 Angular 中上传文件?【英文标题】:File Upload In Angular? 【发布时间】:2017-03-06 00:22:25 【问题描述】:

我知道这是一个非常普遍的问题,但我无法在 Angular 2 中上传文件。 我试过了

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

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

...但是失败了。有人在 Angular 中上传文件吗?你用了什么方法?怎么做?如果提供任何示例代码或演示链接,将不胜感激。

【问题讨论】:

【参考方案1】:

Angular 2 为上传文件提供了良好的支持。不需要第三方库。

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

使用@angular/core": "~2.0.0" 和@angular/http: "~2.0.0"

【讨论】:

它不起作用,至少在我的情况下。 sailsJs 服务器接收空文件数组/对象 它对我有用,除了-我必须在这条线上工作-headers.append('enctype', 'multipart/form-data');(使用'enctype'替换'Content-Type')。也许这取决于服务器端代码。 (即 api) 如果 Angular 团队会写一些关于这个主题的文档,那就太好了,我在他们的文档中找不到一行。此代码示例已过时,不适用于 v4+。 注意对于某些应用服务器,设置content-type会被拒绝。你需要让它为空:let headers = new Headers();浏览器会帮你整理好。 LMFAO 为这个废话苦苦挣扎了 20 分钟,直到我意识到我根本不需要设置标题。注意使用 .Net webapi 的 angular 4.x.x 的其他人,不要尝试设置标题!感谢您指出这一点@PeterS【参考方案2】:

根据上面的答案,我使用 Angular 5.x 构建了这个

只需致电uploadFile(url, file).subscribe() 即可触发上传

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

在你的组件中像这样使用它

  // 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");
        
      )
  

【讨论】:

适用于 Angular6。谢谢你。你需要这些库来导入。从'@angular/common/http'导入HttpClient, HttpParams, HttpRequest, HttpEvent, HttpEventType, HttpResponse; 在我的情况下,我使用的是授权承载并添加了这个额外的代码let params = new HttpParams(); let headers = new HttpHeaders( 'Authorization': 'Bearer ' + localStorage.getItem('accessToken'), ); const options = headers: headers, params: params, reportProgress: true, ; 值得注意的是,如果您可以使用类型推断为uploadFile() 提供函数的返回类型,则可以完全省略ObservableHttpEvent 的导入! this.http.request() 已经返回了 Observable&lt;HttpEvent&lt;&gt;&gt; 的类型,所以如果你给请求调用一个泛型类型(即this.http.request&lt;any&gt;() 那么整个函数就可以使用正确的类型。 html部分是这样的input type="file" (change)="addFiles($event)" style="display: none" #file multiple&gt; &lt;button mat-raised-button color="primary" (click)="selectFile($event)"&gt;Upload File &lt;/button&gt;【参考方案3】:

感谢@Eswar。这段代码对我来说非常有效。我想在解决方案中添加某些内容:

我遇到了错误:java.io.IOException: RESTEASY007550: Unable to get boundary for multipart

为了解决这个错误,您应该删除“Content-Type”“multipart/form-data”。它解决了我的问题。

【讨论】:

+1。如果您删除 Content-Type,它会正确生成。例如:multipart/form-data; boundary=---------------------------186035562730765173675680113。另见***.com/a/29697774/1475331 和github.com/angular/angular/issues/11819。 我收到此错误java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found",这与您的类似,但是当我删除 Content-Type 标头时,我从后端得到了 404。我们正在使用 Spring 和 Angular 2。任何帮助表示赞赏。 这应该只是对他的回答的评论吧? 谢谢,但为什么它不能与“Content-type”标头一起使用?【参考方案4】:

由于代码示例有点过时,我想我会分享一个更新的方法,使用 Angular 4.3 和新的(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

【讨论】:

你有这个的 html 吗?我喜欢这是使用 HttpParams。只是想知道你是否在某个地方有一个完整的工作示例。谢谢 这样如何将多个文件一起上传为一个数组?它应该如何附加到表单数据对象? 查看多部分表单数据webdavsystem.com/javaserver/doc/resumable_upload/multipart_post【参考方案5】:

在 Angular 2+ 中,将 Content-Type 留空是非常重要。如果您将“Content-Type”设置为“multipart/form-data”,则上传将不起作用!

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

【讨论】:

【参考方案6】:

我已经成功使用了以下工具。我与 primeNg 毫无关系,只是传递我的建议。

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

【讨论】:

请告诉我们这是否与 NG2 兼容?? @G1P 它肯定兼容 Angular 4。 primefaces.org/primeng/#/setup【参考方案7】:

这个简单的解决方案对我有用:file-upload.component.html

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

然后在组件中直接用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);
    
  


如果您使用的是 dotnet core,则参数名称必须与 from 字段名称匹配。在这种情况下的文件:

[HttpPost("[action]")]
public async Task<IList<FileDto>> UploadFiles(List<IFormFile> files)

  return await _binaryService.UploadFilesAsync(files);

这个答案抄袭http://blog.teamtreehouse.com/uploading-files-ajax

编辑: 上传后,您必须清除文件上传,以便用户可以选择新文件。而不是使用 XMLHttpRequest,也许使用 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

【讨论】:

【参考方案8】:

This is useful tutorial,如何使用 ng2-file-upload 和 WITHOUT ng2-file-upload 上传文件。

对我来说很有帮助。

目前,教程包含几个错误:

1- 客户端应该与服务器具有相同的上传 url, 所以在app.component.ts换行

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

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

2- 服务器发送响应为'text/html',所以在app.component.ts改变

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

【讨论】:

【参考方案9】:

上传带有表单域的图片

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

就我而言,我需要 C# 中的 .NET Web Api

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

【讨论】:

【参考方案10】:

今天我将ng2-file-upload包集成到我的angular 6应用程序中,非常简单,请找到下面的高级代码。

导入 ng2-file-upload 模块

app.module.ts

    import  FileUploadModule  from 'ng2-file-upload';

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

组件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);

      
    ------
    ------

组件 HTML 添加文件标签

app.component.html

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

在线工作 stackblitz 链接: https://ng2-file-upload-example.stackblitz.io

Stackblitz 代码示例: https://stackblitz.com/edit/ng2-file-upload-example

官方文档链接https://valor-software.com/ng2-file-upload/

【讨论】:

【参考方案11】:

尽量不要设置options参数

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

并确保您没有在 Http 工厂中设置 globalHeaders

【讨论】:

【参考方案12】:

jspdf 和 A​​ngular 8

我生成了一个 pdf 并想通过 POST 请求上传 pdf,我就是这样做的(为了清楚起见,我删除了一些代码和服务层)

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

【讨论】:

【参考方案13】:

我已使用参考上传文件。这种方式上传文件不需要打包。

//要写入.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,
            );
        );
  

//要写入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);

// 用html编写的代码

<input type="file" #fileInput>

【讨论】:

【参考方案14】:

在最简单的形式中,以下代码适用于 Angular 6/7

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

这里是complete implementation

【讨论】:

以上是关于在 Angular 中上传文件?的主要内容,如果未能解决你的问题,请参考以下文章

angular4 文件上传

如何在 Angular 6 中上传之前验证文件

如何在Angular2中上传文件

上传位于 Angular 项目目录中的文件/图像

在angular2中上传文件:正文(FormData)为空

如何使用添加按钮在 Angular 6 中上传多个文件?