在 Angular 5 中使用文件上传保存 FormData

Posted

技术标签:

【中文标题】在 Angular 5 中使用文件上传保存 FormData【英文标题】:Save FormData with File Upload in Angular 5 【发布时间】:2018-07-06 05:48:09 【问题描述】:

我正在尝试将文件与 Angular 5 中的 FormData 一起保存。 我可以获得单个文件,但不知道如何上传所有文件。 我有三个图像文件和输入字段,尝试搜索示例。但仅适用于多个文件上传。 我想从这个表单上传每一个文件。

下面是我的代码:

import  Component, OnInit, ViewEncapsulation  from '@angular/core';
import  Location  from '@angular/common';
import  ActivatedRoute  from '@angular/router';
import  Category  from '../../../shared/services/categories/category';
import  CategoriesService  from '../../../shared/services/categories/categories.service';

@Component(
  selector: 'app-add-category',
  templateUrl: './add-category.component.html',
  styleUrls: ['./add-category.component.scss'],
  encapsulation: ViewEncapsulation.None
)
export class AddCategoryComponent implements OnInit 
  
  category: Category = new Category();
  fileToUpload: File = null;
  
  constructor(
    private categoriesService: CategoriesService,
    private route: ActivatedRoute,
    private location: Location  
  )  

  ngOnInit() 
  

  goBack(): void 
    this.location.back();
  

  handleFileInput(files: FileList) 
    console.log(files);
  

  addCategory() 
    console.log(this.category);
    this.categoriesService.createCategory(this.category).subscribe(() => this.goBack());
  

          <h3 class="box-title">Category</h3>

        <form role="form" (ngSubmit)="addCategory()" #categoryForm="ngForm">

              <div class="box-body">

                <div class="row">
                  <div class="col-lg-6">
                  <label for="Category Name">Name</label>
                  <input type="text" class="form-control" [(ngModel)]="category.category_name" name="category_name" id="category_name" placeholder="Enter Category Name" required="">
                </div>

                <div class="col-lg-6">
                  <label for="Category Path">Path</label>
                  <input type="text" class="form-control" [(ngModel)]="category.category_path" name="category_path" id="category_path" required="">
                </div>
              </div>
            </div>
            <br/>

            <div class="form-group">
                  <label for="Category Description">Description</label>
                  <textarea rows="3" [(ngModel)]="category.category_description" name="category_description" id="category_description" class="form-control" required=""></textarea>
            </div>
                
            <div class="col-lg-12 text-center">
                <input type="file" [(ngModel)]="category.category_banner" (change)="handleFileInput($event.target.files)" class="custom-file-input" name="category_banner" id="category_banner">
                <label class="custom-file-label" for="customFile">Banner</label>
            </div>
            <br/>

            <div class="form-group">
              <label for="Category Banner Code">Banner Code</label>
              <textarea rows="3" [(ngModel)]="category.category_banner_code" name="category_banner_code" id="category_banner_code" class="form-control" required=""></textarea>
            </div>
            
            <br/>
            
            <div class="col-lg-12">
                <input type="file" [(ngModel)]="category.category_image" (change)="handleFileInput($event.target.files)" class="custom-file-input" name="category_image" id="category_image">
                <label class="custom-file-label" for="customFile">Image</label>
            </div>
        
            <br/>

            <div class="col-lg-12">
                <input type="file" [(ngModel)]="category.category_icon" (change)="handleFileInput($event.target.files)" class="custom-file-input" name="category_icon" id="category_icon">
                <label class="custom-file-label" for="customFile">Icon</label>
            </div>

              <div class="form-group">
              <label for="Category Meta Title">Meta Title</label>
              <input type="text" [(ngModel)]="category.category_meta_title" class="form-control" name="category_meta_title" id="category_meta_title" placeholder="Enter Meta Title" required="">
            </div>

            <div class="form-group">
              <label for="Category Meta Description">Meta Description</label>
              <input type="text" [(ngModel)]="category.category_meta_decription" class="form-control" id="category_meta_description" name="category_meta_description" placeholder="Enter Meta Description" required="">
            </div>

            <div class="form-group">
              <label for="Category Meta Keyword">Meta Keyword</label>
              <input type="text" [(ngModel)]="category.category_meta_keyword" class="form-control" id="category_meta_keyword" name="category_meta_keyword" placeholder="Enter Meta Keyword" required="">
            </div>

            <div class="form-group">
             
              <div class="row">
                 <div class="col">Featured :</div>
                <div class="col">
                <label class="radio-inline" for="Category Featured">
                  <input type="radio" [(ngModel)]="category.category_featured" name="category_featured" id="category_featured" value="1" required="">Yes
                </label>
              </div>

              <div class="col">
                <label class="radio-inline" for="Category Featured">
                  <input type="radio" [(ngModel)]="category.category_featured" name="category_featured" id="category_featured" value="0" required="">No
                </label>
            </div>
            </div>
            </div>
              
            <input type="hidden" [(ngModel)]="category.category_status" name="category_status" id="category_status" value="1">

              <div class="box-footer col-md-12">
                <button type="submit" class="btn btn-primary">Submit</button>
              </div>
            </form>

【问题讨论】:

【参考方案1】:

以下是我如何处理来自单个文件输入的多个文件。 我的组件收集表单数据,并生成一个不包含文件的 Data 对象。然后,它使用 Data 对象和文件调用此服务方法,从而在多部分帖子中发送数据和文件。

  save(data: Data, filesForUpload: File[]): Observable<Data> 
    const formData = new FormData();

    // add the files
    if (filesForUpload && filesForUpload.length) 
      filesForUpload.forEach(file => formData.append('files', file));
    

    // add the data object
    formData.append('data', new Blob([JSON.stringify(data)], type: 'application/json'));

    return this.http.post<Data>(this.apiUrl, formData);
  

因此,要处理两个文件输入,您可以这样做:

 save(data: Data, filesA: File[], filesB: File[]): Observable<Data> 
    const formData = new FormData();

    // add the files
    if (filesA && filesA.length) 
      filesA.forEach(file => formData.append('filesA', file));
    

    if (filesB && filesB.length) 
      filesB.forEach(file => formData.append('filesB', file));
    

    // add the data object
    formData.append('data', new Blob([JSON.stringify(data)], type: 'application/json'));

    return this.http.post<Data>(this.apiUrl, formData);
  

这将在您的多部分帖子中为您提供三个部分,一个用于每组文件,一个用于数据对象。

【讨论】:

我不想处理来自单个文件输入的多个文件。我不清楚 filesA 和 filesB 示例我试过这个:code' handleFileInput(files: FileList) this.fileToUpload = files.item(0); 'code 我有三个图像上传按钮,我想将它们与 formData 一起发送。 单文件上传和多文件上传一样,都是在数组中,但数组中只有一项。或者,您可以通过files.item(0); 传入一项。在示例中,假设有两个输入,我将传入两个文件数组。如果有三个输入,您将传递三个数组(或三个单独的项目)。我在 POST 中单独发送它们的原因是您可以在服务器端知道哪个文件来自哪个输入。【参考方案2】:

这是我尝试过的,它按预期工作:

handleCategoryBanner(files: FileList) 
    this.category.category_banner = '/categories/download/' + files[0].name;
    this.formData.append('category_banner', files[0], files[0].name);
    this.categoryContainersService.uploadFile(this.formData).subscribe(filename => console.log(files[0].name));
  
<div class="col-lg-12 text-center">
        <input type="file" (change)="handleCategoryBanner($event.target.files)" class="custom-file-input" id="category_banner" accept=".jpeg,.png,.jpg">
        <input type="hidden" name="category_banner" [(ngModel)]="category.category_banner" />
        <label class="custom-file-label" for="customFile">Banner</label>
    </div>

【讨论】:

【参考方案3】:

只需在输入标签上添加“多个”属性 现在通过在浏览时选择 2 个或更多文件,您将获得对象应该对其进行迭代以提取图像。 像这样-->

 <div class="form-group">
    <label for="file">Choose File</label>
    <input type="file" id="file" (change)="handleFileInput($event.target.files)" multiple>
</div>

【讨论】:

上传的输入框不同【参考方案4】:

我最近遇到了类似的问题。这可以通过在 Angular 代码中将 header 的内容类型设置为 null 来解决。 附上Angular5和spring boot backend的代码sn-p。

let headers = new HttpHeaders();
//this is the important step. You need to set content type as null
headers.set('Content-Type', null);
headers.set('Accept', "multipart/form-data");
let params = new HttpParams();
const formData: FormData = new FormData();
for (let i = 0; i < this.filesList.length; i++) 
  formData.append('fileArray', this.filesList[i], this.filesList[i].name);
 
formData.append('param1', this.param1);
formData.append('param2', this.param2);
this.http.post(this.ROOT_URL + this.SERVICE_ENDPOINT, formData,  params, headers ).subscribe((res) => 
    console.log(res);
);



In the spring boot backend, you need to have the controller as - 

@RequestMapping(value = "/uploadAndSendEmail", method = RequestMethod.POST, consumes= "multipart/form-data")    
public ResponseEntity<String> uploadAndSendEmail(@RequestParam("fileArray") MultipartFile[] fileArray, 
        @RequestParam(value = "param1", required = false) String param1,
        @RequestParam(value = "param2", required = false) String param2) 
        //do your logic
        

【讨论】:

【参考方案5】:

试试这个代码:

handleFileInput(files)
     for (let j = 0; j < files.length; j++) 
      let data = new FormData();
      let fileItem = files[j]._file;
      console.log(fileItem.name);
      data.append('file', fileItem);
    

【讨论】:

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

Angular 5 文件上传:无法在“HTMLInputElement”上设置“值”属性

如何使用 Mongoose、Express、Angular 4 和 NodeJS 上传/保存和显示图片

Angular、Node.js、MySQL 图片上传

java项目,我上传dbf文件,解析文件数据保存到数据库里,数据量太大,速度太慢

将上传图片的路径保存在数据库中

以角度 5 上传前的图像预览