implemented post und put image;
This commit is contained in:
parent
bc12a086f5
commit
a54fc5eb01
15
frontend/package-lock.json
generated
15
frontend/package-lock.json
generated
@ -3334,21 +3334,6 @@
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"angular-file-uploader": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/angular-file-uploader/-/angular-file-uploader-7.0.3.tgz",
|
||||
"integrity": "sha512-0wkr/3Vn3toG/2sTSiD5vJ5EK3HKYXwzyFd3M3hRegcHhQnagN34licn4r+CBWJQQED5aRWUVB9OWOCtuNwimw==",
|
||||
"requires": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz",
|
||||
"integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"ansi-colors": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz",
|
||||
|
||||
@ -26,7 +26,6 @@
|
||||
"@angular/platform-browser-dynamic": "~9.1.9",
|
||||
"@angular/router": "~9.1.9",
|
||||
"@types/uuid": "8.3.0",
|
||||
"angular-file-uploader": "^7.0.3",
|
||||
"bootstrap": "~4.3.1",
|
||||
"jquery": "3.5.1",
|
||||
"ngx-logger": "4.1.9",
|
||||
|
||||
@ -21,39 +21,40 @@ import {MatTabsModule} from '@angular/material/tabs';
|
||||
import {ImagesComponent} from './component/images/images.component';
|
||||
import {MapComponent} from './component/map/map.component';
|
||||
import {AgmCoreModule} from '@agm/core';
|
||||
import {AngularFileUploaderModule} from 'angular-file-uploader';
|
||||
import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
|
||||
import { FileUploaderComponent } from './component/file-uploader/file-uploader.component';
|
||||
import {MatSnackBarModule} from '@angular/material/snack-bar';
|
||||
|
||||
@NgModule({
|
||||
declarations: [LandingComponent, ImagesComponent, MapComponent],
|
||||
imports: [
|
||||
ReactiveFormsModule,
|
||||
BrowserModule,
|
||||
BrowserAnimationsModule,
|
||||
LoggerModule.forRoot({level: environment.log_level, serverLogLevel: NgxLoggerLevel.ERROR}),
|
||||
HttpClientModule,
|
||||
MatFormFieldModule,
|
||||
FormsModule,
|
||||
MatButtonModule,
|
||||
MatInputModule,
|
||||
MatSlideToggleModule,
|
||||
MatListModule,
|
||||
MatExpansionModule,
|
||||
MatCardModule,
|
||||
MatPaginatorModule,
|
||||
MatTableModule,
|
||||
MatTabsModule,
|
||||
// GoogleMapsModule,
|
||||
AgmCoreModule.forRoot({
|
||||
apiKey: 'AIzaSyBDgLJ1sL48IQg7e5jrwmUyXkqeEfVnf78'
|
||||
/* apiKey is required, unless you are a
|
||||
premium customer, in which case you can
|
||||
use clientId
|
||||
*/
|
||||
}),
|
||||
AngularFileUploaderModule,
|
||||
MatProgressSpinnerModule,
|
||||
],
|
||||
declarations: [LandingComponent, ImagesComponent, MapComponent, FileUploaderComponent],
|
||||
imports: [
|
||||
ReactiveFormsModule,
|
||||
BrowserModule,
|
||||
BrowserAnimationsModule,
|
||||
LoggerModule.forRoot({level: environment.log_level, serverLogLevel: NgxLoggerLevel.ERROR}),
|
||||
HttpClientModule,
|
||||
MatFormFieldModule,
|
||||
FormsModule,
|
||||
MatButtonModule,
|
||||
MatInputModule,
|
||||
MatSlideToggleModule,
|
||||
MatListModule,
|
||||
MatExpansionModule,
|
||||
MatCardModule,
|
||||
MatPaginatorModule,
|
||||
MatTableModule,
|
||||
MatTabsModule,
|
||||
MatSnackBarModule,
|
||||
// GoogleMapsModule,
|
||||
AgmCoreModule.forRoot({
|
||||
apiKey: 'AIzaSyBDgLJ1sL48IQg7e5jrwmUyXkqeEfVnf78'
|
||||
/* apiKey is required, unless you are a
|
||||
premium customer, in which case you can
|
||||
use clientId
|
||||
*/
|
||||
}),
|
||||
MatProgressSpinnerModule,
|
||||
],
|
||||
// enables injecting
|
||||
providers: [
|
||||
RestService,
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
.margin-right {
|
||||
margin-right: 1em;
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
<!--suppress ALL -->
|
||||
<button type="button"
|
||||
mat-raised-button
|
||||
(click)="fileInput.click()"
|
||||
class="margin-right"
|
||||
>Choose File</button>
|
||||
<input hidden
|
||||
(change)="onFileSelected($event)"
|
||||
#fileInput
|
||||
type="file"
|
||||
>
|
||||
|
||||
<mat-form-field class="margin-right">
|
||||
<mat-label>Tag</mat-label>
|
||||
<input matInput
|
||||
placeholder="Add a tag"
|
||||
[(ngModel)]="meta ? meta.tag : tag">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="margin-right">
|
||||
<mat-label>Name</mat-label>
|
||||
<input matInput
|
||||
placeholder="Name"
|
||||
[(ngModel)]="meta ? meta.name : name">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field *ngIf="mode==='post'"
|
||||
class="margin-right"
|
||||
>
|
||||
<mat-label>Longitude</mat-label>
|
||||
<input matInput
|
||||
placeholder="Add longitude"
|
||||
[(ngModel)]="meta ? meta.longitude : longitude">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field *ngIf="mode==='post'"
|
||||
class="margin-right"
|
||||
>
|
||||
<mat-label>Longitude</mat-label>
|
||||
<input matInput
|
||||
placeholder="Add latitude"
|
||||
[(ngModel)]="meta ? meta.latitude : latitude">
|
||||
</mat-form-field>
|
||||
|
||||
<button type="button"
|
||||
mat-raised-button
|
||||
class="margin-right"
|
||||
(click)="postFile()"
|
||||
[disabled]="!(file || mode !== 'post')"
|
||||
>
|
||||
<span *ngIf="mode==='post'; else updateImage">Upload Image</span>
|
||||
<ng-template #updateImage>Update Image</ng-template>
|
||||
</button>
|
||||
<br>
|
||||
<ng-container *ngIf="file">
|
||||
<span *ngIf="currentFileName">{{currentFileName}}</span>
|
||||
</ng-container>
|
||||
@ -0,0 +1,143 @@
|
||||
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
|
||||
import {RestService} from '../../services/rest.service';
|
||||
import {NGXLogger} from 'ngx-logger';
|
||||
import {MatSnackBar} from '@angular/material/snack-bar';
|
||||
import {ImageMetadata} from '../../interfaces/interface';
|
||||
|
||||
@Component({
|
||||
selector: 'app-file-uploader',
|
||||
templateUrl: './file-uploader.component.html',
|
||||
styleUrls: ['./file-uploader.component.css']
|
||||
})
|
||||
export class FileUploaderComponent implements OnInit {
|
||||
|
||||
constructor(private restService: RestService, private logger: NGXLogger, private snackBar: MatSnackBar) {
|
||||
}
|
||||
|
||||
@Input()
|
||||
mode: string; // post, put
|
||||
@Input()
|
||||
filename: string; // if mode==put && without file ending
|
||||
@Input()
|
||||
meta: ImageMetadata;
|
||||
|
||||
@Output()
|
||||
reload: EventEmitter<string> = new EventEmitter<string>();
|
||||
|
||||
currentFileName: string;
|
||||
|
||||
id: string;
|
||||
|
||||
file: ArrayBuffer;
|
||||
tag: string;
|
||||
longitude: string;
|
||||
latitude: string;
|
||||
name: string;
|
||||
|
||||
private buildForm(): FormData {
|
||||
const mainForm: FormData = new FormData();
|
||||
const metaDict: {} = {};
|
||||
|
||||
if (this.filename) {
|
||||
this.id = this.filename;
|
||||
} else {
|
||||
this.id = this.currentFileName;
|
||||
}
|
||||
|
||||
metaDict['filename'] = this.currentFileName;
|
||||
|
||||
if (this.mode === 'post') {
|
||||
if (this.longitude && this.latitude) {
|
||||
metaDict['longitude'] = Number.parseFloat(this.longitude);
|
||||
metaDict['latitude'] = Number.parseFloat(this.latitude);
|
||||
} else if (this.meta.longitude && this.meta.latitude) {
|
||||
metaDict['longitude'] = this.meta.longitude;
|
||||
metaDict['latitude'] = this.meta.latitude;
|
||||
} else {
|
||||
throw new Error('Long/Lat necessary if new file!');
|
||||
}
|
||||
}
|
||||
|
||||
if (this.name) {
|
||||
metaDict['name'] = this.name;
|
||||
} else if (this.meta.name) {
|
||||
metaDict['name'] = this.meta.name;
|
||||
} else {
|
||||
if (this.mode === 'post') {
|
||||
throw new Error('Name necessary if new file');
|
||||
}
|
||||
}
|
||||
|
||||
if (this.tag) {
|
||||
metaDict['tag'] = this.tag;
|
||||
} else if (this.meta.tag) {
|
||||
metaDict['tag'] = this.meta.tag;
|
||||
}
|
||||
|
||||
metaDict['datetime'] = new Date().toISOString();
|
||||
|
||||
mainForm.append('id', this.id.split('.')[0]);
|
||||
mainForm.append('metadata', JSON.stringify(metaDict));
|
||||
|
||||
if (this.file) {
|
||||
let binary = '';
|
||||
const bytes = new Uint8Array( this.file );
|
||||
const len = bytes.byteLength;
|
||||
for (let i = 0; i < len; i++) {
|
||||
binary += String.fromCharCode( bytes[ i ] );
|
||||
}
|
||||
mainForm.append('image_data', window.btoa( binary ));
|
||||
} else {
|
||||
throw new Error('No file to upload!');
|
||||
}
|
||||
|
||||
return mainForm;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
onFileSelected(fileEvent: Event) {
|
||||
this.logger.debug('fileEvent', fileEvent);
|
||||
const target: HTMLInputElement = fileEvent.target as HTMLInputElement;
|
||||
|
||||
if (typeof (FileReader) !== 'undefined') {
|
||||
const reader = new FileReader();
|
||||
const file: File = target.files[0];
|
||||
|
||||
reader.onload = (e: any) => {
|
||||
this.file = e.target.result;
|
||||
this.currentFileName = file.name;
|
||||
this.logger.debug('Selected file', this.currentFileName, this.file);
|
||||
};
|
||||
|
||||
reader.readAsArrayBuffer(file);
|
||||
}
|
||||
}
|
||||
|
||||
async postFile() {
|
||||
try {
|
||||
const formData = this.buildForm();
|
||||
this.logger.debug('Build form data', formData);
|
||||
|
||||
let response;
|
||||
if (this.mode === 'post') {
|
||||
response = await this.restService.postImage(formData).toPromise();
|
||||
} else if (this.mode === 'put') {
|
||||
response = await this.restService.updateImage(this.id, formData).toPromise();
|
||||
} else {
|
||||
// noinspection ExceptionCaughtLocallyJS
|
||||
throw new Error('Unknown mode: ' + this.mode);
|
||||
}
|
||||
|
||||
this.logger.debug('Response', response);
|
||||
|
||||
this.snackBar.open('Upload successful', 'Dismiss');
|
||||
this.reload.emit(this.currentFileName);
|
||||
} catch (err) {
|
||||
this.logger.error('error', err);
|
||||
this.snackBar.open(err.error.Error, 'Dismiss');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
<mat-expansion-panel
|
||||
*ngFor="let image of images | slice:paginationGetStart():paginationGetEnd(); let i = index"
|
||||
(opened)="loadImage(i, image.filename.split('.')[0])"
|
||||
(opened)="loadImage(i + paginationGetStart(), image.filename.split('.')[0])"
|
||||
>
|
||||
<mat-expansion-panel-header (click)="$event.stopPropagation()">
|
||||
<span style="width: 30em"
|
||||
@ -24,19 +24,23 @@
|
||||
<div style="width: 20em; height: 20em; border: solid; margin-bottom: 1em; display: flex">
|
||||
<img *ngIf="image.image_b64, let b64; else loadingImage"
|
||||
[src]="b64"
|
||||
style="width: 100%; margin: auto" [alt]="image.filename"/>
|
||||
style="width: 100%; margin: auto" [alt]="image.filename"/>
|
||||
<ng-template #loadingImage>
|
||||
<mat-spinner style="margin: auto;"></mat-spinner>
|
||||
</ng-template>
|
||||
|
||||
</div>
|
||||
|
||||
<angular-file-uploader style="margin-left: 0 !important;"
|
||||
[config]="getImageUpdateConfig(image.filename)">
|
||||
</angular-file-uploader>
|
||||
<app-file-uploader [mode]="'put'"
|
||||
[filename]="image.filename.split('.')[0]"
|
||||
[meta]=image
|
||||
(reload)="loadImage(i + paginationGetStart(), $event.split('.')[0])"
|
||||
></app-file-uploader>
|
||||
|
||||
<span style="float: right">
|
||||
<button mat-raised-button color="warn" (click)="deleteImage(image)">Delete</button>
|
||||
<button mat-raised-button color="warn" (click)="deleteImage(image)">
|
||||
Delete
|
||||
</button>
|
||||
</span>
|
||||
</ng-template>
|
||||
</mat-expansion-panel>
|
||||
@ -48,7 +52,7 @@
|
||||
showFirstLastButtons
|
||||
></mat-paginator>
|
||||
|
||||
<mat-divider style="margin-top: 2em"></mat-divider>
|
||||
<h3>Upload New Image</h3>
|
||||
<angular-file-uploader style="margin-left: 0 !important;"
|
||||
[config]="imageUploadConfig" id="generalUpload">
|
||||
</angular-file-uploader>
|
||||
<app-file-uploader [mode]="'post'" (reload)="reload.emit()"></app-file-uploader>
|
||||
<mat-divider style="margin-top: 2em"></mat-divider>
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import {AfterViewInit, Component, Input, OnInit} from '@angular/core';
|
||||
import {AfterViewInit, Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
|
||||
import {PageEvent} from '@angular/material/paginator';
|
||||
import {ImageMetadata} from '../../interfaces/interface';
|
||||
import {RestService} from '../../services/rest.service';
|
||||
import {NGXLogger} from 'ngx-logger';
|
||||
import {AngularFileUploaderConfig} from 'angular-file-uploader';
|
||||
import {MatSnackBar} from '@angular/material/snack-bar';
|
||||
|
||||
@Component({
|
||||
selector: 'app-images',
|
||||
@ -14,19 +14,16 @@ export class ImagesComponent implements OnInit, AfterViewInit {
|
||||
|
||||
@Input()
|
||||
images: ImageMetadata[];
|
||||
@Output()
|
||||
reload: EventEmitter<void> = new EventEmitter<void>();
|
||||
|
||||
pageSizeOptions: number[] = [5, 10, 25, 100];
|
||||
lastPageEvent: PageEvent;
|
||||
|
||||
imageUploadConfig: AngularFileUploaderConfig = {
|
||||
uploadAPI: {
|
||||
url: this.restService.getPostImageUrl()
|
||||
}
|
||||
};
|
||||
|
||||
constructor(
|
||||
public restService: RestService,
|
||||
private logger: NGXLogger,
|
||||
private snackbar: MatSnackBar
|
||||
) {
|
||||
}
|
||||
|
||||
@ -46,24 +43,18 @@ export class ImagesComponent implements OnInit, AfterViewInit {
|
||||
return this.paginationGetStart() + this.lastPageEvent.pageSize;
|
||||
}
|
||||
|
||||
getImageUpdateConfig(filename: string): AngularFileUploaderConfig {
|
||||
return {
|
||||
uploadAPI: {
|
||||
url: this.restService.getUpdateImageUrl(filename),
|
||||
method: 'PUT'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
deleteImage(image: ImageMetadata) {
|
||||
this.restService.deleteImage(image.filename).toPromise().then(() => {
|
||||
this.restService.deleteImage(image.filename.split('.')[0]).toPromise().then(() => {
|
||||
this.images = this.images.filter(im => im !== image);
|
||||
this.snackbar.open('Successfully deleted ' + image.filename, 'Dismiss');
|
||||
this.reload.emit();
|
||||
}).catch(err => this.logger.error(err));
|
||||
}
|
||||
|
||||
loadImage(i: number, id: string): void {
|
||||
this.restService.getImage(id).toPromise().then(image => {
|
||||
this.images[i].image_b64 = 'data:image/jpg;base64,' + image.image_data;
|
||||
this.logger.debug('image_b64', i, id, this.images[i].image_b64);
|
||||
}).catch(err => {
|
||||
this.logger.error('loadImage', i, id, err);
|
||||
});
|
||||
|
||||
@ -6,11 +6,12 @@
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
<mat-tab-group *ngIf="images.length > 0">
|
||||
<mat-tab-group *ngIf="images.length > 0; else noImages">
|
||||
<mat-tab label="Images">
|
||||
<app-images [images]="images"></app-images>
|
||||
<app-images [images]="images" (reload)="loadImages()"></app-images>
|
||||
</mat-tab>
|
||||
<mat-tab label="Map">
|
||||
<app-map [images]="images"></app-map>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
<ng-template #noImages>No images found</ng-template>
|
||||
|
||||
@ -1 +1,3 @@
|
||||
agm-map { height: 50vh; /* height is required */ }
|
||||
agm-map {
|
||||
height: 50vh; /* height is required */
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<h2>Map</h2>
|
||||
|
||||
<agm-map *ngIf="images"
|
||||
[latitude]="latitude"
|
||||
[longitude]="longitude"
|
||||
[zoom]="2">
|
||||
[latitude]="latitude"
|
||||
[longitude]="longitude"
|
||||
[zoom]="2">
|
||||
<agm-marker
|
||||
*ngFor="let image of images"
|
||||
[latitude]="image.latitude"
|
||||
|
||||
@ -19,10 +19,16 @@ export interface ImageMetadata {
|
||||
sha512: string;
|
||||
selected?: boolean;
|
||||
image_b64?: string;
|
||||
tag?: string;
|
||||
}
|
||||
|
||||
export interface ImageResponse {
|
||||
export interface GetImageResponse {
|
||||
id: string;
|
||||
image_data: string;
|
||||
metadata: ImageMetadata;
|
||||
}
|
||||
|
||||
export interface ImagePostResponse {
|
||||
id: string;
|
||||
filename: string;
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ import {NGXLogger} from 'ngx-logger';
|
||||
import {environment} from '../../environments/environment';
|
||||
import {Observable} from 'rxjs';
|
||||
import {tap} from 'rxjs/operators';
|
||||
import {ImageResponse, StorageHealth} from '../interfaces/interface';
|
||||
import {GetImageResponse, ImagePostResponse, StorageHealth} from '../interfaces/interface';
|
||||
|
||||
@Injectable()
|
||||
export class RestService {
|
||||
@ -14,6 +14,7 @@ export class RestService {
|
||||
private http: HttpClient
|
||||
) {
|
||||
}
|
||||
|
||||
private currentLocation = 'http://' + environment.location + ':' + environment.port;
|
||||
private imageUrl = this.currentLocation + '/image';
|
||||
private getImageUrl = this.imageUrl + '/get';
|
||||
@ -36,8 +37,8 @@ export class RestService {
|
||||
);
|
||||
}
|
||||
|
||||
public getImage(id: string): Observable<ImageResponse> {
|
||||
return this.http.get<ImageResponse>(this.getImageUrl + '/' + id).pipe(
|
||||
public getImage(id: string): Observable<GetImageResponse> {
|
||||
return this.http.get<GetImageResponse>(this.getImageUrl + '/' + id).pipe(
|
||||
tap(next => this.logger.debug('getImage', id, next))
|
||||
);
|
||||
}
|
||||
@ -61,26 +62,26 @@ export class RestService {
|
||||
);
|
||||
}
|
||||
|
||||
// public postImage(id: string, image: any): Observable<any> {
|
||||
// return this.http.post<any>(this.postImageUrl + '/' + id, image).pipe(
|
||||
// tap(next => this.logger.debug('postImage', id, image, next))
|
||||
// );
|
||||
// }
|
||||
|
||||
public getPostImageUrl(): string {
|
||||
return this.postImageUrl;
|
||||
public postImage(imageForm: FormData): Observable<ImagePostResponse> {
|
||||
return this.http.post<ImagePostResponse>(this.postImageUrl, imageForm).pipe(
|
||||
tap(next => this.logger.debug('postImage', imageForm, next))
|
||||
);
|
||||
}
|
||||
|
||||
// public updateImage(id: string, image: any): Observable<any> {
|
||||
// return this.http.put<any>(this.updateImageUrl + '/' + id, image).pipe(
|
||||
// tap(next => this.logger.debug('updateImage', id, image, next))
|
||||
// );
|
||||
// public getPostImageUrl(): string {
|
||||
// return this.postImageUrl;
|
||||
// }
|
||||
|
||||
public getUpdateImageUrl(filenameWithExtension: string): string {
|
||||
return this.updateImageUrl + '/' + RestService.stripUploadFileName(filenameWithExtension);
|
||||
public updateImage(id: string, imageForm: FormData): Observable<any> {
|
||||
return this.http.put<FormData>(this.updateImageUrl + '/' + id, imageForm).pipe(
|
||||
tap(next => this.logger.debug('updateImage', id, imageForm, next))
|
||||
);
|
||||
}
|
||||
|
||||
// public getUpdateImageUrl(filenameWithExtension: string): string {
|
||||
// return this.updateImageUrl + '/' + RestService.stripUploadFileName(filenameWithExtension);
|
||||
// }
|
||||
|
||||
public healthCheck(): Observable<StorageHealth> {
|
||||
return this.http.get<StorageHealth>(this.healthCheckUrl).pipe(
|
||||
tap(next => this.logger.debug('healthCheck', next))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user