diff --git a/client/src/app/instance/instance.reducer.ts b/client/src/app/instance/instance.reducer.ts
index 062c160c5fe4f3fa84539d5e9665a2b5c299f5e5..ed942d2b4006a1d7be4b27325465d2089b8d506e 100644
--- a/client/src/app/instance/instance.reducer.ts
+++ b/client/src/app/instance/instance.reducer.ts
@@ -16,6 +16,7 @@ import * as samp from './store/reducers/samp.reducer';
 import * as coneSearch from './store/reducers/cone-search.reducer';
 import * as detail from './store/reducers/detail.reducer';
 import * as svomJsonKw from './store/reducers/svom-json-kw.reducer';
+import * as downloadFile from './store/reducers/download-file.reducer';
 
 /**
  * Interface for instance state.
@@ -28,7 +29,8 @@ export interface State {
     samp: samp.State,
     coneSearch: coneSearch.State
     detail: detail.State,
-    svomJsonKw: svomJsonKw.State
+    svomJsonKw: svomJsonKw.State,
+    downloadFile: downloadFile.State
 }
 
 const reducers = {
@@ -37,7 +39,8 @@ const reducers = {
     samp: samp.sampReducer,
     coneSearch: coneSearch.coneSearchReducer,
     detail: detail.detailReducer,
-    svomJsonKw: svomJsonKw.svomJsonKwReducer
+    svomJsonKw: svomJsonKw.svomJsonKwReducer,
+    downloadFile: downloadFile.fileReducer
 };
 
 export const instanceReducer = combineReducers(reducers);
diff --git a/client/src/app/instance/search/components/result/datatable-tab.component.html b/client/src/app/instance/search/components/result/datatable-tab.component.html
index b0ad6b0c98a437955dc1be5f759df0ce1f640a94..c638c569a6788c2fcad9652d1aa1ee2d7b1c1439 100644
--- a/client/src/app/instance/search/components/result/datatable-tab.component.html
+++ b/client/src/app/instance/search/components/result/datatable-tab.component.html
@@ -33,7 +33,8 @@
                 [selectedData]="selectedData"
                 (retrieveData)="retrieveData.emit($event)"
                 (addSelectedData)="addSelectedData.emit($event)"
-                (deleteSelectedData)="deleteSelectedData.emit($event)">
+                (deleteSelectedData)="deleteSelectedData.emit($event)"
+                (startsDownloadingFile)="startsDownloadingFile.emit($event)">
         </app-datatable>
     </accordion-group>
 </accordion>
diff --git a/client/src/app/instance/search/components/result/datatable-tab.component.ts b/client/src/app/instance/search/components/result/datatable-tab.component.ts
index c70564ba46caa5d342e2c80c5516221d549dab58..4eee5ec45290fe4ca2bc8e3dfe433d55ad38c55b 100644
--- a/client/src/app/instance/search/components/result/datatable-tab.component.ts
+++ b/client/src/app/instance/search/components/result/datatable-tab.component.ts
@@ -40,4 +40,5 @@ export class DatatableTabComponent {
     @Output() addSelectedData: EventEmitter<number | string> = new EventEmitter();
     @Output() deleteSelectedData: EventEmitter<number | string> = new EventEmitter();
     @Output() broadcast: EventEmitter<string> = new EventEmitter();
+    @Output() startsDownloadingFile: EventEmitter<{url: string, filename: string}> = new EventEmitter();
 }
diff --git a/client/src/app/instance/search/components/result/datatable.component.html b/client/src/app/instance/search/components/result/datatable.component.html
index bbf61801f4fcfe35d312fcebf46867356116be76..f4b0078f47282cd65be076a86f85df27351ce027 100644
--- a/client/src/app/instance/search/components/result/datatable.component.html
+++ b/client/src/app/instance/search/components/result/datatable.component.html
@@ -62,7 +62,8 @@
                                 [value]="datum[attribute.label]"
                                 [datasetName]="dataset.name"
                                 [datasetPublic]="dataset.public"
-                                [config]="getRendererConfig(attribute)">
+                                [config]="getRendererConfig(attribute)"
+                                (startsDownloadingFile)="startsDownloadingFile.emit($event)">
                             </app-download-renderer>
                         </div>
                         <div *ngSwitchCase="'image'">
diff --git a/client/src/app/instance/search/components/result/datatable.component.ts b/client/src/app/instance/search/components/result/datatable.component.ts
index bab2e9c71b6b4105fa0e39b3c018bee5f042e6a7..33191d25025d37bc79730b712355c1ae7fa59f5a 100644
--- a/client/src/app/instance/search/components/result/datatable.component.ts
+++ b/client/src/app/instance/search/components/result/datatable.component.ts
@@ -44,6 +44,7 @@ export class DatatableComponent implements OnInit {
     @Output() retrieveData: EventEmitter<Pagination> = new EventEmitter();
     @Output() addSelectedData: EventEmitter<number | string> = new EventEmitter();
     @Output() deleteSelectedData: EventEmitter<number | string> = new EventEmitter();
+    @Output() startsDownloadingFile: EventEmitter<{url: string, filename: string}> = new EventEmitter();
 
     public page = 1;
     public nbItems = 10;
diff --git a/client/src/app/instance/search/components/result/download-file-tab.component.html b/client/src/app/instance/search/components/result/download-file-tab.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..2030ad73e5a57d05676fc9ced77a03b14cecfe99
--- /dev/null
+++ b/client/src/app/instance/search/components/result/download-file-tab.component.html
@@ -0,0 +1,11 @@
+<div class="jumbotron mb-4 py-4">
+    <div class="lead">
+        Files downloaded : 
+        <ul>
+            <li *ngFor="let downloadFile of downloadedFiles">
+                {{ downloadFile.name }} : 
+                <progressbar [value]="downloadFile.progress" type="warning" [striped]="false">{{ downloadFile.progress }}%</progressbar> 
+            </li>
+        </ul>
+    </div>
+</div>
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/result/download-file-tab.component.scss b/client/src/app/instance/search/components/result/download-file-tab.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/client/src/app/instance/search/components/result/download-file-tab.component.ts b/client/src/app/instance/search/components/result/download-file-tab.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cc8ad76cea0da902fd115c06c7159e8a626bb6ed
--- /dev/null
+++ b/client/src/app/instance/search/components/result/download-file-tab.component.ts
@@ -0,0 +1,25 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { Component, Input } from '@angular/core';
+
+import { DownloadFile } from 'src/app/instance/store/models';
+
+/**
+ * @class
+ * @classdesc Search result reminder component.
+ */
+@Component({
+    selector: 'app-download-file-tab',
+    templateUrl: 'download-file-tab.component.html',
+    styleUrls: ['download-file-tab.component.scss']
+})
+export class DownloadFileTabComponent {
+    @Input() downloadedFiles: DownloadFile[];
+}
diff --git a/client/src/app/instance/search/components/result/download.component.ts b/client/src/app/instance/search/components/result/download.component.ts
index 6094323b74b8711022be8e8b0c92059b17f1d8cb..4b3c199ac2a5fc7ed474960ee75d51986118759a 100644
--- a/client/src/app/instance/search/components/result/download.component.ts
+++ b/client/src/app/instance/search/components/result/download.component.ts
@@ -8,7 +8,7 @@
  */
 
 import { Component, EventEmitter, Input, OnDestroy, Output } from '@angular/core';
