前言
一个常见的前端开发任务就是上传文件,然而,这并不是一项容易的任务。文件上传涉及到诸多的安全、性能和用户体验问题,要想实现一个高质量的文件上传功能,需要经过仔细规划和实践。本文将会介绍多种基于 Angular 的文件上传方案,帮助前端开发者们掌握文件上传的技能。
前置知识
在学习本文之前,你需要了解以下 Angular 的基础知识:
- 声明周期钩子函数
- 双向数据绑定
- 服务
- RxJS
基本上传
基础的文件上传可以使用 HTTP POST 方法来向服务器发送文件。在 Angular 中,我们可以使用 HttpClient
模块来发起 HTTP 请求。
首先,需要在组件中引入 HttpClientModule
模块:
import { HttpClientModule } from '@angular/common/http';
然后,在组件中定义一个上传方法:
uploadFile(file: File) { const formData = new FormData(); formData.append('file', file); this.http.post('/upload', formData).subscribe(response => { console.log('File uploaded successfully!'); }); }
这个方法将会把文件以 FormData 的形式发送到服务器,并在上传完成后打印成功的信息。需要注意的是,HttpClient
会自动添加请求头信息,以确保上传过程的安全性。
带进度条的上传
用户上传一个大文件时,可能需要等待一段时间才能看到上传完成的信息。在这种情况下,为用户提供一个进度条是必要的。Angular 通过 HttpClient
和 RxJS 提供了一个简单的机制来实现带进度条的上传。
首先,在组件中创建一个 Subject
实例:
progress$ = new Subject<number>();
然后,我们可以使用以下方法来上传文件:
// javascriptcn.com 代码示例 uploadFileWithProgress(file: File) { const formData = new FormData(); formData.append('file', file); const req = new HttpRequest('POST', '/upload', formData, { reportProgress: true, }); return this.http.request(req).pipe( tap(event => { if (event.type === HttpEventType.UploadProgress) { const percentDone = Math.round((100 * event.loaded) / event.total); this.progress$.next(percentDone); } else if (event.type === HttpEventType.Response) { console.log('File uploaded successfully!'); } }) ); }
在这个方法中,我们使用 HttpRequest
和 HttpEventType
来实现带进度条的文件上传。tap
操作符用于拦截 RxJS 的流并对其进行处理。在每个上传进度事件到达时,我们通过先前创建的 Subject
发送当前的进度信息。当上传完成时,我们会得到服务器的响应,并打印成功信息。
最后,我们可以在组件的 HTML 中使用 async
和 ngIf
指令来显示进度条:
<ng-container *ngIf="progress$ | async as progress"> <div class="progress"> <div class="progress-bar" role="progressbar" aria-valuenow="{{ progress }}" aria-valuemin="0" aria-valuemax="100" [style.width.%]="progress"></div> </div> </ng-container>
可中断上传
上传一个较大的文件可能需要一些时间,而中途取消上传则可以大大减少带宽和时间的浪费。在 Angular 中,我们可以使用 HttpClient
提供的 Subscription
和 HttpInterceptor
来实现文件上传的取消功能。
首先,在组件中创建一个 Subscription
实例:
private uploadSubscription: Subscription;
然后,我们需要创建一个 HttpInterceptor
实现上传中断,将它返回对应的 Observable
即可。
// javascriptcn.com 代码示例 @Injectable() export class CancelInterceptor implements HttpInterceptor { private cancelPendingRequests$ = new Subject(); private isCancelInterceptorActive = false; constructor() {} onCancelPendingRequests() { this.cancelPendingRequests$.next(); } intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { if (req.body instanceof FormData) { return next.handle(req).pipe( takeUntil(this.cancelPendingRequests$), catchError(error => { if (error?.status === 0) { return of(error as HttpErrorResponse); } else { return throwError(error); } }) ) } else { return next.handle(req); } } }
在上面的CancelInterceptor
中,我们创建了一个Subject
cancelPendingRequests$
,该Subject
将用于中断上传。我们还使用takeUntil
和 of
操作符来中断请求。当错误的状态为 0 时,我们判定请求取消,并将这个错误返回为一个HttpErrorResponse
对象。
接下来,我们在组件中创建一个 cancelUpload
方法,该方法将通过导入上面的 CancelInterceptor
来实现上传的取消:
// javascriptcn.com 代码示例 constructor(private http: HttpClient, private cancelInterceptor: CancelInterceptor) {} uploadAndCancel(file: File) { const formData = new FormData(); formData.append('file', file); const req = new HttpRequest('POST', '/upload', formData, { reportProgress: true, }); this.uploadSubscription = this.http.request(req).pipe( takeUntil(this.cancelInterceptor.cancelPendingRequests$), catchError(error => { if (error?.status === 0) { console.log('Upload cancelled!'); return of(error as HttpErrorResponse); } else { return throwError(error); } }) ).subscribe(response => { console.log('File uploaded successfully!'); this.uploadSubscription.unsubscribe(); }); } cancelUpload() { this.cancelInterceptor.onCancelPendingRequests(); }
当上传中断时,takeUntil
操作符将停止上传,of
操作符将返回一个取消请求的错误信息。在执行取消操作之前,我们会在 uploadAndCancel
方法中为每个上传创建一个 Subscription
,并在完成后取消该 Subscription
。
总结
通过本文,我们介绍了在 Angular 中实现文件上传的多种方法,并且演示了如何实现进度条和中断上传的功能。通过学习这些技能,我们可以更好地掌握前端开发中的文件上传功能。希望这些知识可以对你的前端开发工作有所帮助!
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/6530e9607d4982a6eb27b53c