Angular中实现每行独立展示文件上传进度的技术求助
Angular多文件上传:实现单文件独立进度展示
问题分析
当前代码使用全局uploadStatus和uploading变量控制所有文件的状态展示,导致所有文件共享同一状态,无法区分单个文件的等待、上传中、完成状态,也无法展示单独的上传进度。
解决方案步骤
1. 定义单文件状态模型
创建接口存储每个文件的详细状态,替换原有的File[]数组为包含状态的对象数组:
// 在upload.component.ts中添加 interface UploadFile { file: File; status: 'pending' | 'uploading' | 'complete'; progress: number; // 0-100的进度值 downloadUrl?: string; // 上传完成后的访问链接 } // 替换原files变量 uploadFiles: UploadFile[] = [];
2. 重构文件选择逻辑
更新文件选择方法,将选中的文件包装为带初始状态的UploadFile对象:
// 修改onBrowse方法 onBrowse(event: any) { const selectedFiles = <File[]>event.target.files; this.processSelectedFiles(selectedFiles); } // 修改onFileDrop方法 onFileDrop(event: any) { const selectedFiles = <File[]>event; this.processSelectedFiles(selectedFiles); } // 新增文件处理方法 private processSelectedFiles(files: File[]) { const newUploadFiles = files.map(file => ({ file, status: 'pending', progress: 0 })); this.uploadFiles.push(...newUploadFiles); this.fileSelected = true; this.progressText = `${this.uploadFiles.length} File(s) selected. Click to upload.`; this.buttonText = 'Upload Files'; } // 删除原fileSelect方法
3. 调整MS Graph服务的进度通知逻辑
修改MSGraphService,添加单个文件进度的通知机制(如果服务可修改):
// 在MSGraphService中添加 import { Subject } from 'rxjs'; export class MSGraphService { private fileProgressSubject = new Subject<{index: number, progress: number}>(); fileProgress$ = this.fileProgressSubject.asObservable(); async upload_chunks(file: File, url: string, fileIndex: number) { // 上传过程中更新进度的回调 const updateProgress = (progress: number) => { this.fileProgressSubject.next({index: fileIndex, progress}); }; // 原分块上传逻辑,在合适的节点调用updateProgress更新进度 // ... 原有上传代码 ... // 上传完成后返回下载链接(若有) return downloadUrl; } }
在组件中订阅进度更新:
// 在upload.component.ts的ngOnInit中添加 ngOnInit(): void { this.progressText = 'Drop your files here or Click...'; this.buttonText = 'Browse Files'; // 订阅单个文件进度更新 this.ms_graph.fileProgress$.subscribe(({index, progress}) => { if (this.uploadFiles[index]) { this.uploadFiles[index].progress = progress; this.uploadFiles[index].status = 'uploading'; this.changeDetection.detectChanges(); } }); }
4. 重构上传流程,跟踪单个文件状态
更新上传方法,逐个处理文件并更新对应状态:
// 修改onUpload方法 onUpload() { this.uploading = true; this.progressText = "上传中..."; this.uploadFile(); } // 重写uploadFile方法 async uploadFile() { // 批量创建上传会话 const sessionPromises = this.uploadFiles.map((uploadFile) => { const { file } = uploadFile; const fileNameArray = file.name.lastIndexOf('.'); const fileType = file.name.substring(fileNameArray + 1); const fileName = file.name.substring(0, fileNameArray); return this.ms_graph.get_upload_session(fileType, fileName, this.itemId); }); const sessionResponses = await forkJoin(sessionPromises).toPromise(); // 逐个上传文件 for (let i = 0; i < this.uploadFiles.length; i++) { const uploadFile = this.uploadFiles[i]; const sessionUrl = sessionResponses[i].uploadUrl; try { const downloadUrl = await this.ms_graph.upload_chunks(uploadFile.file, sessionUrl, i); // 上传完成更新状态 uploadFile.status = 'complete'; uploadFile.downloadUrl = downloadUrl; this.toastr.success(`${uploadFile.file.name} 上传成功`, '上传完成', {closeButton: true}); } catch (error) { // 可选:处理上传失败,比如设置status为'failed' uploadFile.status = 'pending'; this.toastr.error(`${uploadFile.file.name} 上传失败`, '上传错误', {closeButton: true}); } this.changeDetection.detectChanges(); } this.fileSelected = false; this.uploading = false; } // 删除原uploadFiles方法及冗余变量(files、fileList、sessionUrls等)
5. 更新HTML模板,渲染单个文件状态
修改模板,遍历uploadFiles数组,根据每个文件的状态动态展示内容:
<div class="upload-table"> <table class="table"> <thead> <tr> <th scope="col">Name</th> <th scope="col">File Size</th> <th>Upload Status</th> </tr> </thead> <tbody class="upload-name-style"> <tr *ngFor="let item of uploadFiles; let i = index" id="{{ i }}"> <td> <strong>{{ item.file.name }}</strong> </td> <td> <strong>{{ item.file.size | filesize }}</strong> </td> <td> <!-- 上传中状态:加载动画+进度百分比 --> <span *ngIf="item.status === 'uploading'"> <i class="fas fa-spinner fa-pulse"></i> {{ item.progress }}% </span> <!-- 等待状态 --> <span *ngIf="item.status === 'pending'">Pending...</span> <!-- 完成状态:显示查看链接 --> <span *ngIf="item.status === 'complete'"> Complete! <a *ngIf="item.downloadUrl" href="{{ item.downloadUrl }}" target="_blank">(view file)</a> </span> </td> </tr> </tbody> </table> </div>
核心逻辑说明
- 通过单个文件状态对象替代全局状态变量,实现每个文件独立的状态跟踪
- 利用RxJS Subject在上传服务中发送单个文件的进度更新,组件订阅后实时更新对应文件状态
- HTML模板根据文件的
status字段动态渲染不同状态内容,包括进度百分比、加载动画和完成后的查看链接
内容的提问来源于stack exchange,提问作者MD40
相关产品推荐
相关产品推荐

