65.9K
CodeProject 正在变化。 阅读更多。
Home

Angular 9/8 教程和示例 — 使用 FormData、HttpClient、RxJS 和 Material ProgressBar 上传文件

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2019 年 12 月 15 日

CPOL

9分钟阅读

viewsIcon

9363

如何使用 FormData、HttpClient(用于发布 multipart/form-data)、Angular 9/8 和 TypeScript 上传多个图像文件

Angular 9/8 Tutorial & Example — Upload files with FormData, HttpClient, RxJS, and Material ProgressBar

在本教程中,我们将通过示例学习如何使用 FormDataHttpClient(用于发布 multipart/form-data)、Angular 9/8 和 TypeScript 上传多个图像文件。

我们将学习如何使用 Angular Material ProgressBar 在上传图像时指示活动,以及如何结合 RxJS map() 方法使用 HttpClient 来监听文件上传进度事件。

通过本教程,您将掌握以下技能:

  • 如何在 TypeScript 和 Angular 8 中上传单个和多个图像文件
  • 如何在 Angular 8 项目中设置 HttpClient
  • 如何使用 HttpClient 发送带有 multipart/form-dataPOST 请求
  • 如何使用 HttpClient 监听文件上传进度事件
  • 如何在 TypeScript 中使用 FormData 创建表单
  • 如何使用 Angular Material 的 MatProgressBar 组件实时指示文件上传百分比
  • 如何使用各种 RxJS 运算符,如 map()catchError()

本教程的步骤如下:

  • 步骤 1 — 设置 Angular CLI v8
  • 步骤 2 — 初始化新的 Angular 8 项目
  • 步骤 3 — 设置 Angular HttpClient
  • 步骤 4 — 创建 Angular 组件
  • 步骤 5 — 添加 Angular 路由
  • 步骤 6 — 设置 Angular Material
  • 步骤 7 — 创建 Angular 文件上传服务
  • 步骤 8 — 使用 Material Icon、Card、Button 和 ProgressBar 组件创建文件上传 UI

我们不会为文件上传创建服务器应用程序,因为这超出了本教程的范围。相反,我们将使用 https://file.io 这个在线文件上传和共享服务。

让我们从 FormData 的快速介绍开始。

什么是 FormData?

FormData 是一种数据结构,可用于存储键值对。它旨在用于保存表单数据,并可与 JavaScript 一起使用来构建与 HTML 表单对应的对象。当您需要将表单数据发送到 RESTful API 端点时,它最有用,例如,使用 XMLHttpRequest 接口或任何 HTTP 客户端库上传单个或多个文件。

您可以通过使用 new 运算符实例化 FormData 接口来创建 FormData 对象,如下所示:

const formData = new FormData()

formData 引用指向 FormData 的一个实例。您可以在对象上调用许多方法来添加和处理数据对。每对都有一个键和值。

这些是 FormData 对象上可用的方法:

  • append():用于将键值对附加到对象。如果键已存在,则该值将附加到该键的原始值。
  • delete():用于删除键值对。
  • entries():返回一个 Iterator 对象,您可以使用它遍历对象中的键值对列表。
  • get():用于返回键的值。如果附加了多个值,则返回第一个值。
  • getAll():用于返回指定键的所有值。
  • has():用于检查是否存在键。
  • keys():返回一个 Iterator 对象,您可以使用它列出对象中可用的键。
  • set():用于将值添加到对象,并指定键。如果键已存在,这将替换该值。
  • values():返回 FormData 对象值的 Iterator 对象。

现在,让我们从先决条件开始本教程。

必备组件

在本教程中,我们将使用 Angular 8 创建一个示例应用程序,因此您需要满足一些先决条件:

  • 已安装 Node.JS 和 NPM 的开发环境
  • TypeScript 的基本知识。特别是熟悉面向对象概念,如 TypeScript 类和装饰器。
  • 本地开发机器已安装 Node 8.9+ 以及 NPM 5.5.1+。Node 是 Angular CLI 以及当前大多数前端工具所必需的。您可以直接访问官方网站的下载页面,下载适用于您操作系统的二进制文件。您也可以参考特定系统的说明,了解如何使用包管理器安装 Node。然而,推荐的方法是使用 NVM — Node 版本管理器 — 一个符合 POSIX 标准的 bash 脚本,用于管理多个活动的 Node.js 版本。

