add file storage to ui
This commit is contained in:
parent
906cee7621
commit
cf5333c788
|
@ -497,6 +497,9 @@ public class DescriptionServiceImpl implements DescriptionService {
|
|||
StorageFile storageFile = this.storageFileService.copyToStorage(fileId, StorageType.Main, true, new BaseFieldSet().ensure(StorageFile._id));
|
||||
this.storageFileService.updatePurgeAt(storageFile.getId(), null);
|
||||
}
|
||||
if (this.conventionService.isValidUUID(data.getTextValue()) && !UUID.fromString(data.getTextValue()).equals(fileId)) {
|
||||
this.storageFileService.updatePurgeAt(UUID.fromString(data.getTextValue()), Instant.now().minusSeconds(60));
|
||||
}
|
||||
data.setTextValue(fileId == null ? null : fileId.toString());
|
||||
} else {
|
||||
data.setTextValue(persist.getTextValue());
|
||||
|
|
|
@ -86,7 +86,7 @@ public class StorageFileServiceImpl implements StorageFileService {
|
|||
StorageFileEntity storageFile = this.buildDataEntry(model);
|
||||
|
||||
File file = new File(this.filePath(storageFile.getFileRef(), storageFile.getStorageType()));
|
||||
if (!file.exists()) throw new FileAlreadyExistsException(storageFile.getName());
|
||||
if (file.exists()) throw new FileAlreadyExistsException(storageFile.getName());
|
||||
|
||||
try (FileOutputStream fos = new FileOutputStream(file)) {
|
||||
fos.write(payload);
|
||||
|
|
|
@ -71,6 +71,23 @@ public class StorageFileController {
|
|||
this.validatorFactory = validatorFactory;
|
||||
}
|
||||
|
||||
@GetMapping("/name/{id}")
|
||||
public String getName(@PathVariable("id") UUID id) throws MyApplicationException, MyForbiddenException, MyNotFoundException {
|
||||
logger.debug(new MapLogEntry("get name" ).And("id", id));
|
||||
|
||||
this.authorizationService.authorizeForce(Permission.BrowseStorageFile);
|
||||
|
||||
StorageFileEntity storageFile = this.queryFactory.query(StorageFileQuery.class).ids(id).firstAs(new BaseFieldSet().ensure(StorageFile._fullName));
|
||||
if (storageFile == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{id, StorageFile.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
|
||||
this.auditService.track(AuditableAction.StorageFile_Download, Map.ofEntries(
|
||||
new AbstractMap.SimpleEntry<String, Object>("id", id)
|
||||
));
|
||||
|
||||
|
||||
return storageFile.getName();
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("upload-temp-files")
|
||||
@Transactional
|
||||
|
@ -82,9 +99,9 @@ public class StorageFileController {
|
|||
List<StorageFile> addedFiles = new ArrayList<>();
|
||||
for (MultipartFile file : files) {
|
||||
StorageFilePersist storageFilePersist = new StorageFilePersist();
|
||||
storageFilePersist.setName(FilenameUtils.removeExtension(file.getName()));
|
||||
storageFilePersist.setExtension(FilenameUtils.getExtension(file.getName()));
|
||||
storageFilePersist.setMimeType(URLConnection.guessContentTypeFromName(file.getName()));
|
||||
storageFilePersist.setName(FilenameUtils.removeExtension(file.getOriginalFilename()));
|
||||
storageFilePersist.setExtension(FilenameUtils.getExtension(file.getOriginalFilename()));
|
||||
storageFilePersist.setMimeType(URLConnection.guessContentTypeFromName(file.getOriginalFilename()));
|
||||
storageFilePersist.setOwnerId(this.userScope.getUserIdSafe());
|
||||
storageFilePersist.setStorageType(StorageType.Temp);
|
||||
storageFilePersist.setLifetime(Duration.ofSeconds(this.config.getTempStoreLifetimeSeconds()));
|
||||
|
@ -104,7 +121,7 @@ public class StorageFileController {
|
|||
|
||||
this.authorizationService.authorizeForce(Permission.BrowseStorageFile);
|
||||
|
||||
StorageFileEntity storageFile = this.queryFactory.query(StorageFileQuery.class).ids(id).firstAs(new BaseFieldSet().ensure(StorageFile._createdAt, StorageFile._fullName, StorageFile._mimeType));
|
||||
StorageFileEntity storageFile = this.queryFactory.query(StorageFileQuery.class).ids(id).firstAs(new BaseFieldSet().ensure(StorageFile._createdAt, StorageFile._fullName, StorageFile._mimeType, StorageFile._extension));
|
||||
if (storageFile == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{id, StorageFile.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
|
||||
byte[] file = this.storageFileService.readAsBytesSafe(id);
|
||||
|
|
|
@ -48,6 +48,7 @@ import { NotificationService } from './services/notification/notification-servic
|
|||
import { SemanticsService } from './services/semantic/semantics.service';
|
||||
import { PrefillingSourceService } from './services/prefilling-source/prefilling-source.service';
|
||||
import { VisibilityRulesService } from '@app/ui/description/editor/description-form/visibility-rules/visibility-rules.service';
|
||||
import { StorageFileService } from './services/storage-file/storage-file.service';
|
||||
//
|
||||
//
|
||||
// This is shared module that provides all the services. Its imported only once on the AppModule.
|
||||
|
@ -114,7 +115,8 @@ export class CoreServiceModule {
|
|||
NotificationService,
|
||||
SemanticsService,
|
||||
PrefillingSourceService,
|
||||
VisibilityRulesService
|
||||
VisibilityRulesService,
|
||||
StorageFileService
|
||||
],
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
import { BaseEntity } from "@common/base/base-entity.model";
|
||||
import { User } from "../user/user";
|
||||
|
||||
export interface StorageFile extends BaseEntity {
|
||||
fileRef: string;
|
||||
name: string;
|
||||
fullName: string;
|
||||
extension: string;
|
||||
mimeType: string;
|
||||
purgeAt: Date;
|
||||
purgedAt: Date;
|
||||
owner: User
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
import { HttpClient, HttpHeaders, HttpResponse } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { DescriptionTemplate } from '@app/core/model/description-template/description-template';
|
||||
import { BaseHttpParams } from '@common/http/base-http-params';
|
||||
import { InterceptorType } from '@common/http/interceptors/interceptor-type';
|
||||
import { FilterService } from '@common/modules/text-filter/filter-service';
|
||||
import { Guid } from '@common/types/guid';
|
||||
import { Observable, throwError } from 'rxjs';
|
||||
import { catchError, map } from 'rxjs/operators';
|
||||
import { ConfigurationService } from '../configuration/configuration.service';
|
||||
import { BaseHttpV2Service } from '../http/base-http-v2.service';
|
||||
import { StorageFile } from '@app/core/model/storage-file/storage-file';
|
||||
|
||||
@Injectable()
|
||||
export class StorageFileService {
|
||||
|
||||
private headers = new HttpHeaders();
|
||||
|
||||
constructor(private http: BaseHttpV2Service, private httpClient: HttpClient, private configurationService: ConfigurationService, private filterService: FilterService) {
|
||||
}
|
||||
|
||||
private get apiBase(): string { return `${this.configurationService.server}storage-file`; }
|
||||
|
||||
getName(id: Guid ): Observable<string> {
|
||||
const url = `${this.apiBase}/name/${id}`;
|
||||
|
||||
return this.http
|
||||
.get<string>(url).pipe(
|
||||
catchError((error: any) => throwError(error)));
|
||||
}
|
||||
|
||||
download(id: Guid ): Observable<HttpResponse<Blob>> {
|
||||
const url = `${this.apiBase}/${id}`;
|
||||
|
||||
return this.http
|
||||
.get<HttpResponse<Blob>>(url).pipe(
|
||||
catchError((error: any) => throwError(error)));
|
||||
}
|
||||
|
||||
uploadTempFiles(item: File): Observable<StorageFile[]> {
|
||||
const url = `${this.apiBase}/upload-temp-files`;
|
||||
|
||||
const formData: FormData = new FormData();
|
||||
formData.append('files', item);
|
||||
|
||||
const params = new BaseHttpParams();
|
||||
params.interceptorContext = {
|
||||
excludedInterceptors: [InterceptorType.JSONContentType]
|
||||
};
|
||||
|
||||
return this.http
|
||||
.post<StorageFile[]>(url, formData, { params: params }).pipe(
|
||||
catchError((error: any) => throwError(error)));
|
||||
}
|
||||
|
||||
}
|
|
@ -132,6 +132,7 @@ export class DescriptionEditorResolver extends BaseEditorResolver {
|
|||
(prefix ? prefix + '.' : '') + [nameof<DescriptionTemplate>(x => x.definition), nameof<DescriptionTemplateDefinition>(x => x.pages), nameof<DescriptionTemplatePage>(x => x.sections), nameof<DescriptionTemplateSection>(x => x.fieldSets), nameof<DescriptionTemplateFieldSet>(x => x.fields), nameof<DescriptionTemplateField>(x => x.data), nameof<DescriptionTemplateExternalDatasetData>(x => x.type)].join('.'),
|
||||
(prefix ? prefix + '.' : '') + [nameof<DescriptionTemplate>(x => x.definition), nameof<DescriptionTemplateDefinition>(x => x.pages), nameof<DescriptionTemplatePage>(x => x.sections), nameof<DescriptionTemplateSection>(x => x.fieldSets), nameof<DescriptionTemplateFieldSet>(x => x.fields), nameof<DescriptionTemplateField>(x => x.data), nameof<DescriptionTemplateUploadData>(x => x.maxFileSizeInMB)].join('.'),
|
||||
(prefix ? prefix + '.' : '') + [nameof<DescriptionTemplate>(x => x.definition), nameof<DescriptionTemplateDefinition>(x => x.pages), nameof<DescriptionTemplatePage>(x => x.sections), nameof<DescriptionTemplateSection>(x => x.fieldSets), nameof<DescriptionTemplateFieldSet>(x => x.fields), nameof<DescriptionTemplateField>(x => x.data), nameof<DescriptionTemplateUploadData>(x => x.types), nameof<DescriptionTemplateUploadOption>(x => x.label)].join('.'),
|
||||
(prefix ? prefix + '.' : '') + [nameof<DescriptionTemplate>(x => x.definition), nameof<DescriptionTemplateDefinition>(x => x.pages), nameof<DescriptionTemplatePage>(x => x.sections), nameof<DescriptionTemplateSection>(x => x.fieldSets), nameof<DescriptionTemplateFieldSet>(x => x.fields), nameof<DescriptionTemplateField>(x => x.data), nameof<DescriptionTemplateUploadData>(x => x.types), nameof<DescriptionTemplateUploadOption>(x => x.value)].join('.'),
|
||||
(prefix ? prefix + '.' : '') + [nameof<DescriptionTemplate>(x => x.definition), nameof<DescriptionTemplateDefinition>(x => x.pages), nameof<DescriptionTemplatePage>(x => x.sections), nameof<DescriptionTemplateSection>(x => x.fieldSets), nameof<DescriptionTemplateFieldSet>(x => x.fields), nameof<DescriptionTemplateField>(x => x.data), nameof<DescriptionTemplateReferenceTypeData>(x => x.referenceType), nameof<ReferenceType>(x => x.id)].join('.'),
|
||||
(prefix ? prefix + '.' : '') + [nameof<DescriptionTemplate>(x => x.definition), nameof<DescriptionTemplateDefinition>(x => x.pages), nameof<DescriptionTemplatePage>(x => x.sections), nameof<DescriptionTemplateSection>(x => x.fieldSets), nameof<DescriptionTemplateFieldSet>(x => x.fields), nameof<DescriptionTemplateField>(x => x.data), nameof<DescriptionTemplateReferenceTypeData>(x => x.referenceType), nameof<ReferenceType>(x => x.name)].join('.'),
|
||||
]
|
||||
|
|
|
@ -108,8 +108,8 @@
|
|||
<ng-container *ngSwitchCase="descriptionTemplateFieldTypeEnum.UPLOAD">
|
||||
<div class="col-12 d-flex justify-content-center">
|
||||
<ngx-dropzone #drop class="drop-file col-12" (change)="fileChangeEvent($event, true)" [multiple]="false" [accept]="typesToString()" [disabled]="propertiesFormGroup?.get(field.id).get('textValue').disabled">
|
||||
<ngx-dropzone-preview *ngIf="propertiesFormGroup?.get(field.id).get('textValue').value && propertiesFormGroup?.get(field.id).get('textValue').value.name" class="file-preview" [removable]="true" (removed)="onRemove()">
|
||||
<ngx-dropzone-label class="file-label">{{ propertiesFormGroup?.get(field.id).get('textValue').value.name }}</ngx-dropzone-label>
|
||||
<ngx-dropzone-preview *ngIf="propertiesFormGroup?.get(field.id).get('textValue').value && fileNameDisplay" class="file-preview" [removable]="true" (removed)="onRemove()">
|
||||
<ngx-dropzone-label class="file-label">{{ fileNameDisplay}}</ngx-dropzone-label>
|
||||
</ngx-dropzone-preview>
|
||||
</ngx-dropzone>
|
||||
</div>
|
||||
|
|
|
@ -9,12 +9,16 @@ import { DescriptionTemplateField, DescriptionTemplateFieldSet, DescriptionTempl
|
|||
import { DmpService } from '@app/core/services/dmp/dmp.service';
|
||||
import { SnackBarNotificationLevel, UiNotificationService } from "@app/core/services/notification/ui-notification-service";
|
||||
import { ReferenceService } from '@app/core/services/reference/reference.service';
|
||||
import { StorageFileService } from '@app/core/services/storage-file/storage-file.service';
|
||||
import { FileUtils } from '@app/core/services/utilities/file-utils.service';
|
||||
import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration';
|
||||
import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration';
|
||||
import { VisibilityRulesService } from '@app/ui/description/editor/description-form/visibility-rules/visibility-rules.service';
|
||||
import { BaseComponent } from '@common/base/base.component';
|
||||
import { FormValidationErrorsDialogComponent } from '@common/forms/form-validation-errors-dialog/form-validation-errors-dialog.component';
|
||||
import { Guid } from '@common/types/guid';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import * as FileSaver from 'file-saver';
|
||||
import { Observable } from 'rxjs';
|
||||
import { distinctUntilChanged, map, takeUntil } from 'rxjs/operators';
|
||||
|
||||
|
@ -66,6 +70,7 @@ export class DescriptionFormFieldComponent extends BaseComponent implements OnIn
|
|||
];
|
||||
|
||||
filesToUpload: FileList;
|
||||
fileNameDisplay: string;
|
||||
|
||||
constructor(
|
||||
private language: TranslateService,
|
||||
|
@ -74,7 +79,8 @@ export class DescriptionFormFieldComponent extends BaseComponent implements OnIn
|
|||
private uiNotificationService: UiNotificationService,
|
||||
public dialog: MatDialog,
|
||||
private fileUtils: FileUtils,
|
||||
private referenceService: ReferenceService
|
||||
private referenceService: ReferenceService,
|
||||
private storageFileService: StorageFileService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
@ -86,6 +92,8 @@ export class DescriptionFormFieldComponent extends BaseComponent implements OnIn
|
|||
|
||||
ngOnInit() {
|
||||
|
||||
if(this.field?.data?.fieldType == DescriptionTemplateFieldType.UPLOAD && this.field && this.field.id && (this.propertiesFormGroup?.get(this.field.id).get('textValue').value != undefined)) this.getUploadFileName();
|
||||
|
||||
this.isRequired = this.field.validations?.includes(DescriptionTemplateFieldValidationType.Required);
|
||||
|
||||
switch (this.field?.data?.fieldType) {
|
||||
|
@ -267,15 +275,28 @@ export class DescriptionFormFieldComponent extends BaseComponent implements OnIn
|
|||
|
||||
}
|
||||
|
||||
private getUploadFileName(){
|
||||
const id = Guid.parse((this.propertiesFormGroup?.get(this.field.id).get('textValue').value as string));
|
||||
|
||||
this.storageFileService.getName(id)
|
||||
.pipe(takeUntil(this._destroyed)).subscribe((name) => {
|
||||
this.fileNameDisplay = name;
|
||||
}, error => {
|
||||
this.fileNameDisplay = null;
|
||||
})
|
||||
}
|
||||
|
||||
public upload() {
|
||||
//TODO: refactor this
|
||||
// this.fileService.upload(this.filesToUpload[0], this.datasetProfileId.id, this.form.value.id).subscribe((fileId: string) => {
|
||||
// this.form.get("value").patchValue(
|
||||
// { "name": this.filesToUpload[0].name, "id": fileId + "", "type": this.filesToUpload[0].type });
|
||||
// this.cdr.detectChanges();
|
||||
// }, error => {
|
||||
// this.onCallbackUploadFail(error.error);
|
||||
// })
|
||||
this.storageFileService.uploadTempFiles(this.filesToUpload[0])
|
||||
.pipe(takeUntil(this._destroyed)).subscribe((response) => {
|
||||
this.propertiesFormGroup?.get(this.field.id).get('textValue').patchValue(response[0].id.toString());
|
||||
this.fileNameDisplay = response[0].name;
|
||||
this.cdr.detectChanges();
|
||||
}, error => {
|
||||
this.onCallbackUploadFail(error.error);
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -285,66 +306,53 @@ export class DescriptionFormFieldComponent extends BaseComponent implements OnIn
|
|||
}
|
||||
|
||||
fileChangeEvent(fileInput: any, dropped: boolean = false) {
|
||||
//TODO: refactor this
|
||||
// if (this.form.value.value) {
|
||||
// this.onRemove(false);
|
||||
// }
|
||||
|
||||
// if (dropped) {
|
||||
// this.filesToUpload = fileInput.addedFiles;
|
||||
// } else {
|
||||
// this.filesToUpload = fileInput.target.files;
|
||||
// }
|
||||
if (dropped) {
|
||||
this.filesToUpload = fileInput.addedFiles;
|
||||
} else {
|
||||
this.filesToUpload = fileInput.target.files;
|
||||
}
|
||||
|
||||
|
||||
// let messages: string[] = [];
|
||||
// if (this.filesToUpload.length == 0) {
|
||||
// messages.push(this.language.instant('DATASET-WIZARD.MESSAGES.NO-FILES-SELECTED'));
|
||||
// return;
|
||||
// } else {
|
||||
// let fileToUpload = this.filesToUpload[0];
|
||||
// if (this.form.get("data") && this.form.get("data").value.types
|
||||
// && this.form.get("data").value.types.map(type => type.value).includes(fileToUpload.type)
|
||||
// && this.form.get("data").value.maxFileSizeInMB
|
||||
// && this.form.get("data").value.maxFileSizeInMB * 1048576 >= fileToUpload.size) {
|
||||
// this.upload();
|
||||
// } else {
|
||||
// this.filesToUpload = null;
|
||||
// messages.push(this.language.instant('DATASET-WIZARD.MESSAGES.LARGE-FILE-OR-UNACCEPTED-TYPE'));
|
||||
// messages.push(this.language.instant('DATASET-WIZARD.MESSAGES.MAX-FILE-SIZE', { 'maxfilesize': this.form.get("data").value.maxFileSizeInMB }));
|
||||
// messages.push(this.language.instant('DATASET-WIZARD.MESSAGES.ACCEPTED-FILE-TRANSFOMER') + this.form.get("data").value.types.map(type => type.value).join(", "));
|
||||
// }
|
||||
let messages: string[] = [];
|
||||
if (this.filesToUpload.length == 0) {
|
||||
messages.push(this.language.instant('DATASET-WIZARD.MESSAGES.NO-FILES-SELECTED'));
|
||||
return;
|
||||
} else {
|
||||
let fileToUpload = this.filesToUpload[0];
|
||||
const data = this.field.data as DescriptionTemplateUploadData;
|
||||
if (data && data.types
|
||||
&& data.types.map(type => type.value).includes(fileToUpload.type)
|
||||
&& data.maxFileSizeInMB
|
||||
&& data.maxFileSizeInMB * 1048576 >= fileToUpload.size) {
|
||||
this.upload();
|
||||
} else {
|
||||
this.filesToUpload = null;
|
||||
messages.push(this.language.instant('DATASET-WIZARD.MESSAGES.LARGE-FILE-OR-UNACCEPTED-TYPE'));
|
||||
messages.push(this.language.instant('DATASET-WIZARD.MESSAGES.MAX-FILE-SIZE', { 'maxfilesize': data.maxFileSizeInMB }));
|
||||
messages.push(this.language.instant('DATASET-WIZARD.MESSAGES.ACCEPTED-FILE-TRANSFOMER') + data.types.map(type => type.value).join(", "));
|
||||
}
|
||||
|
||||
// if (messages && messages.length > 0) {
|
||||
// this.dialog.open(FormValidationErrorsDialogComponent, {
|
||||
// data: {
|
||||
// errorMessages: messages
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
if (messages && messages.length > 0) {
|
||||
this.dialog.open(FormValidationErrorsDialogComponent, {
|
||||
data: {
|
||||
errorMessages: messages
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onRemove(makeFilesNull: boolean = true) {
|
||||
//TODO: refactor this
|
||||
// delete from tmp folder - subscribe call
|
||||
// this.fileService.deleteFromTempFolder(this.form.value.value.id).subscribe(res => {
|
||||
// if (makeFilesNull) {
|
||||
// this.makeFilesNull();
|
||||
// }
|
||||
// this.cdr.detectChanges();
|
||||
// }, error => {
|
||||
// if (makeFilesNull) {
|
||||
// this.makeFilesNull();
|
||||
// }
|
||||
// })
|
||||
this.makeFilesNull()
|
||||
this.cdr.detectChanges();
|
||||
|
||||
}
|
||||
|
||||
makeFilesNull() {
|
||||
//TODO: refactor this
|
||||
// this.filesToUpload = null;
|
||||
// this.form.value.value = null;
|
||||
// this.form.get("value").patchValue(null);
|
||||
this.filesToUpload = null;
|
||||
this.propertiesFormGroup?.get(this.field.id).get('textValue').patchValue(null);
|
||||
this.fileNameDisplay = null;
|
||||
}
|
||||
|
||||
typesToString() {
|
||||
|
@ -352,14 +360,18 @@ export class DescriptionFormFieldComponent extends BaseComponent implements OnIn
|
|||
}
|
||||
|
||||
download(): void {
|
||||
//TODO: refactor this
|
||||
// this.fileService.download(this.form.value.value.id)
|
||||
// .pipe(takeUntil(this._destroyed))
|
||||
// .subscribe(response => {
|
||||
// const blob = new Blob([response.body], { type: this.form.value.value.type });
|
||||
// const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition'));
|
||||
|
||||
// FileSaver.saveAs(blob, filename);
|
||||
// });
|
||||
if (this.propertiesFormGroup?.get(this.field.id).get('textValue').value) {
|
||||
const id = Guid.parse((this.propertiesFormGroup?.get(this.field.id).get('textValue').value as string));
|
||||
|
||||
this.storageFileService.download(id).subscribe((response) => {
|
||||
const blob = new Blob([response.body]);
|
||||
const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition'));
|
||||
FileSaver.saveAs(blob, filename);
|
||||
}, error => {
|
||||
this.onCallbackUploadFail(error.error);
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue