Я пытаюсь создать форму для загрузки, основанную на этой статье:
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;
}
}
Единственное, что я изменил в службе - это URL (по понятным причинам). Затем я создал компонент для использования этого сервиса:
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 вместо того, чтобы быть инкрементным.
Может кто-нибудь определить, что я делаю неправильно?