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





5.00/5 (2投票s)
如何使用 FormData、HttpClient(用于发布 multipart/form-data)、Angular 9/8 和 TypeScript 上传多个图像文件
在本教程中,我们将通过示例学习如何使用 FormData
、HttpClient
(用于发布 multipart/form-data)、Angular 9/8 和 TypeScript 上传多个图像文件。
我们将学习如何使用 Angular Material ProgressBar
在上传图像时指示活动,以及如何结合 RxJS map()
方法使用 HttpClient
来监听文件上传进度事件。
通过本教程,您将掌握以下技能:
- 如何在 TypeScript 和 Angular 8 中上传单个和多个图像文件
- 如何在 Angular 8 项目中设置
HttpClient
- 如何使用
HttpClient
发送带有 multipart/form-data 的POST
请求 - 如何使用
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()
方法,该方法只需调用 HttpClient
的 post()
方法即可向文件上传服务器发送带有表单数据的 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';
接下来,定义 fileUpload
和 files
变量并注入 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
的字段。此键的名称需要与您的服务器期望找到文件的位置一致,否则服务器将无法提取文件。
接下来,我们通过调用 UploadService
的 Upload()
方法将表单数据发送到服务器。
接下来,定义可用于上传多个图像文件的 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;
}
这是我们应用程序上传四个图像文件时的屏幕截图:
结论
作为本教程的总结,我们学习了如何使用 HttpClient
和 FormData
发送 POST 请求,将单个和多个图像文件上传到服务器。我们使用了各种 Angular Material 组件,如 MatCard
、MatButton
、MatIcon
和 MatProgressBar
来创建 UI 布局。