注意:如果您不想为 Angular 开发安装本地环境,但仍想尝试本教程中的代码,可以使用 Stackblitz,这是一个在线前端开发 IDE,您可以用它来创建与 Angular CLI 兼容的 Angular 项目。

步骤 1 — 设置 Angular CLI v8

在此步骤中,我们将安装最新的 Angular CLI 8 版本(撰写本教程时)。

注意:这些说明也适用于 Angular 9。

Angular CLI 是用于初始化和使用 Angular 项目的官方工具。要安装它,请打开一个新的命令行界面并运行以下命令:

$ npm install -g @angular/cli

在撰写本教程时,您的系统上将安装 angular/cli v8.3.2

在下一步中,我们将学习如何从终端初始化一个新的示例项目。

步骤 2 — 初始化新的 Angular 8 项目

安装 Angular CLI 后,让我们创建示例项目。回到终端并运行以下命令:

$ cd ~  
$ ng new angular-upload-example

CLI 会问您几个问题——如果 **您想添加 Angular 路由吗?** 输入 **y** 表示是,以及 **您想使用哪种样式表格式?** 选择 **CSS**。

这将指示 CLI 自动在我们的项目中设置路由,因此我们只需为组件添加路由即可在应用程序中实现导航。

接下来,导航到您的项目文件夹并使用以下命令运行本地开发服务器:

$ cd angular-upload-example  
$ ng serve

本地开发服务器将开始在 https://:4200/ 地址监听。

打开您的网页浏览器并导航到 https://:4200/ 地址,即可看到您的应用程序正在运行。这是此时的屏幕截图:

您现在应该让开发服务器保持运行,并启动一个新的终端来运行后续步骤的 CLI 命令。

步骤 3 — 设置 Angular HttpClient

使用 Angular CLI 初始化 Angular 项目后,让我们继续在示例中设置 HttpClient

HttpClient 存在于一个单独的 Angular 模块中,因此在使用它之前,我们需要在主应用程序模块中导入它。

用代码编辑器或 IDE 打开您的示例项目。我将使用 Visual Studio Code

接下来,打开 src/app/app.module.ts 文件,导入 HttpClientModule 并将其添加到模块的 imports 数组中,如下所示:

import { BrowserModule } from '@angular/platform-browser';  
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';  
import { AppComponent } from './app.component';  
import { HttpClientModule } from '@angular/common/http';

@NgModule({  
  declarations: [  
    AppComponent,  
  ],  
  imports: [  
    BrowserModule,  
    AppRoutingModule,  
    HttpClientModule  
  ],  
  providers: [],  
  bootstrap: [AppComponent]  
})  
export class AppModule { }

就这样,我们现在可以使用项目中的 HttpClient 服务了,但首先,我们需要创建应用程序的两个组件:home 和 about。

步骤 4 — 创建 Angular 组件

设置 Angular HttpClient 后,让我们创建控制应用程序 UI 的 Angular 组件。

回到一个新的终端并运行以下命令:

$ cd ~/angular-upload-example  
$ ng generate component home

CLI 为该组件创建了四个文件,并将其添加到 src/app/app.module.ts 文件中的 declarations 数组中。

接下来,让我们使用以下命令创建 about component

$ ng generate component about

接下来,打开 src/app/about/about.component.html 并添加以下代码:

<p style="padding: 13px;">  
   Angular 8 tutorial & example —  How to upload multiple image files with FormData & HttpClient  
</p>

我们将在后续步骤中处理 home 组件。

步骤 5 — 添加 Angular 路由

创建 Angular 组件后,让我们将它们添加到路由器。

转到 src/app/app-routing.module.ts 文件,即路由配置,并导入组件,然后添加以下路由:

import { NgModule } from '@angular/core';  
import { Routes, RouterModule } from '@angular/router';  
import { HomeComponent } from './home/home.component';  
import { AboutComponent } from './about/about.component';

const routes: Routes = [  
  { path: '', redirectTo: 'home', pathMatch: 'full'},  
  { path: 'home', component: HomeComponent },  
  { path: 'about', component: AboutComponent },  
];

@NgModule({  
  imports: [RouterModule.forRoot(routes)],  
  exports: [RouterModule]  
})  
export class AppRoutingModule { }

步骤 6 — 设置 Angular Material

在为应用程序的各个组件添加路由后,让我们看看如何添加 Angular Material 来设置 UI 样式。

Angular Material 提供了 Material Design 组件,允许开发人员创建专业的 UI。

进入您的终端,从项目根文件夹运行以下命令:

$ ng add @angular/material

系统会提示您选择一个主题,我们选择 Indigo/Pink

对于其他问题——**是否设置 HammerJS 用于手势识别?** 和 **是否设置 Angular Material 的浏览器动画?** 在键盘上按 **Enter** 选择默认答案。

接下来,打开 src/styles.css 文件并添加一个主题:

@import "~@angular/material/prebuilt-themes/indigo-pink.css";

每个 Angular Material 组件都存在于其单独的模块中,在使用该组件之前您需要导入它。打开 src/app/app.module.ts 文件并添加以下导入:

import { MatToolbarModule,  
  MatIconModule,  
  MatCardModule,  
  MatButtonModule,  
  MatProgressBarModule } from '@angular/material';

我们导入了以下模块:

  • MatIcon 使在您的应用程序中使用基于矢量的图标变得容易。
  • MatToolbar 包含标题、标题或操作的容器。
  • MatCard 包含用于文本、照片和单一主题上下文中的操作的内容容器。
  • MatButton 包含一个带有 Material Design 样式和墨水涟漪增强的原生 <button><a> 元素。
  • MatProgressBar 包含一个水平进度条,用于指示进度和活动。

接下来,您需要将这些模块添加到 imports 数组中:

@NgModule({  
  declarations: [  
    AppComponent,  
    HomeComponent,  
    AboutComponent  
  ],  
  imports: [  
    BrowserModule,  
    AppRoutingModule,  
    HttpClientModule,  
    BrowserAnimationsModule,  
    MatToolbarModule,  
    MatIconModule,  
    MatButtonModule,  
    MatCardModule,  
    MatProgressBarModule  
  ],  
  providers: [],  
  bootstrap: [AppComponent]  
})  
export class AppModule { }

接下来,让我们为应用程序添加一个工具栏。转到 src/app/app.component.html 文件并添加以下代码:

<mat-toolbar color="primary">  
  <h1>  
    ngImageUpload   
  </h1>  
  <button mat-button routerLink="/">Home</button>  
  <button mat-button routerLink="/about">About</button>
</mat-toolbar>
<router-outlet></router-outlet>

我们创建了应用程序的外壳,其中包含一个带有两个导航按钮的顶部栏,用于主页和关于组件。

到目前为止,我们教程的回顾:

  • 我们已经安装了 Angular CLI,初始化了一个新项目并创建了几个带有路由的组件。
  • 我们已经在项目中配置了 HttpClient 和 Angular Material,并添加了一个包含顶部栏和导航的应用程序外壳。

步骤 7 — 创建 Angular 文件上传服务

现在,让我们创建一个 Angular 服务,用于封装项目中图像文件上传的代码。

回到终端并运行以下命令以生成一个新服务:

$ ng generate service upload

接下来,打开 src/app/upload.service.ts 文件,首先添加以下导入:

import { HttpClient, HttpEvent, HttpErrorResponse, HttpEventType } 
       from  '@angular/common/http';  
import { map } from  'rxjs/operators';

接下来,注入 HttpClient 并定义 SERVER_URL 变量,该变量将包含文件上传服务器的地址:

