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 }}...
-
+