Merge pull request #5505 from tsiegleauq/multiple-file-download
Download mutliple files as zip
This commit is contained in:
commit
c6abbb629e
@ -59,6 +59,7 @@
|
|||||||
"css-element-queries": "^1.2.3",
|
"css-element-queries": "^1.2.3",
|
||||||
"exceljs": "1.15.0",
|
"exceljs": "1.15.0",
|
||||||
"file-saver": "^2.0.2",
|
"file-saver": "^2.0.2",
|
||||||
|
"jszip": "^3.5.0",
|
||||||
"lz4js": "^0.2.0",
|
"lz4js": "^0.2.0",
|
||||||
"material-icon-font": "git+https://github.com/petergng/materialIconFont.git",
|
"material-icon-font": "git+https://github.com/petergng/materialIconFont.git",
|
||||||
"moment": "^2.24.0",
|
"moment": "^2.24.0",
|
||||||
|
@ -273,4 +273,26 @@ export class HttpService {
|
|||||||
public async delete<T>(path: string, data?: any, queryParams?: QueryParams, header?: HttpHeaders): Promise<T> {
|
public async delete<T>(path: string, data?: any, queryParams?: QueryParams, header?: HttpHeaders): Promise<T> {
|
||||||
return await this.send<T>(path, HTTPMethod.DELETE, data, queryParams, header);
|
return await this.send<T>(path, HTTPMethod.DELETE, data, queryParams, header);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a binary file from the url and returns a base64 value
|
||||||
|
*
|
||||||
|
* @param url file url
|
||||||
|
* @returns a promise with a base64 string
|
||||||
|
*/
|
||||||
|
public async downloadAsBase64(url: string): Promise<string> {
|
||||||
|
return new Promise<string>(async (resolve, reject) => {
|
||||||
|
const headers = new HttpHeaders();
|
||||||
|
const file = await this.get<Blob>(url, {}, {}, headers, 'blob');
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
reader.onload = () => {
|
||||||
|
const resultStr: string = reader.result as string;
|
||||||
|
resolve(resultStr.split(',')[1]);
|
||||||
|
};
|
||||||
|
reader.onerror = error => {
|
||||||
|
reject(error);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { HttpHeaders } from '@angular/common/http';
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||||
|
|
||||||
@ -100,7 +99,7 @@ export class PdfDocumentService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const promises = fontPathList.map(fontPath => {
|
const promises = fontPathList.map(fontPath => {
|
||||||
return this.convertUrlToBase64(fontPath).then(base64 => {
|
return this.httpService.downloadAsBase64(fontPath).then(base64 => {
|
||||||
return {
|
return {
|
||||||
[fontPath.split('/').pop()]: base64
|
[fontPath.split('/').pop()]: base64
|
||||||
};
|
};
|
||||||
@ -117,29 +116,6 @@ export class PdfDocumentService {
|
|||||||
return vfs;
|
return vfs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves a binary file from the url and returns a base64 value
|
|
||||||
*
|
|
||||||
* @param url file url
|
|
||||||
* @returns a promise with a base64 string
|
|
||||||
*/
|
|
||||||
private async convertUrlToBase64(url: string): Promise<string> {
|
|
||||||
return new Promise<string>((resolve, reject) => {
|
|
||||||
const headers = new HttpHeaders();
|
|
||||||
this.httpService.get<Blob>(url, {}, {}, headers, 'blob').then(file => {
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.readAsDataURL(file);
|
|
||||||
reader.onload = () => {
|
|
||||||
const resultStr: string = reader.result as string;
|
|
||||||
resolve(resultStr.split(',')[1]);
|
|
||||||
};
|
|
||||||
reader.onerror = error => {
|
|
||||||
reject(error);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the name of a font from the value of the given
|
* Returns the name of a font from the value of the given
|
||||||
* config variable.
|
* config variable.
|
||||||
@ -665,7 +641,7 @@ export class PdfDocumentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!vfs[url]) {
|
if (!vfs[url]) {
|
||||||
const base64 = await this.convertUrlToBase64(url);
|
const base64 = await this.httpService.downloadAsBase64(url);
|
||||||
vfs[url] = base64;
|
vfs[url] = base64;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,8 @@ import { HttpHeaders } from '@angular/common/http';
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { saveAs } from 'file-saver';
|
||||||
|
import * as JSZip from 'jszip';
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
import { first, map } from 'rxjs/operators';
|
import { first, map } from 'rxjs/operators';
|
||||||
|
|
||||||
@ -136,6 +138,18 @@ export class MediafileRepositoryService extends BaseIsListOfSpeakersContentObjec
|
|||||||
return this.httpService.post<Identifiable>('/rest/mediafiles/mediafile/', file, {}, emptyHeader);
|
return this.httpService.post<Identifiable>('/rest/mediafiles/mediafile/', file, {}, emptyHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async downloadArchive(archiveName: string, files: ViewMediafile[]): Promise<void> {
|
||||||
|
const zip = new JSZip();
|
||||||
|
for (const file of files) {
|
||||||
|
if (!file.is_directory) {
|
||||||
|
const base64Data = await this.httpService.downloadAsBase64(file.url);
|
||||||
|
zip.file(file.filename, base64Data, { base64: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const archive = await zip.generateAsync({ type: 'blob' });
|
||||||
|
saveAs(archive, archiveName);
|
||||||
|
}
|
||||||
|
|
||||||
public getDirectoryBehaviorSubject(): BehaviorSubject<ViewMediafile[]> {
|
public getDirectoryBehaviorSubject(): BehaviorSubject<ViewMediafile[]> {
|
||||||
return this.directoryBehaviorSubject;
|
return this.directoryBehaviorSubject;
|
||||||
}
|
}
|
||||||
|
@ -229,12 +229,20 @@
|
|||||||
<!-- Menu for Mediafiles -->
|
<!-- Menu for Mediafiles -->
|
||||||
<mat-menu #mediafilesMenu="matMenu">
|
<mat-menu #mediafilesMenu="matMenu">
|
||||||
<div *ngIf="!isMultiSelect">
|
<div *ngIf="!isMultiSelect">
|
||||||
|
<button mat-menu-item (click)="downloadMultiple()">
|
||||||
|
<mat-icon>cloud_download</mat-icon>
|
||||||
|
<span>{{ 'Download folder' | translate }}</span>
|
||||||
|
</button>
|
||||||
<button mat-menu-item *osPerms="'mediafiles.can_manage'" (click)="toggleMultiSelect()">
|
<button mat-menu-item *osPerms="'mediafiles.can_manage'" (click)="toggleMultiSelect()">
|
||||||
<mat-icon>library_add</mat-icon>
|
<mat-icon>library_add</mat-icon>
|
||||||
<span>{{ 'Multiselect' | translate }}</span>
|
<span>{{ 'Multiselect' | translate }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="isMultiSelect">
|
<div *ngIf="isMultiSelect">
|
||||||
|
<button mat-menu-item [disabled]="!selectedRows.length" (click)="downloadMultiple(selectedRows)">
|
||||||
|
<mat-icon>cloud_download</mat-icon>
|
||||||
|
<span>{{ 'Download selected' | translate }}</span>
|
||||||
|
</button>
|
||||||
<button mat-menu-item [disabled]="!selectedRows.length" (click)="move(moveDialog, selectedRows)">
|
<button mat-menu-item [disabled]="!selectedRows.length" (click)="move(moveDialog, selectedRows)">
|
||||||
<mat-icon>near_me</mat-icon>
|
<mat-icon>near_me</mat-icon>
|
||||||
<span>{{ 'Move' | translate }}</span>
|
<span>{{ 'Move' | translate }}</span>
|
||||||
|
@ -23,6 +23,7 @@ import { OperatorService, Permission } from 'app/core/core-services/operator.ser
|
|||||||
import { StorageService } from 'app/core/core-services/storage.service';
|
import { StorageService } from 'app/core/core-services/storage.service';
|
||||||
import { MediafileRepositoryService } from 'app/core/repositories/mediafiles/mediafile-repository.service';
|
import { MediafileRepositoryService } from 'app/core/repositories/mediafiles/mediafile-repository.service';
|
||||||
import { GroupRepositoryService } from 'app/core/repositories/users/group-repository.service';
|
import { GroupRepositoryService } from 'app/core/repositories/users/group-repository.service';
|
||||||
|
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||||
import { MediaManageService } from 'app/core/ui-services/media-manage.service';
|
import { MediaManageService } from 'app/core/ui-services/media-manage.service';
|
||||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||||
import { ViewportService } from 'app/core/ui-services/viewport.service';
|
import { ViewportService } from 'app/core/ui-services/viewport.service';
|
||||||
@ -202,7 +203,8 @@ export class MediafileListComponent extends BaseListViewComponent<ViewMediafile>
|
|||||||
private fb: FormBuilder,
|
private fb: FormBuilder,
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
private groupRepo: GroupRepositoryService,
|
private groupRepo: GroupRepositoryService,
|
||||||
private cd: ChangeDetectorRef
|
private cd: ChangeDetectorRef,
|
||||||
|
private configService: ConfigService
|
||||||
) {
|
) {
|
||||||
super(titleService, translate, matSnackBar, storage);
|
super(titleService, translate, matSnackBar, storage);
|
||||||
this.canMultiSelect = true;
|
this.canMultiSelect = true;
|
||||||
@ -459,6 +461,16 @@ export class MediafileListComponent extends BaseListViewComponent<ViewMediafile>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public downloadMultiple(mediafiles: ViewMediafile[] = this.dataSource.source): void {
|
||||||
|
/**
|
||||||
|
* TODO: naming the files is discussable
|
||||||
|
*/
|
||||||
|
const eventName = this.configService.instant<string>('general_event_name');
|
||||||
|
const dirName = this.directory?.filename ?? this.translate.instant('Files');
|
||||||
|
const archiveName = `${eventName} - ${dirName}`.trim();
|
||||||
|
this.repo.downloadArchive(archiveName, mediafiles);
|
||||||
|
}
|
||||||
|
|
||||||
public move(templateRef: TemplateRef<string>, mediafiles: ViewMediafile[]): void {
|
public move(templateRef: TemplateRef<string>, mediafiles: ViewMediafile[]): void {
|
||||||
this.moveForm.reset();
|
this.moveForm.reset();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user