@Injectable({  
  providedIn: 'root'  
})  
export class UploadService { 
	SERVER_URL: string = "https://file.io/";  
	constructor(private httpClient: HttpClient) { }

接下来,添加 upload() 方法,该方法只需调用 HttpClientpost() 方法即可向文件上传服务器发送带有表单数据的 HTTP POST 请求:

public upload(formData) {

	return this.httpClient.post<any>(this.SERVER_URL, formData, {  
      reportProgress: true,  
      observe: 'events'  
    });  
}</any>

步骤 9 — 使用 Material Icon、Card、Button 和 ProgressBar 组件创建文件上传 UI

在创建负责将 FormData 发送到文件上传服务器的服务之后,现在让我们创建用于将图像上传到服务器的 UI。

打开 src/app/home/home.component.ts 文件,首先添加以下 import 语句:

import { Component, OnInit, ViewChild, ElementRef  } from '@angular/core';
import { HttpEventType, HttpErrorResponse } from '@angular/common/http';
import { of } from 'rxjs';  
import { catchError, map } from 'rxjs/operators';  
import { UploadService } from  '../upload.service';

接下来,定义 fileUploadfiles 变量并注入 UploadService,如下所示:

@Component({  
  selector: 'app-home',  
  templateUrl: './home.component.html',  
  styleUrls: ['./home.component.css']  
})  
export class HomeComponent implements OnInit {
    @ViewChild("fileUpload", {static: false}) fileUpload: ElementRef;files  = [];  
    constructor(private uploadService: UploadService) { }

接下来,定义 uploadFile() 方法:

uploadFile(file) {  
    const formData = new FormData();  
    formData.append('file', file.data);  
    file.inProgress = true;  
    this.uploadService.upload(formData).pipe(  
      map(event => {  
        switch (event.type) {  
          case HttpEventType.UploadProgress:  
            file.progress = Math.round(event.loaded * 100 / event.total);  
            break;  
          case HttpEventType.Response:  
            return event;  
        }  
      }),  
      catchError((error: HttpErrorResponse) => {  
        file.inProgress = false;  
        return of(`${file.data.name} upload failed.`);  
      })).subscribe((event: any) => {  
        if (typeof (event) === 'object') {  
          console.log(event.body);  
        }  
      });  
  }

我们创建了一个 FormData 实例,并将文件附加到一个名为 file 的字段。此键的名称需要与您的服务器期望找到文件的位置一致,否则服务器将无法提取文件。

接下来,我们通过调用 UploadServiceUpload() 方法将表单数据发送到服务器。

接下来,定义可用于上传多个图像文件的 uploadFiles() 方法:

private uploadFiles() {  
    this.fileUpload.nativeElement.value = '';  
    this.files.forEach(file => {  
      this.uploadFile(file);  
    });  
}

接下来,定义 onClick() 方法:

onClick() {  
    const fileUpload = this.fileUpload.nativeElement;fileUpload.onchange = () => {  
    for (let index = 0; index < fileUpload.files.length; index++)  
    {  
     const file = fileUpload.files[index];  
     this.files.push({ data: file, inProgress: false, progress: 0});  
    }  
      this.uploadFiles();  
    };  
    fileUpload.click();  
}

接下来,我们需要创建图像上传 UI 的 HTML 模板。打开 src/app/home/home.component.html 文件并添加以下内容:

<div style="text-align:center; margin-top: 100px; ">
    <mat-card style="margin-top:10px; width: 50%;">  
        <mat-card-content>  
            <ul>  
                <li *ngFor="let file of files">  
                    <mat-progress-bar [value]="file.progress"></mat-progress-bar>  
                    <span id="file-label">  
                          
                    </span>  
                </li>  
            </ul>  
        </mat-card-content>  
        <mat-card-actions>  
            <button mat-button color="warn" (click)="onClick()">  
                <mat-icon>file_upload</mat-icon>  
                Upload  
            </button>  
        </mat-card-actions>  
    </mat-card><input type="file" #fileUpload id="fileUpload" 
    name="fileUpload" multiple="multiple" accept="image/*" style="display:none;" /></div>

接下来,打开 src/app/home/home.component.css 文件并添加以下 CSS 代码:

ul,  
li {  
      margin: 0;  
      padding: 0;  
      list-style: none;  
}

这是我们应用程序上传四个图像文件时的屏幕截图:

结论

作为本教程的总结,我们学习了如何使用 HttpClientFormData 发送 POST 请求,将单个和多个图像文件上传到服务器。我们使用了各种 Angular Material 组件,如 MatCardMatButtonMatIconMatProgressBar 来创建 UI 布局。

© . All rights reserved.