【问题标题】:Angular upload progress is not reporting correctly角度上传进度未正确报告
【发布时间】:2020-04-18 11:33:51
【问题描述】:

我正在尝试构建一个受这篇文章启发的上传表单:

https://malcoded.com/posts/angular-file-upload-component-with-express/

我正在使用 Angular 9 和 ngx-bootstrap,但我没有使用对话框或 Angular 材质。 所以,我征收了这样的上传服务:

import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs';
import {
  HttpClient,
  HttpRequest,
  HttpEventType,
  HttpResponse,
} from '@angular/common/http';

import { environment } from '@environments/environment';

@Injectable({
  providedIn: 'root',
})
export class UploadService {
  private url: string = `${environment.apiUrl}/halls/file`;

  constructor(private http: HttpClient) {}

  public upload(
    files: Set<File>
  ): { [key: string]: { progress: Observable<number> } } {
    // this will be the our resulting map
    const status: { [key: string]: { progress: Observable<number> } } = {};

    files.forEach((file) => {
      // create a new multipart-form for every file
      const formData: FormData = new FormData();
      formData.append('file', file, file.name);

      // create a http-post request and pass the form
      // tell it to report the upload progress
      const req = new HttpRequest('POST', this.url, formData, {
        reportProgress: true,
      });

      // create a new progress-subject for every file
      const progress = new Subject<number>();

      // send the http-request and subscribe for progress-updates
      this.http.request(req).subscribe((event) => {
        if (event.type === HttpEventType.UploadProgress) {
          // calculate the progress percentage
          const percentDone = Math.round((100 * event.loaded) / event.total);

          // pass the percentage into the progress-stream
          progress.next(percentDone);
        } else if (event instanceof HttpResponse) {
          // Close the progress-stream if we get an answer form the API
          // The upload is complete
          progress.complete();
        }
      });

      // Save every progress-observable in a map of all observables
      status[file.name] = {
        progress: progress.asObservable(),
      };
    });

    // return the map of progress.observables
    return status;
  }
}

我在服务中唯一改变的是网址(原因很明显)。 然后我创建了一个组件来使用这个服务:

import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
import { Observable, forkJoin } from 'rxjs';

import { UploadService } from '@services';
import { Screenshot } from '../_core/models/screenshot';

@Component({
  selector: 'app-screenshots-save',
  templateUrl: './screenshots-save.component.html',
  styleUrls: ['./screenshots-save.component.scss'],
})
export class ScreenshotsSaveComponent implements OnInit {
  @ViewChild('file', { static: false }) file: ElementRef;

  public screenshots: Screenshot[] = [];
  public files: Set<File> = new Set();
  public urls: string[] = [];
  public progress: { [key: string]: { progress: Observable<number> } } = {};
  public uploading: boolean = false;
  public uploadSuccessful: boolean = false;

  constructor(private uploadService: UploadService) {}

  ngOnInit(): void {}

  onFilesAdded(event: any): void {
    const files: { [key: string]: File } = this.file.nativeElement.files;
    for (let key in files) {
      if (!isNaN(parseInt(key))) {
        var f = files[key];
        let reader = new FileReader();

        reader.onload = (e: any) => {
          this.urls.push(e.target.result);
        };

        this.files.add(f);
        reader.readAsDataURL(f);
      }
    }
  }

  addFiles(): void {
    this.file.nativeElement.click();
  }

  upload(): void {
    this.uploading = true;
    this.progress = this.uploadService.upload(this.files);

    let allProgressObservables = [];
    for (let key in this.progress) {
      allProgressObservables.push(this.progress[key].progress);
    }

    forkJoin(allProgressObservables).subscribe(() => {
      this.uploadSuccessful = true;
      this.uploading = false;
    });
  }

  clear(): void {
    this.files = new Set();
    this.urls = [];
    this.progress = {};
    this.uploading = false;
    this.uploadSuccessful = false;
  }
}

这与他的略有不同,因为它不使用对话框,但大部分是相同的。主要部分是upload 方法。您可以看到我在this.progress = this.uploadService.upload(this.files);这一行中将进度附加到上传服务。

在我的 html 中,我已经这样做了:

<form>
    <div class="d-none">
        <label>Screenshots</label>
        <input #file class="form-control-file" type="file" multiple accept=".jpg,.png"
            (change)="onFilesAdded($event)" />
    </div>

    <div class="form-group">
        <button class="btn btn-dark mr-2" type="button" (click)="addFiles()">Add
            screenshot(s)</button>
        <button class="btn btn-primary mr-2" type="button" [disabled]="!files.size"
            (click)="upload()">Upload</button>
        <button class="btn btn-danger" type="button" [disabled]="!files.size"
            (click)="clear()">Clear</button>
    </div>
</form>

<div class="row">
    <div class="col-md-4" *ngFor="let file of files; let i = index">
        <div class="card mb-4">
            <img class="card-img-top" [src]="urls[i]" [alt]="file.name">
            <div class="card-body">
                <h5 class="card-title">{{ file.name }}</h5>

                <progressbar [value]="progress[file.name]?.progress | async" [striped]="true"
                    [animate]="true">
                </progressbar>
            </div>
        </div>
    </div>
</div>

如您所见,我将进度绑定到 ngx-bootstrap 进度条。当我添加我的文件时,它应该显示为 0。但是当我按下上传按钮时,所有文件的进度都显示为 100,即使它实际上还没有完成上传。在它移动到我的下一个屏幕之前有一个延迟,这意味着进度条没有像我预期的那样改变。它是 0 或 100,而不是增量。

谁能发现我做错了什么?

【问题讨论】:

  • 您找到解决方案了吗?我也有这个问题。

标签: angular typescript upload reportprogress


【解决方案1】:

我也有同样的问题,但不是 Angular 问题。问题是你的internet连接上传速度太快,所以从0到100的速度非常快,所以上传服务一眨眼就从next(0)到next(100)。因此,服务器没有时间向您发送进度值。

为了验证这一点,请先尝试加载您的页面,然后使用 Firefox 或 Chrome 中的开发工具,转到“网络”选项卡,单击“在线”并选择“慢 3G”。另外,请尝试使用更大的文件,这将有助于您在上传栏中看到更长的进度。

对于开发工具,您可以执行 F12(对于 Windows),转到 network,您将找到 bandwidth,您将找到一些默认值(你可以添加自己的速度)

编辑 2020 年 11 月 12 日

我将分享一个工作代码(在生产和工作中),主要区别是我的上传服务中有 observe: 'events' 而你有 reportProgress: true em>,试着像我一样改变它

我的进度服务

upload(beanId: string, fileFormData: FormData, docType: DocTypeCode): Observable<number> {

    const progress$ = new BehaviorSubject(0);

    this.httpService.securePostWithProgress(this.localConfig.getUrls().DOCUMENT + `/upload/${docType}`, null, fileFormData, 'text')
      .pipe(
        tap((event: HttpEvent<any>) => {
          switch (event.type) {
            case HttpEventType.Sent:
              break;
            case HttpEventType.ResponseHeader:
              break;
            case HttpEventType.UploadProgress:
              const progress = Math.round(event.loaded / event.total * 100);
              progress$.next(progress);
              break;
            case HttpEventType.Response:
              setTimeout(() => {
                progress$.complete();
              }, 500);

          }
        }),
        catchError((e) => {
          progress$.error(e);
          throw e;
        })
      ).subscribe();
    return progress$.asObservable()
    }

上传服务


 securePostWithProgress(url: string, params: HttpParams, body?: FormData, responseType?: any): Observable<any> {
    this.token = this.tokenService.getToken();
    const headers = HttpHeaders.createInstance()
      .addAccessToken(this.token)
      .getHeaders();

    const options = { headers, params, responseType: responseType, observe: 'events', reportProgress: true };

    const req = new HttpRequest('POST', url, body, options);

    console.log('url for securePost request : ' + url);

    return this.http.request(req);
  }

还有表单数据

   const formData = new FormData();
        formData.append('file', imgBlob, name);
        this.uplodaDocument(formData, docTypeCode);

【讨论】:

  • 这不是本地开发问题;它在网站上直播
  • 本地与否是同样的问题,如果你有光纤和一个好的服务器和我的小文件(与你的带宽成比例),试试带宽限制并告诉我你是否有同样的问题。如果未验证您的服务器是否可以选择发送上传状态为活动状态(如果您使用良好的注释、conf 等)
  • 我做到了,这是我尝试的第一件事:(
  • 你的服务器是什么?
  • 代码中的HttpHeaders 来自哪里?请始终将您的导入添加到代码 sn-ps 中!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多