-import { HttpClient } from '@angular/common/http';
+import { HttpClient, HttpRequest, HttpEventType } from '@angular/common/http';
 import { interval, Subscription} from 'rxjs';
 
 import { Criterion, ConeSearch, criterionToString } from '../../../store/models';
@@ -33,6 +33,7 @@ export class DownloadComponent implements OnDestroy {
     @Input() dataLength: number;
     @Input() sampRegistered: boolean;
     @Output() broadcast: EventEmitter<string> = new EventEmitter();
+    @Output() startsDownloadingFile: EventEmitter<{url: string, filename: string}> = new EventEmitter();
 
     archiveName = '';
     archiveInProgress = false;
@@ -93,19 +94,15 @@ export class DownloadComponent implements OnDestroy {
     }
 
     /**
-     * Returns URL to download archive.
-     *
-     * @return boolean
+     * Allows to download file.
      */
-    createFilesArchiveUrl(): string {
-        let query: string = `${getHost(this.appConfig.apiUrl)}/archive/${this.datasetSelected}?a=${this.outputList.join(';')}`;
-        if (this.criteriaList.length > 0) {
-            query += `&c=${this.criteriaList.map(criterion => criterionToString(criterion)).join(';')}`;
-        }
-        if (this.coneSearch) {
-            query += `&cs=${this.coneSearch.ra}:${this.coneSearch.dec}:${this.coneSearch.radius}`;
-        }
-        return query;
+     click(event, href, extension): void {
+        event.preventDefault();
+
+        const url = href;
+        const filename = `${this.datasetSelected}.${extension}`;
+
+        this.startsDownloadingFile.emit({ url, filename })
     }
 
     /**
@@ -113,24 +110,24 @@ export class DownloadComponent implements OnDestroy {
      *
      * @fires EventEmitter<string>
      */
-    broadcastVotable(): void {
+     broadcastVotable(): void {
         this.broadcast.emit(this.getUrl('votable'));
     }
 
     /**
-     * Allows to download file.
+     * Returns URL to download archive.
+     *
+     * @return boolean
      */
-    click(event, href, extension): void {
-        event.preventDefault();
-
-        this.http.get(href, {responseType: "blob"}).subscribe(
-            data => {
-                let downloadLink = document.createElement('a');
-                downloadLink.href = window.URL.createObjectURL(data);
-                downloadLink.setAttribute('download', `${this.datasetSelected}.${extension}`);
-                downloadLink.click();
-            }
-        );
+    createFilesArchiveUrl(): string {
+        let query: string = `${getHost(this.appConfig.apiUrl)}/archive/${this.datasetSelected}?a=${this.outputList.join(';')}`;
+        if (this.criteriaList.length > 0) {
+            query += `&c=${this.criteriaList.map(criterion => criterionToString(criterion)).join(';')}`;
+        }
+        if (this.coneSearch) {
+            query += `&cs=${this.coneSearch.ra}:${this.coneSearch.dec}:${this.coneSearch.radius}`;
+        }
+        return query;
     }
 
     getArchiveUrl() {
diff --git a/client/src/app/instance/search/components/result/index.ts b/client/src/app/instance/search/components/result/index.ts
index fffbcf4e766b53c4b4788371155f5e08e7b22207..4fa8d96abe8417aebb5a490ea60e9744f7616cc6 100644
--- a/client/src/app/instance/search/components/result/index.ts
+++ b/client/src/app/instance/search/components/result/index.ts
@@ -5,6 +5,7 @@ import { SampComponent } from './samp.component';
 import { UrlDisplayComponent } from './url-display.component';
 import { DatatableComponent } from './datatable.component';
 import { DatatableActionsComponent } from './datatable-actions.component';
+import { DownloadFileTabComponent } from './download-file-tab.component';
 import { rendererComponents } from './renderer';
 
 export const resultComponents = [
@@ -15,5 +16,6 @@ export const resultComponents = [
     UrlDisplayComponent,
     DatatableComponent,
     DatatableActionsComponent,
+    DownloadFileTabComponent,
     rendererComponents
 ];
diff --git a/client/src/app/instance/search/components/result/renderer/download-renderer.component.ts b/client/src/app/instance/search/components/result/renderer/download-renderer.component.ts
index db6f92c8d0abe1335808f6017f972f99a488ae90..50c551818b7e85b28e6891ef8f60f66c5c1c2310 100644
--- a/client/src/app/instance/search/components/result/renderer/download-renderer.component.ts
+++ b/client/src/app/instance/search/components/result/renderer/download-renderer.component.ts
@@ -7,8 +7,7 @@
  * file that was distributed with this source code.
  */
 
-import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
-import { HttpClient } from '@angular/common/http';
+import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
 
 import { DownloadRendererConfig } from 'src/app/metamodel/models/renderers/download-renderer-config.model';
 import { getHost } from 'src/app/shared/utils';
@@ -28,8 +27,9 @@ export class DownloadRendererComponent {
     @Input() datasetName: string;
     @Input() datasetPublic: boolean;
     @Input() config: DownloadRendererConfig;
+    @Output() startsDownloadingFile: EventEmitter<{url: string, filename: string}> = new EventEmitter();
 
-    constructor(private appConfig: AppConfigService, private http: HttpClient) { }
+    constructor(private appConfig: AppConfigService) { }
 
     /**
      * Returns link href.
@@ -50,21 +50,14 @@ export class DownloadRendererComponent {
     }
 
     /**
-     * Downloads file on click.
+     * Starts downloading file on click.
      */
     click(event): void {
         event.preventDefault();
+        
+        const url = this.getHref();
+        const filename = url.substring(url.lastIndexOf('/') + 1);
 
-        const href = this.getHref();
-        this.http.get(href, { responseType: "blob" }).subscribe(
-            data => {
-                const filename = href.substring(href.lastIndexOf('/') + 1);
-
-                let downloadLink = document.createElement('a');
-                downloadLink.href = window.URL.createObjectURL(data);
-                downloadLink.setAttribute('download', filename);
-                downloadLink.click();
-            }
-        );
+        this.startsDownloadingFile.emit({ url, filename })
     }
 }
diff --git a/client/src/app/instance/search/containers/result.component.html b/client/src/app/instance/search/containers/result.component.html
index 6ab41d2cd824b5b049c07165a48da4f90bdd5428..7775c355e9de9b612db54af4ed69045e9e48bdfc 100644
--- a/client/src/app/instance/search/containers/result.component.html
+++ b/client/src/app/instance/search/containers/result.component.html
@@ -25,6 +25,10 @@
                     selected with <span class="font-weight-bold">{{ dataLength | async }}</span> objects found.
                 </div>
             </div>
+            <app-download-file-tab
+                *ngIf="(downloadedFiles | async).length > 0"
+                [downloadedFiles]="downloadedFiles | async">
+            </app-download-file-tab>
             <app-download
                 [datasetSelected]="datasetSelected | async"
                 [datasetList]="datasetList | async"
@@ -33,7 +37,8 @@
                 [coneSearch]="coneSearch | async"
                 [dataLength]="dataLength | async"
                 [sampRegistered]="sampRegistered | async"
-                (broadcast)="broadcastVotable($event)">
+                (broadcast)="broadcastVotable($event)"
+                (startsDownloadingFile)="startsDownloadingFile($event)">
             </app-download>
             <app-reminder
                 [datasetSelected]="datasetSelected | async"
@@ -77,7 +82,8 @@
                 (retrieveData)="retrieveData($event)"
                 (addSelectedData)="addSearchData($event)"
                 (deleteSelectedData)="deleteSearchData($event)"
-                (broadcast)="broadcastVotable($event)">
+                (broadcast)="broadcastVotable($event)"
+                (startsDownloadingFile)="startsDownloadingFile($event)">
             </app-datatable-tab>
         </ng-container>
     </div>
diff --git a/client/src/app/instance/search/containers/result.component.ts b/client/src/app/instance/search/containers/result.component.ts
index 599e5cb56a342dc18cb8fae409fc70da7e8a2831..90fc23194beed185c86b27bbd7d51eb3515f4276 100644
--- a/client/src/app/instance/search/containers/result.component.ts
+++ b/client/src/app/instance/search/containers/result.component.ts
@@ -13,13 +13,15 @@ import { Store } from '@ngrx/store';
 import { Observable, Subscription } from 'rxjs';
 
 import { AbstractSearchComponent } from './abstract-search.component';
-import { Pagination } from '../../store/models';
+import { Pagination, DownloadFile } from '../../store/models';
 import { Instance } from 'src/app/metamodel/models';
 import * as instanceSelector from 'src/app/metamodel/selectors/instance.selector';
 import * as searchActions from '../../store/actions/search.actions';
 import * as searchSelector from '../../store/selectors/search.selector';
 import * as sampActions from '../../store/actions/samp.actions';
 import * as sampSelector from '../../store/selectors/samp.selector';
+import * as downloadFileActions from '../../store/actions/download-file.actions';
+import * as downloadFileSelector from '../../store/selectors/download-file.selector';
 
 /**
  * @class
@@ -42,6 +44,7 @@ export class ResultComponent extends AbstractSearchComponent {
     public dataIsLoaded: Observable<boolean>;
     public selectedData: Observable<any>;
     public sampRegistered: Observable<boolean>;
+    public downloadedFiles: Observable<DownloadFile[]>;
     public pristineSubscription: Subscription;
 
     constructor(protected store: Store<{ }>) {
@@ -55,6 +58,7 @@ export class ResultComponent extends AbstractSearchComponent {
         this.dataIsLoaded = this.store.select(searchSelector.selectDataIsLoaded);
         this.selectedData = this.store.select(searchSelector.selectSelectedData);
         this.sampRegistered = this.store.select(sampSelector.selectRegistered);
+        this.downloadedFiles = this.store.select(downloadFileSelector.selectDownloadedFiles);
     }
 
     ngOnInit(): void {
@@ -120,6 +124,15 @@ export class ResultComponent extends AbstractSearchComponent {
         this.store.dispatch(searchActions.deleteSelectedData({ id }));
     }
 
+    /**
+     * Dispatches action to starts downloading file.
+     *
+     * @param  {url: string, filename: string} download - Info about file to download
+     */
+    startsDownloadingFile(download: {url: string, filename: string}): void {
+        this.store.dispatch(downloadFileActions.startsDownloadingFile(download));
+    }
+
     /**
      * Dispatches action to destroy search results.
      */
diff --git a/client/src/app/instance/store/actions/download-file.actions.ts b/client/src/app/instance/store/actions/download-file.actions.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f5ff418da3290c6dd1108f964d8b6d28bea38b02
--- /dev/null
+++ b/client/src/app/instance/store/actions/download-file.actions.ts
@@ -0,0 +1,14 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+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');
diff --git a/client/src/app/instance/store/effects/detail.effects.ts b/client/src/app/instance/store/effects/detail.effects.ts
index 941f318d5ab5563666b0bbbc78334f47de607d15..b1f2aa5f2bfec3d2c09c3aa7055ee309655cafff 100644
--- a/client/src/app/instance/store/effects/detail.effects.ts
+++ b/client/src/app/instance/store/effects/detail.effects.ts
@@ -10,7 +10,7 @@
 import { Injectable } from '@angular/core';
 
 import { Actions, createEffect, ofType, concatLatestFrom } from '@ngrx/effects';
-import { Store  } from '@ngrx/store';
+import { Store } from '@ngrx/store';
 import { of } from 'rxjs';
 import { map, tap, mergeMap, catchError } from 'rxjs/operators';
 import { ToastrService } from 'ngx-toastr';
diff --git a/client/src/app/instance/store/effects/download-file.effects.ts b/client/src/app/instance/store/effects/download-file.effects.ts
new file mode 100644
index 0000000000000000000000000000000000000000..21091c776b82fc7c4b0af03fa8559ffb4970ae7c
--- /dev/null
+++ b/client/src/app/instance/store/effects/download-file.effects.ts
@@ -0,0 +1,59 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { Injectable } from '@angular/core';
+import { HttpEventType } from '@angular/common/http';
+
+import { Actions, createEffect, ofType, concatLatestFrom } 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 { DownloadFileService } from '../services/download-file.service';
+import * as downloadFileActions from '../actions/download-file.actions';
+
+/**
+ * @class
+ * @classdesc File effects.
+ */
+@Injectable()
+export class DownloadFileEffects {
+    /**
+     * Calls actions to retrieve object.
+     */
+    startsDownloadingFile$ = createEffect(() =>
+        this.actions$.pipe(
+            ofType(downloadFileActions.startsDownloadingFile),
+            mergeMap((action) => this.downloadFileService.startsDownloadingFile(action.url)
+                .pipe(
+                    map(event => {
+                        if (event.type === HttpEventType.DownloadProgress) {
+                            this.store.dispatch(downloadFileActions.updateDownloadProgress({ 
+                                progress: Math.round((100 * event.loaded) / event.total),
+                                filename: action.filename
+                            }));
+                        }
+
+                        if (event.type === HttpEventType.Response) {
+                            this.downloadFileService.saveDownloadedFile(event.body as Blob, action.filename);
+                        }
+                    })
+                )
+            )
+        ), { dispatch: false }
+    );
+
+    constructor(
+        private actions$: Actions,
+        private downloadFileService: DownloadFileService,
+        private store: Store<{ }>,
+        private toastr: ToastrService
+    ) {}
+}
diff --git a/client/src/app/instance/store/effects/index.ts b/client/src/app/instance/store/effects/index.ts
index 69b9a3b26cc0cd09cc16e974cbb4ddc4102571b9..18cb99de4d7126d13194355146ddacf39b3c8ae5 100644
--- a/client/src/app/instance/store/effects/index.ts
+++ b/client/src/app/instance/store/effects/index.ts
@@ -4,6 +4,7 @@ import { SearchMultipleEffects } from './search-multiple.effects';
 import { ConeSearchEffects } from './cone-search.effects';
 import { DetailEffects } from './detail.effects';
 import { SvomJsonKwEffects } from './svom-json-kw.effects';
+import { DownloadFileEffects } from './download-file.effects';
 
 export const instanceEffects = [
     SampEffects,
@@ -11,5 +12,6 @@ export const instanceEffects = [
     SearchMultipleEffects,
     ConeSearchEffects,
     DetailEffects,
-    SvomJsonKwEffects
+    SvomJsonKwEffects,
+    DownloadFileEffects
 ];
diff --git a/client/src/app/instance/store/models/download-file.model.ts b/client/src/app/instance/store/models/download-file.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d4d08f454cd6832a508d932f152294a0cd050a6e
--- /dev/null
+++ b/client/src/app/instance/store/models/download-file.model.ts
@@ -0,0 +1,14 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+export interface DownloadFile {
+    name: string,
+    state: 'PENDING' | 'IN_PROGRESS' | 'DONE'
+    progress: number
+}
diff --git a/client/src/app/instance/store/models/index.ts b/client/src/app/instance/store/models/index.ts
index 2570f1630cacd2de9afd9bda713f92230d6b8f22..3b2241bcfbdd3629726f8766f5220e0b74b9ccf1 100644
--- a/client/src/app/instance/store/models/index.ts
+++ b/client/src/app/instance/store/models/index.ts
@@ -7,4 +7,5 @@ export * from './cone-search.model';
 export * from './resolver.model';
 export * from './search-multiple-dataset-length';
 export * from './search-multiple-dataset-data';
-export * from './svom-keyword.model';
\ No newline at end of file
+export * from './svom-keyword.model';
+export * from './download-file.model';
diff --git a/client/src/app/instance/store/reducers/download-file.reducer.ts b/client/src/app/instance/store/reducers/download-file.reducer.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c0021b4683c468798cd401a0b49314989448e1fc
--- /dev/null
+++ b/client/src/app/instance/store/reducers/download-file.reducer.ts
@@ -0,0 +1,51 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { createReducer, on } from '@ngrx/store';
+
+import { DownloadFile } from '../models';
+import * as downloadFileActions from '../actions/download-file.actions';
+
+/**
+ * Interface for file state.
+ *
+ * @interface State
+ */
+export interface State {
+    downloadedFiles: DownloadFile[]
+}
+
+export const initialState: State = {
+    downloadedFiles: []
+};
+
+export const fileReducer = createReducer(
+    initialState,
+    on(downloadFileActions.startsDownloadingFile, (state, { filename }) => ({
+        ...state,
+        downloadedFiles: [...state.downloadedFiles, {
+            name: filename,
+            state: 'PENDING',
+            progress: 0
+        }]
+    })),
+    on(downloadFileActions.updateDownloadProgress, (state, { progress, filename }) => ({
+        ...state,
+        downloadedFiles: [
+            ...state.downloadedFiles.filter(f => f.name !== filename),
+            {
+                name: filename,
+                state: 'IN_PROGRESS',
+                progress: progress
+            }
+        ]
+    }))
+);
+
+export const selectDownloadedFiles = (state: State) => state.downloadedFiles;
\ No newline at end of file
diff --git a/client/src/app/instance/store/selectors/download-file.selector.ts b/client/src/app/instance/store/selectors/download-file.selector.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b955521d86de25bbf5c6eef631a4b77e4f2bd982
--- /dev/null
+++ b/client/src/app/instance/store/selectors/download-file.selector.ts
@@ -0,0 +1,23 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { createSelector } from '@ngrx/store';
+
+import * as reducer from '../../instance.reducer';
+import * as fromDownloadFile from '../reducers/download-file.reducer';
+
+export const selectDownloadFileState = createSelector(
+    reducer.getInstanceState,
+    (state: reducer.State) => state.downloadFile
+);
+
+export const selectDownloadedFiles = createSelector(
+    selectDownloadFileState,
+    fromDownloadFile.selectDownloadedFiles
+);
diff --git a/client/src/app/instance/store/services/download-file.service.ts b/client/src/app/instance/store/services/download-file.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..663e052129cd09d9cc46d0ae09ea4f19fdf2ae89
--- /dev/null
+++ b/client/src/app/instance/store/services/download-file.service.ts
@@ -0,0 +1,34 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { Injectable } from '@angular/core';
+import { HttpClient, HttpRequest } from '@angular/common/http';
+
+@Injectable({providedIn: 'root'})
+export class DownloadFileService {
+    constructor(private http: HttpClient) { }
+
+    startsDownloadingFile(url: string) {
+        const request = new HttpRequest(
+            "GET",
+            url,
+            {},
+            { reportProgress: true, responseType: 'blob' }
+        );
+
+        return this.http.request(request);
+    }
+
+    saveDownloadedFile(body: Blob, filename: string) {
+        let downloadLink = document.createElement('a');
+        downloadLink.href = window.URL.createObjectURL(body);
+        downloadLink.setAttribute('download', filename);
+        downloadLink.click();
+    }
+}
\ No newline at end of file
diff --git a/client/src/app/instance/store/services/index.ts b/client/src/app/instance/store/services/index.ts
index 795315e994b19b011ba88241a4428380e6932c09..d70ddbf82870dc1059472f410111eb4979b00052 100644
--- a/client/src/app/instance/store/services/index.ts
+++ b/client/src/app/instance/store/services/index.ts
@@ -3,11 +3,13 @@ import { SampService } from './samp.service';
 import { ConeSearchService } from './cone-search.service';
 import { DetailService } from './detail.service';
 import { SvomJsonKwService } from './svom-json-kw.service';
+import { DownloadFileService } from './download-file.service';
 
 export const instanceServices = [
     SearchService,
     SampService,
     ConeSearchService,
     DetailService,
-    SvomJsonKwService
+    SvomJsonKwService,
+    DownloadFileService
 ];
diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts
index bfb8594e449030fea934eab9a1143c8ead325adb..5455dacaf03013492277fba8166c698db0219f76 100644
--- a/client/src/app/shared/shared.module.ts
+++ b/client/src/app/shared/shared.module.ts
@@ -20,6 +20,7 @@ import { TooltipModule } from 'ngx-bootstrap/tooltip';
 import { BsDatepickerModule } from 'ngx-bootstrap/datepicker';
 import { TabsModule } from 'ngx-bootstrap/tabs';
 import { PaginationModule } from 'ngx-bootstrap/pagination';
+import { ProgressbarModule } from 'ngx-bootstrap/progressbar';
 import { NgSelectModule } from '@ng-select/ng-select';
 import { NgxJsonViewerModule } from 'ngx-json-viewer';
 
@@ -48,6 +49,7 @@ import { sharedPipes } from './pipes';
         BsDatepickerModule.forRoot(),
         TabsModule.forRoot(),
         PaginationModule.forRoot(),
+        ProgressbarModule.forRoot(),
         NgSelectModule,
         NgxJsonViewerModule
     ],
@@ -63,6 +65,7 @@ import { sharedPipes } from './pipes';
         BsDatepickerModule,
         TabsModule,
         PaginationModule,
+        ProgressbarModule,
         NgSelectModule,
         NgxJsonViewerModule,
         sharedComponents,