From d03d3f6ed42928d83e68a456719de6e19ab89819 Mon Sep 17 00:00:00 2001 From: rubikscraft Date: Mon, 19 Sep 2022 20:45:35 +0200 Subject: [PATCH] add support for upload status report --- .../routes/image/image-manage.controller.ts | 1 + .../processing/processing.component.html | 24 ++++++++- .../processing/processing.component.scss | 3 ++ .../routes/processing/processing.component.ts | 36 ++++++++++--- .../routes/processing/processing.module.ts | 2 + frontend/src/app/services/api/api.service.ts | 6 ++- .../src/app/services/api/image.service.ts | 53 ++++++++++++++++--- .../download-dialog.component.html | 6 ++- 8 files changed, 112 insertions(+), 19 deletions(-) create mode 100644 frontend/src/app/routes/processing/processing.component.scss diff --git a/backend/src/routes/image/image-manage.controller.ts b/backend/src/routes/image/image-manage.controller.ts index c5a51e4..6376b2b 100644 --- a/backend/src/routes/image/image-manage.controller.ts +++ b/backend/src/routes/image/image-manage.controller.ts @@ -84,6 +84,7 @@ export class ImageManageController { for await (const file of multipart) { const buffer = await file.toBuffer(); const filename = file.filename; + console.log(filename); // const id = ThrowIfFailed( // await this.ingressDB.uploadFile(filename, buffer), diff --git a/frontend/src/app/routes/processing/processing.component.html b/frontend/src/app/routes/processing/processing.component.html index 2432ba6..c88be0c 100644 --- a/frontend/src/app/routes/processing/processing.component.html +++ b/frontend/src/app/routes/processing/processing.component.html @@ -1,6 +1,26 @@
-

Processing

- + +

Loading

+ +
+ +

Uploading

+

{{ (progress * 100).toFixed(0) }}% complete

+ +
+ +

Processing

+

{{ (progress * 100).toFixed(0) }}% complete

+ +
diff --git a/frontend/src/app/routes/processing/processing.component.scss b/frontend/src/app/routes/processing/processing.component.scss new file mode 100644 index 0000000..d96cebd --- /dev/null +++ b/frontend/src/app/routes/processing/processing.component.scss @@ -0,0 +1,3 @@ +mat-progress-bar { + width: 80%; +} diff --git a/frontend/src/app/routes/processing/processing.component.ts b/frontend/src/app/routes/processing/processing.component.ts index 114ec27..c878993 100644 --- a/frontend/src/app/routes/processing/processing.component.ts +++ b/frontend/src/app/routes/processing/processing.component.ts @@ -1,18 +1,28 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit } from '@angular/core'; import { Router } from '@angular/router'; -import { Fail, FT, HasFailed } from 'picsur-shared/dist/types'; +import { Fail, FT } from 'picsur-shared/dist/types'; import { ProcessingViewMeta } from 'src/app/models/dto/processing-view-meta.dto'; import { ApiService } from 'src/app/services/api/api.service'; import { ImageService } from 'src/app/services/api/image.service'; import { Logger } from 'src/app/services/logger/logger.service'; import { ErrorService } from 'src/app/util/error-manager/error.service'; +enum ProcessingState { + Idle = 'idle', + Uploading = 'uploading', + Processing = 'processing', +} + @Component({ templateUrl: './processing.component.html', + styleUrls: ['./processing.component.scss'], }) -export class ProcessingComponent implements OnInit { +export class ProcessingComponent implements OnInit, OnDestroy { private readonly logger = new Logger(ProcessingComponent.name); + public state = ProcessingState.Idle; + public progress = 0; + constructor( private readonly router: Router, private readonly imageService: ImageService, @@ -31,10 +41,24 @@ export class ProcessingComponent implements OnInit { history.replaceState(null, ''); - const id = await this.imageService.UploadImage(state.imageFiles[0]); + const request = this.imageService.UploadImages(state.imageFiles); + request.progress.subscribe((progress) => { + this.progress = progress; + }); + this.state = ProcessingState.Uploading; - if (HasFailed(id)) return this.errorService.quitFailure(id, this.logger); + await request.result; - this.router.navigate([`/view/`, id], { replaceUrl: true }); + this.logger.debug('Upload finished'); + + // if (HasFailed(id)) return this.errorService.quitFailure(id, this.logger); + + // this.router.navigate([`/view/`, id], { replaceUrl: true }); + } + + ngOnDestroy(): void { + if (this.state === ProcessingState.Idle) return; + + this.errorService.info('Upload continued in background'); } } diff --git a/frontend/src/app/routes/processing/processing.module.ts b/frontend/src/app/routes/processing/processing.module.ts index 3547b15..27c1d53 100644 --- a/frontend/src/app/routes/processing/processing.module.ts +++ b/frontend/src/app/routes/processing/processing.module.ts @@ -1,5 +1,6 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { ErrorManagerModule } from 'src/app/util/error-manager/error-manager.module'; import { ProcessingComponent } from './processing.component'; @@ -12,6 +13,7 @@ import { ProcessingRoutingModule } from './processing.routing.module'; ErrorManagerModule, ProcessingRoutingModule, MatProgressSpinnerModule, + MatProgressBarModule, ], }) export default class ProcessingRouteModule {} diff --git a/frontend/src/app/services/api/api.service.ts b/frontend/src/app/services/api/api.service.ts index 84cf5df..ef2cb7e 100644 --- a/frontend/src/app/services/api/api.service.ts +++ b/frontend/src/app/services/api/api.service.ts @@ -237,10 +237,12 @@ export class ApiService { const result = await axios.request({ url, onDownloadProgress: (e) => { - downloadProgress.next(e.loaded / e.total * 100); + if (e.total === 0) return downloadProgress.next(0); + downloadProgress.next(e.loaded / e.total); }, onUploadProgress: (e) => { - uploadProgress.next(e.loaded / e.total * 100); + if (e.total === 0) return uploadProgress.next(0); + uploadProgress.next(e.loaded / e.total); }, signal: abortController.signal, ...options, diff --git a/frontend/src/app/services/api/image.service.ts b/frontend/src/app/services/api/image.service.ts index 9018f23..52ba06c 100644 --- a/frontend/src/app/services/api/image.service.ts +++ b/frontend/src/app/services/api/image.service.ts @@ -23,6 +23,7 @@ import { HasSuccess, Open } from 'picsur-shared/dist/types/failable'; +import { Observable, Subject } from 'rxjs'; import { ImagesUploadRequest } from 'src/app/models/dto/images-upload-request.dto'; import { ImageUploadRequest } from '../../models/dto/image-upload-request.dto'; import { ApiService } from './api.service'; @@ -49,19 +50,55 @@ export class ImageService { return Open(result, 'id'); } - public async UploadImages(images: File[]): AsyncFailable { + public UploadImages(images: File[]): { + progress: Observable; + result: AsyncFailable; + cancel: () => void; + } { console.log('Uploading images', images); // Split into chunks of 20 - const groups = this.chunks(images, 20); + const totalBytes = images.reduce((acc, cur) => acc + cur.size, 0); + const groups = this.chunks(images, 20).map((chunk) => { + const groupSize = chunk.reduce((acc, cur) => acc + cur.size, 0); + return { + images: chunk, + groupSize, + }; + }); + const progress = new Subject(); + const aborter = new AbortController(); - const result = await this.api.postForm( - ImageUploadResponse, - '/api/image/upload/bulk', - new ImagesUploadRequest(images), - ); + const result = (async () => { + let processedBytes = 0; + for (const group of groups) { + const request = await this.api.postForm( + ImageUploadResponse, + '/api/image/upload/bulk', + new ImagesUploadRequest(group.images), + ); - return []; + request.uploadProgress.subscribe((p) => { + progress.next((processedBytes + p * group.groupSize) / totalBytes); + }); + + aborter.signal.addEventListener('abort', () => { + request.cancel(); + }); + + await request.result; + + progress.next((processedBytes += group.groupSize) / totalBytes); + } + + return ''; + })(); + + return { + progress: progress.asObservable(), + result: result, + cancel: () => aborter.abort(), + }; } public async GetImageMeta(image: string): AsyncFailable { diff --git a/frontend/src/app/util/dialog-manager/download-dialog/download-dialog.component.html b/frontend/src/app/util/dialog-manager/download-dialog/download-dialog.component.html index e6765f1..4f01de9 100644 --- a/frontend/src/app/util/dialog-manager/download-dialog/download-dialog.component.html +++ b/frontend/src/app/util/dialog-manager/download-dialog/download-dialog.component.html @@ -1,4 +1,8 @@

Downloading {{ data.name }}...

- +