add support for upload status report

This commit is contained in:
rubikscraft
2022-09-19 20:45:35 +02:00
parent 6e4ac465d4
commit d03d3f6ed4
8 changed files with 112 additions and 19 deletions

View File

@@ -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),

View File

@@ -1,6 +1,26 @@
<div class="content-border">
<div class="centered">
<h1>Processing</h1>
<mat-spinner color="accent"></mat-spinner>
<ng-container *ngIf="state === 'idle'">
<h1>Loading</h1>
<mat-spinner color="accent"></mat-spinner>
</ng-container>
<ng-container *ngIf="state === 'uploading'">
<h1>Uploading</h1>
<p>{{ (progress * 100).toFixed(0) }}% complete</p>
<mat-progress-bar
[mode]="progress === 0 ? 'indeterminate' : 'determinate'"
[value]="progress * 100"
color="accent"
></mat-progress-bar>
</ng-container>
<ng-container *ngIf="state === 'processing'">
<h1>Processing</h1>
<p>{{ (progress * 100).toFixed(0) }}% complete</p>
<mat-progress-bar
[mode]="progress === 0 ? 'indeterminate' : 'determinate'"
[value]="progress * 100"
color="accent"
></mat-progress-bar>
</ng-container>
</div>
</div>

View File

@@ -0,0 +1,3 @@
mat-progress-bar {
width: 80%;
}

View File

@@ -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');
}
}

View File

@@ -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 {}

View File

@@ -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,

View File

@@ -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<string[]> {
public UploadImages(images: File[]): {
progress: Observable<number>;
result: AsyncFailable<string>;
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<number>();
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<ImageMetaResponse> {

View File

@@ -1,4 +1,8 @@
<div class="dialog-text">
<h2>Downloading {{ data.name }}...</h2>
<mat-progress-bar mode="determinate" [value]="progress | async" color="accent"></mat-progress-bar>
<mat-progress-bar
[mode]="((progress | async) ?? 0) === 0 ? 'indeterminate' : 'determinate'"
[value]="((progress | async) ?? 0) * 100"
color="accent"
></mat-progress-bar>
</div>