【发布时间】: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