You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 15:27:21