mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-09-17 14:21:29 +00:00
[AAE-1977] Fix file uploaded after cancelling upload (#5532)
* [AAE-1977] Fix file uploaded after cancelling upload * Add constants
This commit is contained in:
@@ -73,8 +73,7 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy {
|
|||||||
private uploadService: UploadService,
|
private uploadService: UploadService,
|
||||||
private changeDetector: ChangeDetectorRef,
|
private changeDetector: ChangeDetectorRef,
|
||||||
private userPreferencesService: UserPreferencesService,
|
private userPreferencesService: UserPreferencesService,
|
||||||
private elementRef: ElementRef
|
private elementRef: ElementRef) {
|
||||||
) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
@@ -104,9 +103,9 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.counterSubscription = merge(
|
this.counterSubscription = merge(
|
||||||
this.uploadService.fileUploadComplete,
|
this.uploadService.fileUploadComplete,
|
||||||
this.uploadService.fileUploadDeleted
|
this.uploadService.fileUploadDeleted
|
||||||
)
|
)
|
||||||
.pipe(takeUntil(this.onDestroy$))
|
.pipe(takeUntil(this.onDestroy$))
|
||||||
.subscribe(event => {
|
.subscribe(event => {
|
||||||
this.totalCompleted = event.totalComplete;
|
this.totalCompleted = event.totalComplete;
|
||||||
@@ -130,11 +129,11 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy {
|
|||||||
.pipe(takeUntil(this.onDestroy$))
|
.pipe(takeUntil(this.onDestroy$))
|
||||||
.subscribe(objId => {
|
.subscribe(objId => {
|
||||||
if (this.filesUploadingList) {
|
if (this.filesUploadingList) {
|
||||||
const file = this.filesUploadingList.find((item) => {
|
const uploadedFile = this.filesUploadingList.find((file) => {
|
||||||
return item.data.entry.id === objId;
|
return file.data ? file.data.entry.id === objId : false;
|
||||||
});
|
});
|
||||||
if (file) {
|
if (uploadedFile) {
|
||||||
file.status = FileUploadStatus.Cancelled;
|
uploadedFile.status = FileUploadStatus.Cancelled;
|
||||||
this.changeDetector.detectChanges();
|
this.changeDetector.detectChanges();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -54,8 +54,8 @@ export class FileUploadingListComponent {
|
|||||||
constructor(
|
constructor(
|
||||||
private uploadService: UploadService,
|
private uploadService: UploadService,
|
||||||
private nodesApi: NodesApiService,
|
private nodesApi: NodesApiService,
|
||||||
private translateService: TranslationService
|
private translateService: TranslationService) {
|
||||||
) {}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancel file upload
|
* Cancel file upload
|
||||||
@@ -91,16 +91,18 @@ export class FileUploadingListComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call the appropriate method for each file, depending on state
|
* Calls the appropriate methods for each file, depending on state
|
||||||
*/
|
*/
|
||||||
cancelAllFiles(): void {
|
cancelAllFiles(): void {
|
||||||
this.getUploadingFiles().forEach((file) =>
|
const deletedFiles: Observable<FileModel>[] = [];
|
||||||
this.uploadService.cancelUpload(file)
|
|
||||||
);
|
|
||||||
|
|
||||||
const deletedFiles = this.files
|
this.files.forEach((file) => {
|
||||||
.filter((file) => file.status === FileUploadStatus.Complete)
|
if (this.isUploadingFile(file)) {
|
||||||
.map((file) => this.deleteNode(file));
|
this.uploadService.cancelUpload(file);
|
||||||
|
} else if (file.status === FileUploadStatus.Complete) {
|
||||||
|
deletedFiles.push(this.deleteNode(file));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
forkJoin(...deletedFiles).subscribe((files: FileModel[]) => {
|
forkJoin(...deletedFiles).subscribe((files: FileModel[]) => {
|
||||||
const errors = files.filter(
|
const errors = files.filter(
|
||||||
@@ -192,12 +194,9 @@ export class FileUploadingListComponent {
|
|||||||
this.error.emit(messageError);
|
this.error.emit(messageError);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getUploadingFiles(): FileModel[] {
|
private isUploadingFile(file: FileModel): boolean {
|
||||||
return this.files.filter(
|
return file.status === FileUploadStatus.Pending ||
|
||||||
item =>
|
file.status === FileUploadStatus.Starting ||
|
||||||
item.status === FileUploadStatus.Pending ||
|
file.status === FileUploadStatus.Progress;
|
||||||
item.status === FileUploadStatus.Progress ||
|
|
||||||
item.status === FileUploadStatus.Starting
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -146,21 +146,62 @@ describe('UploadService', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should make XHR abort request after the xhr abort is called', (done) => {
|
it('should abort file only if it\'s safe to abort', (done) => {
|
||||||
const emitter = new EventEmitter();
|
const emitter = new EventEmitter();
|
||||||
|
|
||||||
const emitterDisposable = emitter.subscribe((e) => {
|
const emitterDisposable = emitter.subscribe((event) => {
|
||||||
expect(e.value).toEqual('File aborted');
|
expect(event.value).toEqual('File aborted');
|
||||||
emitterDisposable.unsubscribe();
|
emitterDisposable.unsubscribe();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const fileFake = new FileModel(<File> { name: 'fake-name', size: 10000000 });
|
||||||
|
service.addToQueue(fileFake);
|
||||||
|
service.uploadFilesInTheQueue(emitter);
|
||||||
|
|
||||||
|
const file = service.getQueue();
|
||||||
|
service.cancelUpload(...file);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should let file complete and then delete node if it\'s not safe to abort', (done) => {
|
||||||
|
const emitter = new EventEmitter();
|
||||||
|
|
||||||
|
const emitterDisposable = emitter.subscribe((event) => {
|
||||||
|
expect(event.value).toEqual('File deleted');
|
||||||
|
emitterDisposable.unsubscribe();
|
||||||
|
|
||||||
|
const deleteRequest = jasmine.Ajax.requests.mostRecent();
|
||||||
|
expect(deleteRequest.url).toBe('http://localhost:9876/ecm/alfresco/api/-default-/public/alfresco/versions/1/nodes/myNodeId?permanent=true');
|
||||||
|
expect(deleteRequest.method).toBe('DELETE');
|
||||||
|
|
||||||
|
jasmine.Ajax.requests.mostRecent().respondWith({
|
||||||
|
'status': 200,
|
||||||
|
contentType: 'text/plain',
|
||||||
|
responseText: 'File deleted'
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
const fileFake = new FileModel(<File> { name: 'fake-name', size: 10 });
|
const fileFake = new FileModel(<File> { name: 'fake-name', size: 10 });
|
||||||
service.addToQueue(fileFake);
|
service.addToQueue(fileFake);
|
||||||
service.uploadFilesInTheQueue(emitter);
|
service.uploadFilesInTheQueue(emitter);
|
||||||
|
|
||||||
const file = service.getQueue();
|
const file = service.getQueue();
|
||||||
service.cancelUpload(...file);
|
service.cancelUpload(...file);
|
||||||
|
|
||||||
|
const request = jasmine.Ajax.requests.mostRecent();
|
||||||
|
expect(request.url).toBe('http://localhost:9876/ecm/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children?autoRename=true&include=allowableOperations');
|
||||||
|
expect(request.method).toBe('POST');
|
||||||
|
|
||||||
|
jasmine.Ajax.requests.mostRecent().respondWith({
|
||||||
|
'status': 200,
|
||||||
|
contentType: 'json',
|
||||||
|
responseText: {
|
||||||
|
entry: {
|
||||||
|
id: 'myNodeId'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('If newVersion is set, name should be a param', () => {
|
it('If newVersion is set, name should be a param', () => {
|
||||||
@@ -196,7 +237,7 @@ describe('UploadService', () => {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
const filesFake = new FileModel(
|
const filesFake = new FileModel(
|
||||||
<File> { name: 'fake-name', size: 10 },
|
<File> { name: 'fake-file-name', size: 10 },
|
||||||
<FileUploadOptions> { parentId: '123', path: 'fake-dir' }
|
<FileUploadOptions> { parentId: '123', path: 'fake-dir' }
|
||||||
);
|
);
|
||||||
service.addToQueue(filesFake);
|
service.addToQueue(filesFake);
|
||||||
|
@@ -28,6 +28,9 @@ import {
|
|||||||
import { FileModel, FileUploadProgress, FileUploadStatus } from '../models/file.model';
|
import { FileModel, FileUploadProgress, FileUploadStatus } from '../models/file.model';
|
||||||
import { AlfrescoApiService } from './alfresco-api.service';
|
import { AlfrescoApiService } from './alfresco-api.service';
|
||||||
|
|
||||||
|
const MIN_CANCELLABLE_FILE_SIZE = 1000000;
|
||||||
|
const MAX_CANCELLABLE_FILE_PERCENTAGE = 50;
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
@@ -39,6 +42,7 @@ export class UploadService {
|
|||||||
private totalError: number = 0;
|
private totalError: number = 0;
|
||||||
private excludedFileList: string[] = [];
|
private excludedFileList: string[] = [];
|
||||||
private matchingOptions: any = null;
|
private matchingOptions: any = null;
|
||||||
|
private abortedFile: string;
|
||||||
|
|
||||||
activeTask: Promise<any> = null;
|
activeTask: Promise<any> = null;
|
||||||
queue: FileModel[] = [];
|
queue: FileModel[] = [];
|
||||||
@@ -113,7 +117,7 @@ export class UploadService {
|
|||||||
|
|
||||||
const promise = this.beginUpload(file, emitter);
|
const promise = this.beginUpload(file, emitter);
|
||||||
this.activeTask = promise;
|
this.activeTask = promise;
|
||||||
this.cache[file.id] = promise;
|
this.cache[file.name] = promise;
|
||||||
|
|
||||||
const next = () => {
|
const next = () => {
|
||||||
this.activeTask = null;
|
this.activeTask = null;
|
||||||
@@ -132,15 +136,21 @@ export class UploadService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancels uploading of files.
|
* Cancels uploading of files.
|
||||||
|
* If the file is smaller than 1 MB the file will be uploaded and then the node deleted
|
||||||
|
* to prevent having files that were aborted but still uploaded.
|
||||||
* @param files One or more separate parameters or an array of files specifying uploads to cancel
|
* @param files One or more separate parameters or an array of files specifying uploads to cancel
|
||||||
*/
|
*/
|
||||||
cancelUpload(...files: FileModel[]) {
|
cancelUpload(...files: FileModel[]) {
|
||||||
files.forEach((file) => {
|
files.forEach((file) => {
|
||||||
const promise = this.cache[file.id];
|
const promise = this.cache[file.name];
|
||||||
|
|
||||||
if (promise) {
|
if (promise) {
|
||||||
promise.abort();
|
if (this.isSaveToAbortFile(file)) {
|
||||||
delete this.cache[file.id];
|
promise.abort();
|
||||||
|
}
|
||||||
|
this.abortedFile = file.name;
|
||||||
|
delete this.cache[file.name];
|
||||||
|
promise.next();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
const performAction = this.getAction(file);
|
const performAction = this.getAction(file);
|
||||||
performAction();
|
performAction();
|
||||||
@@ -200,7 +210,6 @@ export class UploadService {
|
|||||||
private beginUpload(file: FileModel, emitter: EventEmitter<any>): any {
|
private beginUpload(file: FileModel, emitter: EventEmitter<any>): any {
|
||||||
|
|
||||||
const promise = this.getUploadPromise(file);
|
const promise = this.getUploadPromise(file);
|
||||||
|
|
||||||
promise.on('progress', (progress: FileUploadProgress) => {
|
promise.on('progress', (progress: FileUploadProgress) => {
|
||||||
this.onUploadProgress(file, progress);
|
this.onUploadProgress(file, progress);
|
||||||
})
|
})
|
||||||
@@ -217,12 +226,20 @@ export class UploadService {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.on('success', (data) => {
|
.on('success', (data) => {
|
||||||
this.onUploadComplete(file, data);
|
if (this.abortedFile === file.name) {
|
||||||
if (emitter) {
|
this.onUploadAborted(file);
|
||||||
emitter.emit({ value: data });
|
this.deleteAbortedNode(data.entry.id);
|
||||||
|
if (emitter) {
|
||||||
|
emitter.emit({ value: 'File deleted' });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.onUploadComplete(file, data);
|
||||||
|
if (emitter) {
|
||||||
|
emitter.emit({ value: data });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {});
|
.catch(() => { });
|
||||||
|
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
@@ -249,13 +266,13 @@ export class UploadService {
|
|||||||
|
|
||||||
private onUploadError(file: FileModel, error: any): void {
|
private onUploadError(file: FileModel, error: any): void {
|
||||||
if (file) {
|
if (file) {
|
||||||
file.errorCode = ( error || {} ).status;
|
file.errorCode = (error || {}).status;
|
||||||
file.status = FileUploadStatus.Error;
|
file.status = FileUploadStatus.Error;
|
||||||
this.totalError++;
|
this.totalError++;
|
||||||
|
|
||||||
const promise = this.cache[file.id];
|
const promise = this.cache[file.name];
|
||||||
if (promise) {
|
if (promise) {
|
||||||
delete this.cache[file.id];
|
delete this.cache[file.name];
|
||||||
}
|
}
|
||||||
|
|
||||||
const event = new FileUploadErrorEvent(file, error, this.totalError);
|
const event = new FileUploadErrorEvent(file, error, this.totalError);
|
||||||
@@ -269,10 +286,9 @@ export class UploadService {
|
|||||||
file.status = FileUploadStatus.Complete;
|
file.status = FileUploadStatus.Complete;
|
||||||
file.data = data;
|
file.data = data;
|
||||||
this.totalComplete++;
|
this.totalComplete++;
|
||||||
|
const promise = this.cache[file.name];
|
||||||
const promise = this.cache[file.id];
|
|
||||||
if (promise) {
|
if (promise) {
|
||||||
delete this.cache[file.id];
|
delete this.cache[file.name];
|
||||||
}
|
}
|
||||||
|
|
||||||
const event = new FileUploadCompleteEvent(file, this.totalComplete, data, this.totalAborted);
|
const event = new FileUploadCompleteEvent(file, this.totalComplete, data, this.totalAborted);
|
||||||
@@ -286,15 +302,9 @@ export class UploadService {
|
|||||||
file.status = FileUploadStatus.Aborted;
|
file.status = FileUploadStatus.Aborted;
|
||||||
this.totalAborted++;
|
this.totalAborted++;
|
||||||
|
|
||||||
const promise = this.cache[file.id];
|
|
||||||
if (promise) {
|
|
||||||
delete this.cache[file.id];
|
|
||||||
}
|
|
||||||
|
|
||||||
const event = new FileUploadEvent(file, FileUploadStatus.Aborted);
|
const event = new FileUploadEvent(file, FileUploadStatus.Aborted);
|
||||||
this.fileUpload.next(event);
|
this.fileUpload.next(event);
|
||||||
this.fileUploadAborted.next(event);
|
this.fileUploadAborted.next(event);
|
||||||
promise.next();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,7 +329,7 @@ export class UploadService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getAction(file) {
|
private getAction(file: FileModel) {
|
||||||
const actions = {
|
const actions = {
|
||||||
[FileUploadStatus.Pending]: () => this.onUploadCancelled(file),
|
[FileUploadStatus.Pending]: () => this.onUploadCancelled(file),
|
||||||
[FileUploadStatus.Deleted]: () => this.onUploadDeleted(file),
|
[FileUploadStatus.Deleted]: () => this.onUploadDeleted(file),
|
||||||
@@ -328,4 +338,14 @@ export class UploadService {
|
|||||||
|
|
||||||
return actions[file.status];
|
return actions[file.status];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private deleteAbortedNode(nodeId: string) {
|
||||||
|
this.apiService.getInstance().core.nodesApi.deleteNode(nodeId, { permanent: true })
|
||||||
|
.then(() => this.abortedFile = undefined);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private isSaveToAbortFile(file: FileModel): boolean {
|
||||||
|
return file.size > MIN_CANCELLABLE_FILE_SIZE && file.progress.percent < MAX_CANCELLABLE_FILE_PERCENTAGE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user