Skip to content
Snippets Groups Projects
Commit c87245bf authored by François Agneray's avatar François Agneray
Browse files

Uniqid for dataset files archive

parent 51591dc9
No related branches found
No related tags found
2 merge requests!72Develop,!32Resolve "Télécharger une archive"
......@@ -44,14 +44,11 @@
<a *ngIf="!archiveInProgress" [href]="createFilesArchiveUrl()" (click)="createFilesArchive($event, createFilesArchiveUrl())" class="btn btn-outline-primary" title="Download an archive with all files">
<span class="fas fa-archive"></span> Files archive
</a>
<div *ngIf="archiveInProgress && !archiveIsAvailable">
<div *ngIf="archiveInProgress">
<span class="fas fa-circle-notch fa-spin fa-3x"></span>
<span class="sr-only">Loading...</span>
Please wait archive is under construction...
</div>
<a *ngIf="archiveIsAvailable" [href]="getArchiveUrl()" (click)="click($event, getArchiveUrl(), 'zip')" class="btn btn-outline-primary" title="Download an archive with all files">
<span class="fas fa-archive"></span> {{ archiveName }}
</a>
</div>
</div>
</div>
......
......@@ -8,7 +8,7 @@
*/
import { Component, EventEmitter, Input, OnDestroy, Output } from '@angular/core';
import { HttpClient, HttpRequest, HttpEventType } from '@angular/common/http';
import { HttpClient } from '@angular/common/http';
import { interval, Subscription} from 'rxjs';
import { Criterion, ConeSearch, criterionToString } from '../../../store/models';
......@@ -36,8 +36,8 @@ export class DownloadComponent implements OnDestroy {
@Output() startsDownloadingFile: EventEmitter<{url: string, filename: string}> = new EventEmitter();
archiveName = '';
archiveId ='';
archiveInProgress = false;
archiveIsAvailable = false;
archiveIsAvailableSubscription: Subscription
constructor(private appConfig: AppConfigService, private http: HttpClient) { }
......@@ -96,7 +96,7 @@ export class DownloadComponent implements OnDestroy {
/**
* Allows to download file.
*/
click(event, href, extension): void {
click(event, href, extension): void {
event.preventDefault();
const url = href;
......@@ -110,7 +110,7 @@ export class DownloadComponent implements OnDestroy {
*
* @fires EventEmitter<string>
*/
broadcastVotable(): void {
broadcastVotable(): void {
this.broadcast.emit(this.getUrl('votable'));
}
......@@ -131,15 +131,16 @@ export class DownloadComponent implements OnDestroy {
}
getArchiveUrl() {
return `${getHost(this.appConfig.apiUrl)}/download-archive?archive_name=${this.archiveName}`;
return `${getHost(this.appConfig.apiUrl)}/download-archive/${this.datasetSelected}?archive_id=${this.archiveId}`;
}
testArchiveIsAvailable() {
const url = `${getHost(this.appConfig.apiUrl)}/archive-is-available?archive_name=${this.archiveName}`;
const url = `${getHost(this.appConfig.apiUrl)}/archive-is-available?archive_id=${this.archiveId}`;
this.http.get<{"archive_is_available": boolean}>(url).subscribe(data => {
if (data.archive_is_available) {
this.archiveIsAvailable = true;
this.archiveInProgress = false;
this.archiveIsAvailableSubscription.unsubscribe();
this.startsDownloadingFile.emit({ url: this.getArchiveUrl(), filename: this.archiveName })
}
});
}
......@@ -150,9 +151,10 @@ export class DownloadComponent implements OnDestroy {
createFilesArchive(event, href): void {
event.preventDefault();
this.http.get<{"archive": string}>(href).subscribe(data => {
this.http.get<{"archive_name": string, "archive_id": string}>(href).subscribe(data => {
this.archiveInProgress = true;
this.archiveName = data.archive;
this.archiveName = data.archive_name;
this.archiveId = data.archive_id;
this.archiveIsAvailableSubscription = interval(1000).subscribe(() => this.testArchiveIsAvailable());
});
}
......
......@@ -11,4 +11,4 @@ import { createAction, props } from '@ngrx/store';
export const startsDownloadingFile = createAction('[File] Starts Downloading File', props<{ url: string, filename: string }>());
export const updateDownloadProgress = createAction('[File] Update Download Progress', props<{ progress: number, filename: string }>());
export const startsArchiveFilesCreation = createAction('[File] Starts Archive Files Creation');
export const fileDownloaded = createAction('[File] File Downloaded', props<{ filename: string }>());
......@@ -10,11 +10,9 @@
import { Injectable } from '@angular/core';
import { HttpEventType } from '@angular/common/http';
import { Actions, createEffect, ofType, concatLatestFrom } from '@ngrx/effects';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { Store } from '@ngrx/store';
import { of } from 'rxjs';
import { map, tap, mergeMap, catchError } from 'rxjs/operators';
import { ToastrService } from 'ngx-toastr';
import { map, mergeMap } from 'rxjs/operators';
import { DownloadFileService } from '../services/download-file.service';
import * as downloadFileActions from '../actions/download-file.actions';
......@@ -42,6 +40,7 @@ export class DownloadFileEffects {
}
if (event.type === HttpEventType.Response) {
this.store.dispatch(downloadFileActions.fileDownloaded({ filename: action.filename }));
this.downloadFileService.saveDownloadedFile(event.body as Blob, action.filename);
}
})
......@@ -53,7 +52,6 @@ export class DownloadFileEffects {
constructor(
private actions$: Actions,
private downloadFileService: DownloadFileService,
private store: Store<{ }>,
private toastr: ToastrService
private store: Store<{ }>
) {}
}
......@@ -45,6 +45,17 @@ export const fileReducer = createReducer(
progress: progress
}
]
})),
on(downloadFileActions.fileDownloaded, (state, { filename }) => ({
...state,
downloadedFiles: [
...state.downloadedFiles.filter(f => f.name !== filename),
{
name: filename,
state: 'DONE',
progress: 100
}
]
}))
);
......
......@@ -70,7 +70,7 @@ $app->group('', function (RouteCollectorProxy $group) {
$app->get('/search/{dname}', App\Action\SearchAction::class);
$app->get('/archive/{dname}', App\Action\ArchiveAction::class);
$app->get('/archive-is-available', App\Action\ArchiveIsAvailableAction::class);
$app->get('/download-archive', App\Action\DownloadArchiveAction::class);
$app->get('/download-archive/{dname}', App\Action\DownloadArchiveAction::class);
$app->get('/dataset-file-explorer/{dname}[{fpath:.*}]', App\Action\DatasetFileExplorerAction::class);
$app->get('/download-instance-file/{iname}/[{fpath:.*}]', App\Action\DownloadInstanceFileAction::class);
$app->get('/download-file/{dname}/[{fpath:.*}]', App\Action\DownloadFileAction::class);
......@@ -112,12 +112,13 @@ final class ArchiveAction extends AbstractAction
// Create the name of the future archive
$archiveName = 'archive_' . $dataset->getName() . '_' . (new \DateTime())->format('Y-m-d\TH:i:s') . '.zip';
$archiveId = uniqid();
// Publish message in the archive queue
$channel = $this->rmq->channel();
$channel->queue_declare('archive', false, false, false, false);
$msg = new AMQPMessage(json_encode(array(
'archive_name' => $archiveName,
'archive_id' => $archiveId,
'dataset_name' => $datasetName,
'query' => $request->getUri()->getQuery(),
'param_a' => $queryParams['a']
......@@ -125,7 +126,7 @@ final class ArchiveAction extends AbstractAction
$channel->basic_publish($msg, '', 'archive');
// Just returns the future archive name
$payload = json_encode(array('archive' => $archiveName));
$payload = json_encode(array('archive_name' => $archiveName, 'archive_id' => $archiveId));
$response->getBody()->write($payload);
return $response;
}
......
......@@ -68,10 +68,10 @@ final class ArchiveIsAvailableAction extends AbstractAction
}
$queryParams = $request->getQueryParams();
$archiveName = $queryParams['archive_name'];
$archiveId = $queryParams['archive_id'] . '.zip';
// Search the file
$filePath = $this->dataPath . '/ARCHIVE/' . $archiveName;
$filePath = $this->dataPath . '/ARCHIVE/' . $archiveId;
$isAvailable = false;
if (file_exists($filePath)) {
......
......@@ -66,18 +66,38 @@ final class DownloadArchiveAction extends AbstractAction
if ($request->getMethod() === OPTIONS) {
return $response->withHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
}
// Search the correct dataset with primary key
$dataset = $this->em->find('App\Entity\Dataset', $args['dname']);
// If dataset is not found 404
if (is_null($dataset)) {
throw new HttpNotFoundException(
$request,
'Dataset with name ' . $args['dname'] . ' is not found'
);
}
// If dataset is private and authorization enabled
if (!$dataset->getPublic() && boolval($this->settings['enabled'])) {
$this->verifyDatasetAuthorization(
$request,
$dataset->getName(),
explode(',', $this->settings['admin_roles'])
);
}
$queryParams = $request->getQueryParams();
$archiveName = $queryParams['archive_name'];
$archiveId = $queryParams['archive_id'];
// Search the file
$filePath = $this->dataPath . '/ARCHIVE/' . $archiveName;
$filePath = $this->dataPath . '/ARCHIVE/' . $archiveId . '.zip';
// If the file not found 404
if (!file_exists($filePath)) {
throw new HttpNotFoundException(
$request,
'Archive file with name ' . $archiveName . ' is not found'
'Archive file with name ' . $archiveId . '.zip is not found'
);
}
......
......@@ -21,7 +21,7 @@ def archive_handler(ch, method, properties, body):
# create a ZipFile object
data_path = utils.get_data_path()
zip_path = data_path + "/ARCHIVE/" + message["archive_name"]
zip_path = data_path + "/ARCHIVE/" + message["archive_id"] + ".zip"
zip = ZipFile(zip_path + ".tmp", 'w')
# Search files
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment