Angular / RxJs: прогресс загрузки
У меня есть рабочее решение для получения прогресса загрузки в Angular 13/RxJs 7.
Сначала я определил некоторые перечисления:
export enum RequestType {
get = 'GET',
post = 'POST',
put = 'PUT',
delete = 'DELETE',
}
export enum ActionType {
download = 1,
upload
}
Затем я реализовал общий сервис для отслеживания прогресса:
@Injectable({
providedIn: 'root'
})
export class DownloadProgressService {
percentDone = 0;
actionType: ActionType;
constructor(private http: HttpClient) { }
downloadFile(uri: string, requestType: RequestType, actionType: ActionType, body: any): Observable<number> {
this.actionType = actionType;
const req = new HttpRequest(requestType, uri, body, {
reportProgress: true,
responseType: 'blob'
});
return this.http
.request(req)
.pipe(
map(event => this.getPercentage(event)),
);
}
public getPercentage(event: HttpEvent<any>): number {
switch (event.type) {
case HttpEventType.UploadProgress:
// Downloading files will still trigger upload progress due to the back-and-forth, ignore this
if (this.actionType !== ActionType.upload) {
return 0;
}
// Compute and show the % done:
if (event && event.total) {
this.percentDone = Math.round(100 * event.loaded / event.total);
return this.percentDone;
} else {
return 0;
}
case HttpEventType.DownloadProgress:
if (event && event.total) {
this.percentDone = Math.round(100 * event.loaded / event.total);
}
return this.percentDone;
default:
// Not an event we care about
return this.percentDone;
}
}
}
Затем я просто подписываюсь на это и получаю правильный ход загрузки:
this.downloadService
.downloadFile(url, RequestType.post, ActionType.download, body)
.subscribe(progress => this.progress = progress);
Все это отлично работает, я получаю индикатор выполнения с моей библиотекой компонентов, который показывает реальный прогресс.
Вопрос... как мне получить результирующий файл?
2 ответа
Вместо того, чтобы возвращать только ход выполнения, вы можете вернуть объект, содержащий процент выполнения и ответ HTTP.
return this.http.request(req).pipe(
map(event => ({
progress: this.getPercentage(event),
response: event
})),
);
Теперь в подписке можно использовать объект
this.downloadService
.downloadFile(url, RequestType.post, ActionType.download, body)
.subscribe({
next: ({progress, response}) => {
this.progress = progress;
// use response
},
error: (error: any) => {
// handle errors
}
});
Может кому поможет Решение
Я беру за основу этот пример и добавляю расчет процента за скачанный файл.
- Ваш сервер должен отправлять контент в виде Blob (двоичного файла).
- В заголовках запросов сервер должен отправить заголовок
Content-length: 789(other count file size in bytes)
. Я потерял много времени, чтобы понять, что на самом деле происходит на сервере и чего мне не хватает, чтобы узнать размер файла.
В этом примере визуально увидеть, что произошло, невозможно, поскольку файл очень маленький. Но откройте инструменты разработчика и установите регулирование, и вы увидите. Для больших файлов вы можете увидеть расчет загрузки в процентах, поскольку пакеты разделяются и отправляются частями.