diff --git a/client/package.json b/client/package.json
index 4acaf2113c48ceee5372e722c4e9d34fc07d5c63..c085d335281027eb0572f85cac2166d3d66ae3e1 100644
--- a/client/package.json
+++ b/client/package.json
@@ -26,6 +26,7 @@
     "@ngrx/store-devtools": "13.0.2",
     "bootstrap": "4.6.1",
     "d3": "^5.15.1",
+    "file-saver": "^2.0.5",
     "keycloak-angular": "^9.1.0",
     "keycloak-js": "^16.1.1",
     "ngx-bootstrap": "^8.0.0",
@@ -40,6 +41,7 @@
     "@angular/cli": "~13.2.3",
     "@angular/compiler-cli": "~13.2.2",
     "@types/d3": "^5.7.2",
+    "@types/file-saver": "^2.0.5",
     "@types/jasmine": "~3.10.0",
     "@types/jest": "^27.4.0",
     "@types/node": "^12.11.1",
@@ -49,4 +51,4 @@
     "jest-preset-angular": "^11.1.0",
     "typescript": "~4.5.5"
   }
-}
\ No newline at end of file
+}
diff --git a/client/src/app/instance/instance.reducer.ts b/client/src/app/instance/instance.reducer.ts
index 3be0935a92e04797c5cd669a3721253f0bf81a81..be6b9a59f52e9af870a494c6ba7c55bf944263ac 100644
--- a/client/src/app/instance/instance.reducer.ts
+++ b/client/src/app/instance/instance.reducer.ts
@@ -15,7 +15,7 @@ import * as searchMultiple from './store/reducers/search-multiple.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';
+import * as archive from './store/reducers/archive.reducer';
 
 /**
  * Interface for instance state.
@@ -28,7 +28,7 @@ export interface State {
     coneSearch: coneSearch.State
     detail: detail.State,
     svomJsonKw: svomJsonKw.State,
-    downloadFile: downloadFile.State
+    archive: archive.State
 }
 
 const reducers = {
@@ -37,7 +37,7 @@ const reducers = {
     coneSearch: coneSearch.coneSearchReducer,
     detail: detail.detailReducer,
     svomJsonKw: svomJsonKw.svomJsonKwReducer,
-    downloadFile: downloadFile.fileReducer
+    archive: archive.archiveReducer
 };
 
 export const instanceReducer = combineReducers(reducers);
diff --git a/client/src/app/instance/search/components/result/abstract-download.component.ts b/client/src/app/instance/search/components/result/abstract-download.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..16e1f4dd6b397660c9319b6ae603f88e3f46af37
--- /dev/null
+++ b/client/src/app/instance/search/components/result/abstract-download.component.ts
@@ -0,0 +1,80 @@
+import { Directive, Input, Output, EventEmitter } from '@angular/core';
+
+import { Dataset } from 'src/app/metamodel/models';
+import { Criterion, ConeSearch, criterionToString } from 'src/app/instance/store/models';
+import { AppConfigService } from 'src/app/app-config.service';
+import { getHost } from 'src/app/shared/utils';
+
+@Directive()
+export abstract class AbstractDownloadComponent {
+    @Input() dataset: Dataset;
+    @Input() criteriaList: Criterion[];
+    @Input() outputList: number[];
+    @Input() coneSearch: ConeSearch;
+    @Input() archiveIsCreating: boolean;
+    @Output() downloadFile: EventEmitter<{url: string, filename: string}> = new EventEmitter();
+
+    constructor(private appConfig: AppConfigService) { }
+
+    /**
+     * Returns API URL to get data with user parameters.
+     *
+     * @return string
+     */
+    getUrl(format: string, selectedData: string = null): string {
+        return `${getHost(this.appConfig.apiUrl)}/search/${this.getQuery(selectedData)}&f=${format}`;
+    }
+
+    getQuery(selectedData: string = null) {
+        let query = `${this.dataset.name}?a=${this.outputList.join(';')}`;
+        if (this.criteriaList.length > 0) {
+            query += `&c=${this.criteriaList.map(criterion => criterionToString(criterion)).join(';')}`;
+            if (selectedData) {
+                query += `;${selectedData}`;
+            }
+        } else if (selectedData) {
+            query += `&c=${selectedData}`;
+        }
+        if (this.coneSearch) {
+            query += `&cs=${this.coneSearch.ra}:${this.coneSearch.dec}:${this.coneSearch.radius}`;
+        }
+        return query;
+    }
+
+    download(event, url: string, format: string) {
+        event.preventDefault();
+
+        const timeElapsed = Date.now();
+        const today = new Date(timeElapsed);
+        const filename = `result_${this.dataset.name}_${today.toISOString()}.${this.formatToExtension(format)}`;
+
+        this.downloadFile.emit({ url, filename });
+    }
+
+    formatToExtension(format: string) {
+        let extension: string;
+        switch (format) {
+            case 'json': { 
+                extension = 'json';
+                break; 
+            }
+            case 'csv': { 
+                extension = 'csv';
+                break; 
+            }
+            case 'ascii': { 
+                extension = 'txt';
+                break; 
+            }
+            case 'votable': { 
+                extension = 'xml';
+                break; 
+            }
+            default: { 
+                extension = 'json';
+                break; 
+            }
+        }
+        return extension;
+    }
+}
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/result/cone-search-plot.component.html b/client/src/app/instance/search/components/result/cone-search-plot.component.html
index 75192165db8f2c2ca8b91d8e614319569acdbdea..0d97ee1d4ae5dd8fbddf3e4d3334a991d6df0993 100644
--- a/client/src/app/instance/search/components/result/cone-search-plot.component.html
+++ b/client/src/app/instance/search/components/result/cone-search-plot.component.html
@@ -1 +1 @@
-<div id="plot" class="row bg-light"></div>
\ No newline at end of file
+<div id="plot" class="row justify-content-center"></div>
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/result/cone-search-plot.component.ts b/client/src/app/instance/search/components/result/cone-search-plot.component.ts
index e57aa634c5bca4bb4d55476c965320d837ca57c7..f2990528eb1e8ab673336c9ee704c66aa1d9cbb1 100644
--- a/client/src/app/instance/search/components/result/cone-search-plot.component.ts
+++ b/client/src/app/instance/search/components/result/cone-search-plot.component.ts
@@ -7,13 +7,12 @@
  * file that was distributed with this source code.
  */
 
-import { Component, Input, ChangeDetectionStrategy, ViewEncapsulation, OnInit, SimpleChanges } from '@angular/core';
+import { Component, Input, ChangeDetectionStrategy, ViewEncapsulation, OnInit } from '@angular/core';
 
 import * as d3 from 'd3';
 
-import { Dataset, Image } from 'src/app/metamodel/models';
+import { Dataset } from 'src/app/metamodel/models';
 import { ConeSearch } from 'src/app/instance/store/models';
-import { AppConfigService } from 'src/app/app-config.service';
 
 /**
  * @class
@@ -30,7 +29,7 @@ export class ConeSearchPlotComponent implements OnInit {
     @Input() coneSearch: ConeSearch;
     @Input() dataset: Dataset;
     @Input() data: {x: number, y: number}[];
-    @Input() selectedBackground: Image;
+    @Input() backgroundHref: string;
 
     // Interactive variables intialisation
     margin = { top: 50, right: 50, bottom: 50 , left: 50 };
@@ -41,42 +40,10 @@ export class ConeSearchPlotComponent implements OnInit {
     x: d3.ScaleLinear<number, number>;
     y: d3.ScaleLinear<number, number>;
 
-    constructor(private config: AppConfigService) { }
-
     ngOnInit(): void {
         this.coneSearchPlot();
     }
 
-    ngOnChanges(changes: SimpleChanges) {
-        if (changes.selectedBackground && changes.selectedBackground.currentValue) {
-            console.log('coucou');
-            this.image.attr('xlink:href', this.getHrefBackgroundImage());
-        }
-    }
-
-
-    getHrefBackgroundImage() {
-        if (this.selectedBackground) {
-            let href = `${this.config.servicesUrl}/fits-cut-to-png/${this.dataset.name}?filename=${this.selectedBackground.file_path}`;
-            href += `&ra=${this.coneSearch.ra}`;
-            href += `&dec=${this.coneSearch.dec}`;
-            href += `&radius=${this.coneSearch.radius}`;
-            href += `&stretch=${this.selectedBackground.stretch}`;
-            href += `&pmin=${this.selectedBackground.pmin}`;
-            href += `&pmax=${this.selectedBackground.pmax}`;
-            href += `&axes=false`;
-            return href;
-        } else {
-            const scale = this.coneSearch.radius / this.width;     // arcsec/pix
-            return `https://skyserver.sdss.org/dr16/SkyServerWS/ImgCutout/getjpeg?TaskName=Skyserver.Chart.Image
-                &ra=${this.coneSearch.ra}
-                &dec=${this.coneSearch.dec}
-                &scale=${scale}
-                &width=${this.width}
-                &height=${this.height}`;
-        }
-    }
-
     coneSearchPlot(): void {
         // Init SVG
         const svg = d3.select('#plot').append('svg')
@@ -93,7 +60,7 @@ export class ConeSearchPlotComponent implements OnInit {
 
         // Background image
         this.image = svg.append('image');
-        this.image.attr('xlink:href', this.getHrefBackgroundImage())
+        this.image.attr('xlink:href', this.backgroundHref)
             .attr('width', this.width)
             .attr('height', this.height);
 
diff --git a/client/src/app/instance/search/components/result/datatable-actions.component.html b/client/src/app/instance/search/components/result/datatable-actions.component.html
index d20f9c16448339958b173ae5a7b146b50728a98c..3889169ed51cc03145b4e17198f16706b4227be2 100644
--- a/client/src/app/instance/search/components/result/datatable-actions.component.html
+++ b/client/src/app/instance/search/components/result/datatable-actions.component.html
@@ -1,30 +1,35 @@
-<div *ngIf="getDataset().datatable_selectable_rows" class="btn-group mb-2" dropdown [isDisabled]="selectedData.length < 1">
+<div *ngIf="dataset.datatable_selectable_rows" class="btn-group mb-2" dropdown [isDisabled]="selectedData.length < 1">
     <button id="button-basic" dropdownToggle type="button" class="btn btn-primary dropdown-toggle" aria-controls="dropdown-basic">
         Actions <span class="caret"></span>
     </button>
     <ul id="dropdown-basic" *dropdownMenu class="dropdown-menu" role="menu" aria-labelledby="button-basic">
-        <li *ngIf="getConfigDownloadResultFormat('download_csv')" role="menuitem">
-            <a class="dropdown-item" (click)="downloadResult('csv')">
+        <li *ngIf="dataset.download_json" role="menuitem">
+            <a class="dropdown-item" [href]="getDatatableUrl('json')" (click)="download($event, getDatatableUrl('json'), 'json')">
+                <span class="fas fa-file"></span> Download JSON
+            </a>
+        </li>
+        <li *ngIf="dataset.download_csv" role="menuitem">
+            <a class="dropdown-item" [href]="getDatatableUrl('csv')" (click)="download($event, getDatatableUrl('csv'), 'csv')">
                 <span class="fas fa-file-csv"></span> Download CSV
             </a>
         </li>
-        <li *ngIf="getConfigDownloadResultFormat('download_ascii')" role="menuitem">
-            <a class="dropdown-item" (click)="downloadResult('ascii')">
+        <li *ngIf="dataset.download_ascii" role="menuitem">
+            <a class="dropdown-item" [href]="getDatatableUrl('ascii')" (click)="download($event, getDatatableUrl('ascii'), 'ascii')">
                 <span class="fas fa-file"></span> Download ASCII
             </a>
         </li>
-        <li *ngIf="getConfigDownloadResultFormat('download_vo')" role="menuitem">
-            <a class="dropdown-item" (click)="downloadResult('votable')">
+        <li *ngIf="dataset.download_vo" role="menuitem">
+            <a class="dropdown-item" [href]="getDatatableUrl('votable')" (click)="download($event, getDatatableUrl('votable'), 'votable')">
                 <span class="fas fa-file"></span> VOtable
             </a>
         </li>
-        <li *ngIf="getConfigDownloadResultFormat('download_vo')" role="menuitem" [class.disabled]="!sampRegistered">
+        <li *ngIf="dataset.download_vo" role="menuitem" [class.disabled]="!sampRegistered">
             <a class="dropdown-item" [class.disabled]="!sampRegistered" (click)="broadcastResult()">
                 <span class="fas fa-broadcast-tower"></span> Broadcast VOtable
             </a>
         </li>
-        <li *ngIf="getConfigDownloadResultFormat('download_archive')" role="menuitem">
-            <a class="dropdown-item" (click)="downloadArchive()">
+        <li *ngIf="isArchiveIsAvailable()" role="menuitem" [class.disabled]="archiveIsCreating">
+            <a class="dropdown-item" [class.disabled]="archiveIsCreating" (click)="downloadArchive()">
                 <span class="fas fa-archive"></span> Download files archive
             </a>
         </li>
diff --git a/client/src/app/instance/search/components/result/datatable-actions.component.ts b/client/src/app/instance/search/components/result/datatable-actions.component.ts
index ac49911e931c23f40ee960e412eb20e8cde68e43..5d13989cfebdefa05ac1a4a9d99abee5842ec21a 100644
--- a/client/src/app/instance/search/components/result/datatable-actions.component.ts
+++ b/client/src/app/instance/search/components/result/datatable-actions.component.ts
@@ -1,53 +1,38 @@
 import { Component, Input, Output, EventEmitter } from '@angular/core';
 
-import { Dataset } from 'src/app/metamodel/models';
+import { AbstractDownloadComponent } from './abstract-download.component';
+import { Attribute } from 'src/app/metamodel/models';
 
 @Component({
     selector: 'app-datatable-actions',
     templateUrl: 'datatable-actions.component.html'
 })
-export class DatatableActionsComponent {
+export class DatatableActionsComponent extends AbstractDownloadComponent {
+    @Input() attributeList: Attribute[];
     @Input() selectedData: any[] = [];
-    @Input() datasetSelected: string;
-    @Input() datasetList: Dataset[];
     @Input() sampRegistered: boolean;
-    @Output() startTaskCreateResult: EventEmitter<{ format: string, selectedData: boolean, broadcastVo: boolean }> = new EventEmitter();
-    @Output() startTaskCreateArchive: EventEmitter<{ selectedData: boolean }> = new EventEmitter();
+    @Output() broadcastVotable: EventEmitter<string> = new EventEmitter();
+    @Output() startTaskCreateArchive: EventEmitter<string> = new EventEmitter();
 
-    /**
-     * Checks if the download format is allowed by Anis Admin configuration.
-     *
-     * @param  {string} format - The file format to download.
-     *
-     * @return boolean
-     */
-    getConfigDownloadResultFormat(format: string): boolean {
-        return this.getDataset()[format];
+    isArchiveIsAvailable() {
+        return this.attributeList
+            .filter(attribute => this.outputList.includes(attribute.id))
+            .filter(attribute => attribute.archive)
+            .length > 0;
     }
 
-    getDataset() {
-        return this.datasetList.find(d => d.name === this.datasetSelected);
+    getDatatableUrl(format: string): string {
+        const attributeId = this.attributeList.find(a => a.primary_key);
+        return this.getUrl(format, `${attributeId.id}::in::${this.selectedData.join('|')}`);
     }
-
-    downloadResult(format: string) {
-        this.startTaskCreateResult.emit({
-            format,
-            selectedData: true,
-            broadcastVo: false
-        });
-    }
-
+    
     broadcastResult() {
-        this.startTaskCreateResult.emit({
-            format: 'votable',
-            selectedData: true,
-            broadcastVo: true
-        })
+        const url = this.getDatatableUrl('votable');
+        this.broadcastVotable.emit(url);
     }
 
     downloadArchive() {
-        this.startTaskCreateArchive.emit({
-            selectedData: true
-        });
+        const attributeId = this.attributeList.find(a => a.primary_key);
+        this.startTaskCreateArchive.emit(this.getQuery(`${attributeId.id}::in::${this.selectedData.join('|')}`));
     }
 }
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
deleted file mode 100644
index d35da3bb90fac0e8f942488940b1827a7135561c..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/result/datatable-tab.component.html
+++ /dev/null
@@ -1,59 +0,0 @@
-<accordion *ngIf="(datasetList | datasetByName:datasetSelected).datatable_enabled" [isAnimated]="true">
-    <accordion-group #ag [isOpen]="(datasetList | datasetByName:datasetSelected).datatable_opened" [panelClass]="'custom-accordion'" class="my-2">
-        <button class="btn btn-link btn-block clearfix" accordion-heading>
-            <span class="pull-left float-left">
-                Display result details
-                &nbsp;
-                <span *ngIf="ag.isOpen"><span class="fas fa-chevron-up"></span></span>
-                <span *ngIf="!ag.isOpen"><span class="fas fa-chevron-down"></span></span>
-            </span>
-        </button>
-        <div class="row">
-            <div class="col-md-5" *ngIf="coneSearch">
-                <div class="form-group">
-                    <label for="file_size">Background image</label>
-                    <ng-select [(ngModel)]="selectedBackground">
-                        <ng-option *ngFor="let image of imageList" [value]="image">{{ image.file_path }}</ng-option>
-                     </ng-select>
-                </div>
-                <app-cone-search-plot *ngIf="dataIsLoaded"
-                    [coneSearch]="coneSearch"
-                    [dataset]="datasetList | datasetByName:datasetSelected"
-                    [data]="getData()"
-                    [selectedBackground]="selectedBackground">
-                </app-cone-search-plot>
-            </div>
-            <div class="datatable-group" [ngClass]="{'col': !coneSearch, 'col-md-7' : coneSearch }">
-                <div class="row">
-                    <div class="col">
-                        <app-datatable-actions 
-                            [selectedData]="selectedData"
-                            [datasetSelected]="datasetSelected"
-                            [datasetList]="datasetList"
-                            [sampRegistered]="sampRegistered"
-                            (broadcast)="broadcast.emit($event)"
-                            (startTaskCreateResult)="startTaskCreateResult.emit($event)"
-                            (startTaskCreateArchive)="startTaskCreateArchive.emit($event)">
-                        </app-datatable-actions>
-                        <app-datatable
-                            [dataset]="datasetList | datasetByName:datasetSelected"
-                            [instance]="instance"
-                            [attributeList]="attributeList"
-                            [outputList]="outputList"
-                            [queryParams]="queryParams"
-                            [dataLength]="dataLength"
-                            [data]="data"
-                            [dataIsLoading]="dataIsLoading"
-                            [dataIsLoaded]="dataIsLoaded"
-                            [selectedData]="selectedData"
-                            (retrieveData)="retrieveData.emit($event)"
-                            (addSelectedData)="addSelectedData.emit($event)"
-                            (deleteSelectedData)="deleteSelectedData.emit($event)"
-                            (downloadFile)="downloadFile.emit($event)">
-                        </app-datatable>
-                    </div>
-                </div>
-            </div>
-        </div>
-    </accordion-group>
-</accordion>
diff --git a/client/src/app/instance/search/components/result/datatable-tab.component.scss b/client/src/app/instance/search/components/result/datatable-tab.component.scss
deleted file mode 100644
index 96286643764a50077a9f90b8a8776616bb1c80af..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/result/datatable-tab.component.scss
+++ /dev/null
@@ -1,8 +0,0 @@
-.datatable-group > .row {
-    overflow-x: auto;
-    white-space: nowrap;
-}
-.datatable-group > .row > .col {
-    display: inline-block;
-    float: none;
-}
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/result/datatable-tab.component.spec.ts b/client/src/app/instance/search/components/result/datatable-tab.component.spec.ts
deleted file mode 100644
index 3d261b62cf37b9689007c61b7de1a748793b5651..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/result/datatable-tab.component.spec.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-import { Component, Input } from '@angular/core';
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
-
-import { AccordionModule } from 'ngx-bootstrap/accordion';
-
-import { DatatableTabComponent } from './datatable-tab.component';
-import { Attribute, Dataset, Instance } from '../../../../metamodel/models';
-import { SearchQueryParams, Criterion, ConeSearch } from '../../../store/models';
-import { DatasetByNamePipe } from '../../../../shared/pipes/dataset-by-name.pipe';
-
-describe('[Instance][Search][Component][Result] DatatableTabComponent', () => {
-    @Component({ selector: 'app-datatable', template: '' })
-    class DatatableStubComponent {
-        @Input() dataset: Dataset;
-        @Input() instance: Instance;
-        @Input() attributeList: Attribute[];
-        @Input() outputList: number[];
-        @Input() queryParams: SearchQueryParams;
-        @Input() dataLength: number;
-        @Input() data: any[];
-        @Input() dataIsLoading: boolean;
-        @Input() dataIsLoaded: boolean;
-        @Input() selectedData: any[] = [];
-    }
-
-    @Component({ selector: 'app-datatable-actions', template: '' })
-    class DatatableActionsStubComponent {
-        @Input() selectedData: any[] = [];
-        @Input() datasetSelected: string;
-        @Input() datasetList: Dataset[];
-        @Input() attributeList: Attribute[];
-        @Input() criteriaList: Criterion[];
-        @Input() outputList: number[];
-        @Input() coneSearch: ConeSearch;
-        @Input() dataLength: number;
-        @Input() sampRegistered: boolean;
-    }
-
-    let component: DatatableTabComponent;
-    let fixture: ComponentFixture<DatatableTabComponent>;
-
-    beforeEach(() => {
-        TestBed.configureTestingModule({
-            declarations: [
-                DatatableTabComponent,
-                DatatableStubComponent,
-                DatatableActionsStubComponent,
-                DatasetByNamePipe
-            ],
-            imports: [
-                AccordionModule.forRoot(),
-                BrowserAnimationsModule
-            ]
-        });
-        fixture = TestBed.createComponent(DatatableTabComponent);
-        component = fixture.componentInstance;
-    });
-
-    it('should create the component', () => {
-        expect(component).toBeTruthy();
-    });
-});
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
deleted file mode 100644
index 239dde5745afd6c0239ff2185b6686ce28c5abd1..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/result/datatable-tab.component.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-/**
- * 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 { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
-
-import { Instance, Attribute, Dataset, Image } from 'src/app/metamodel/models';
-import { Pagination, SearchQueryParams, Criterion, ConeSearch } from 'src/app/instance/store/models';
-
-/**
- * @class
- * @classdesc Search result datatable tab component.
- */
-@Component({
-    selector: 'app-datatable-tab',
-    templateUrl: 'datatable-tab.component.html',
-    styleUrls: [ 'datatable-tab.component.scss' ],
-    changeDetection: ChangeDetectionStrategy.OnPush
-})
-export class DatatableTabComponent {
-    @Input() datasetSelected: string;
-    @Input() instance: Instance;
-    @Input() datasetList: Dataset[];
-    @Input() attributeList: Attribute[];
-    @Input() outputList: number[];
-    @Input() criteriaList: Criterion[];
-    @Input() coneSearch: ConeSearch;
-    @Input() queryParams: SearchQueryParams;
-    @Input() dataLength: number;
-    @Input() sampRegistered: boolean;
-    @Input() data: any[];
-    @Input() dataIsLoading: boolean;
-    @Input() dataIsLoaded: boolean;
-    @Input() selectedData: any[];
-    @Input() imageList: Image[];
-    @Input() imageListIsLoading: boolean;
-    @Input() imageListIsLoaded: boolean;
-    @Output() retrieveData: EventEmitter<Pagination> = new EventEmitter();
-    @Output() addSelectedData: EventEmitter<number | string> = new EventEmitter();
-    @Output() deleteSelectedData: EventEmitter<number | string> = new EventEmitter();
-    @Output() broadcast: EventEmitter<string> = new EventEmitter();
-    @Output() startTaskCreateResult: EventEmitter<{ format: string, selectedData: boolean, broadcastVo: boolean }> = new EventEmitter();
-    @Output() startTaskCreateArchive: EventEmitter<{ selectedData: boolean }> = new EventEmitter();
-    @Output() downloadFile: EventEmitter<{url: string, fileId: string, datasetName: string, filename: string}> = new EventEmitter();
-
-    selectedBackground: Image = null;
-
-    getData() {
-        const dataset = this.getDataset();
-        const columnRa = this.attributeList.find(a => a.id === dataset.cone_search_column_ra);
-        const columnDec = this.attributeList.find(a => a.id === dataset.cone_search_column_dec);
-        return this.data.map(d => ({ "x": +d[columnRa.label], "y": +d[columnDec.label] }));
-    }
-
-    /**
-     * Returns selected dataset for the search.
-     *
-     * @return Dataset
-     */
-     getDataset(): Dataset {
-        return this.datasetList.find(dataset => dataset.name === this.datasetSelected);
-    }
-}
diff --git a/client/src/app/instance/search/components/result/datatable.component.scss b/client/src/app/instance/search/components/result/datatable.component.scss
index 4d4085831e280f22c13f1d70d5e164691ffca869..d40e1d1dac814cf5d102188e107a1358420f18ff 100644
--- a/client/src/app/instance/search/components/result/datatable.component.scss
+++ b/client/src/app/instance/search/components/result/datatable.component.scss
@@ -7,11 +7,6 @@
  * file that was distributed with this source code.
  */
 
-.table-responsive {
-    overflow-y: scroll;
-    height: 650px;
-}
-
 table th:not(.select) {
     min-width: 130px;
 }
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 739b27df65417b20b3d92c1db34b346375ca719b..95cd763f07618d853931201f0268fa447ae117d9 100644
--- a/client/src/app/instance/search/components/result/datatable.component.ts
+++ b/client/src/app/instance/search/components/result/datatable.component.ts
@@ -44,7 +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() downloadFile: EventEmitter<{url: string, fileId: string, datasetName: string, filename: string}> = new EventEmitter();
+    @Output() downloadFile: 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
deleted file mode 100644
index c4032c0b8fc06a3f80ac8c5b7cbeecf79e22bf57..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/result/download-file-tab.component.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<div class="jumbotron mb-4 py-4">
-    <div class="lead">
-        Files downloaded : 
-        <ul>
-            <li *ngFor="let downloadFile of downloadedFiles">
-                {{ downloadFile.fileName }} :
-                <ng-container *ngIf="downloadFile.state == 'PENDING'">
-                    <br>
-                    <span class="fas fa-circle-notch fa-spin"></span>
-                    <span class="sr-only">Loading...</span>
-                </ng-container>
-                <progressbar [value]="downloadFile.progress" [type]="getType(downloadFile.progress)" [animate]="true">{{ 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
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
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
deleted file mode 100644
index 95d1e5b6389f9ef6b91263070574821c752901d9..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/result/download-file-tab.component.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- * 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[];
-
-    getType(value: number): 'success' | 'info' {
-        if (value < 100) {
-            return 'info';
-        }
-        
-        return 'success';
-    }
-}
diff --git a/client/src/app/instance/search/components/result/download-result.component.html b/client/src/app/instance/search/components/result/download-result.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..f85406687cd5304dddea4e92d8ab04edde4ef045
--- /dev/null
+++ b/client/src/app/instance/search/components/result/download-result.component.html
@@ -0,0 +1,47 @@
+<table>
+    <tr>
+        <th>Download results </th>
+        <td>
+            <a *ngIf="dataset.download_json" [href]="getUrl('json')" (click)="download($event, getUrl('json'), 'json')" class="btn btn-primary" title="Download results in JSON format">
+                <span class="fas fa-file"></span> JSON
+            </a>
+            &nbsp;
+            <a *ngIf="dataset.download_csv" [href]="getUrl('csv')" (click)="download($event, getUrl('csv'), 'csv')" class="btn btn-primary" title="Download results in CSV format">
+                <span class="fas fa-file-csv"></span> CSV
+            </a>
+            &nbsp;
+            <a *ngIf="dataset.download_ascii" [href]="getUrl('ascii')" (click)="download($event, getUrl('ascii'), 'ascii')" class="btn btn-primary" title="Download results in ASCII format">
+                <span class="fas fa-file"></span> ASCII
+            </a>
+            &nbsp;
+            <a *ngIf="dataset.download_vo" [href]="getUrl('votable')" (click)="download($event, getUrl('votable'), 'votable')" class="btn btn-primary" title="Download results in VO format">
+                <span class="fas fa-file"></span> VOtable
+            </a>
+        </td>
+    </tr>
+    <tr *ngIf="isArchiveIsAvailable()">
+        <th>Download files </th>
+        <td>
+            <a [class.disabled]="archiveIsCreating" (click)="downloadArchive()" class="btn btn-primary" title="Download an archive with all files">
+                <span class="fas fa-archive"></span> Files archive
+            </a>
+        </td>
+    </tr>
+    <tr *ngIf="instance.samp_enabled">
+        <th>VO-SAMP </th>
+        <td>
+            <button *ngIf="!sampRegistered" (click)="sampRegister.emit()" class="btn btn-primary">Try to register</button>
+            <button *ngIf="sampRegistered" (click)="sampUnregister.emit()" class="btn btn-danger">SAMP Unregister</button>
+            &nbsp;
+            <button *ngIf="sampRegistered && dataset.download_vo" (click)="broadcastResult()" class="btn btn-primary" title="Broadcast samp votable">
+                <span class="fas fa-broadcast-tower"></span> Broadcast VOtable
+            </button>
+        </td>
+    </tr>
+</table>
+
+<p *ngIf="archiveIsCreating" class="text-center mt-2 text-danger font-weight-bold">
+    Archive is under construction ! Please stay on this page
+    <span class="fas fa-circle-notch fa-spin fa-2x"></span>
+    <span class="sr-only">Loading...</span>
+</p>
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/result/download-result.component.scss b/client/src/app/instance/search/components/result/download-result.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..f27c2de02bf06a9cc68a93abf3b16a80ab25bb0e
--- /dev/null
+++ b/client/src/app/instance/search/components/result/download-result.component.scss
@@ -0,0 +1,8 @@
+th {
+    font-weight: normal;
+    width: 200px;
+}
+
+tr {
+    height: 50px;
+}
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/result/download-result.component.ts b/client/src/app/instance/search/components/result/download-result.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4a25094ca2fba3e58afa7b95eb8d01a98481a874
--- /dev/null
+++ b/client/src/app/instance/search/components/result/download-result.component.ts
@@ -0,0 +1,35 @@
+import { Component, Input, Output, EventEmitter } from '@angular/core';
+
+import { AbstractDownloadComponent } from './abstract-download.component';
+import { Instance, Attribute } from 'src/app/metamodel/models';
+
+@Component({
+    selector: 'app-download-result',
+    templateUrl: 'download-result.component.html',
+    styleUrls: ['download-result.component.scss']
+})
+export class DownloadResultComponent extends AbstractDownloadComponent{
+    @Input() instance: Instance;
+    @Input() attributeList: Attribute[];
+    @Input() sampRegistered: boolean;
+    @Output() sampRegister: EventEmitter<{}> = new EventEmitter();
+    @Output() sampUnregister: EventEmitter<{}> = new EventEmitter();
+    @Output() broadcastVotable: EventEmitter<string> = new EventEmitter();
+    @Output() startTaskCreateArchive: EventEmitter<string> = new EventEmitter();
+
+    isArchiveIsAvailable() {
+        return this.attributeList
+            .filter(attribute => this.outputList.includes(attribute.id))
+            .filter(attribute => attribute.archive)
+            .length > 0;
+    }
+
+    broadcastResult() {
+        const url = this.getUrl('votable');
+        this.broadcastVotable.emit(url);
+    }
+
+    downloadArchive() {
+        this.startTaskCreateArchive.emit(this.getQuery());
+    }
+}
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/result/download.component.html b/client/src/app/instance/search/components/result/download.component.html
deleted file mode 100644
index d4fa1f18feaeef270109a562c23753688759e7de..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/result/download.component.html
+++ /dev/null
@@ -1,55 +0,0 @@
-<accordion *ngIf="isDownloadActivated()" [isAnimated]="true">
-    <accordion-group #ag [isOpen]="isDownloadOpened()" [panelClass]="'custom-accordion'" class="my-2">
-        <button class="btn btn-link btn-block clearfix" accordion-heading>
-            <span class="pull-left float-left">
-                Download results
-                &nbsp;
-                <span *ngIf="ag.isOpen">
-                    <span class="fas fa-chevron-up"></span>
-                </span>
-                <span *ngIf="!ag.isOpen">
-                    <span class="fas fa-chevron-down"></span>
-                </span>
-            </span>
-        </button>
-        <div>
-            <div class="row">
-                <div class="col-auto align-self-center">
-                    <p>Download results just here:</p>
-                </div>
-                <div class="col">
-                    <a *ngIf="getConfigDownloadResultFormat('download_json')" (click)="downloadResult('json')" class="btn btn-outline-primary" title="Download results in JSON format">
-                        <span class="fas fa-file"></span> JSON
-                    </a>
-                    &nbsp;
-                    <a *ngIf="getConfigDownloadResultFormat('download_csv')" (click)="downloadResult('csv')" class="btn btn-outline-primary" title="Download results in CSV format">
-                        <span class="fas fa-file-csv"></span> CSV
-                    </a>
-                    &nbsp;
-                    <a *ngIf="getConfigDownloadResultFormat('download_ascii')" (click)="downloadResult('ascii')" class="btn btn-outline-primary" title="Download results in ASCII format">
-                        <span class="fas fa-file"></span> ASCII
-                    </a>
-                    &nbsp;
-                    <a *ngIf="getConfigDownloadResultFormat('download_vo')" (click)="downloadResult('votable')" class="btn btn-outline-primary" title="Download results in VO format">
-                        <span class="fas fa-file"></span> VOtable
-                    </a>
-                    &nbsp;
-                    <button *ngIf="getConfigDownloadResultFormat('download_vo')" [disabled]="!sampRegistered" (click)="broadcastResult()" class="btn btn-outline-primary" title="Broadcast samp votable">
-                        <span class="fas fa-broadcast-tower"></span> Broadcast VOtable
-                    </button>
-                </div>
-            </div>
-            <hr *ngIf="isArchiveIsAvailable()" class="my-4">
-            <div *ngIf="isArchiveIsAvailable()" class="row">
-                <div class="col-auto align-self-center">
-                    <p>Download archive files just here:</p>
-                </div>
-                <div class="col">
-                    <a (click)="downloadArchive()" class="btn btn-outline-primary" title="Download an archive with all files">
-                        <span class="fas fa-archive"></span> Files archive
-                    </a>
-                </div>
-            </div>
-        </div>
-    </accordion-group>
-</accordion>
diff --git a/client/src/app/instance/search/components/result/download.component.spec.ts b/client/src/app/instance/search/components/result/download.component.spec.ts
deleted file mode 100644
index c6a1b42dca00844985d60e2c5e0613795c80d4e5..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/result/download.component.spec.ts
+++ /dev/null
@@ -1,256 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
-import { HttpClientTestingModule } from '@angular/common/http/testing';
-
-import { AccordionModule } from 'ngx-bootstrap/accordion';
-
-import { DownloadComponent } from './download.component';
-import { AppConfigService } from '../../../../app-config.service';
-
-describe('[Instance][Search][Component][Result] DownloadComponent', () => {
-    let component: DownloadComponent;
-    let fixture: ComponentFixture<DownloadComponent>;
-    let appConfigServiceStub = new AppConfigService();
-
-    beforeEach(() => {
-        TestBed.configureTestingModule({
-            declarations: [DownloadComponent],
-            imports: [
-                HttpClientTestingModule,
-                AccordionModule.forRoot(),
-                BrowserAnimationsModule
-            ],
-            providers: [{ provide: AppConfigService, useValue: appConfigServiceStub }]
-        });
-        fixture = TestBed.createComponent(DownloadComponent);
-        component = fixture.componentInstance;
-    });
-
-    it('should create the component', () => {
-        expect(component).toBeTruthy();
-    });
-
-    it('#isDownloadActivated() should return if download section has to be enabled or not', () => {
-        component.datasetList = [
-            {
-                name: 'myDataset',
-                table_ref: 'table',
-                label: 'my dataset',
-                description: 'This is my dataset',
-                display: 1,
-                data_path: '/path',
-                survey_name: 'mySurvey',
-                id_dataset_family: 1,
-                public: true,
-                full_data_path: '/data/path',
-                info_survey_enabled: true,
-                info_survey_label: 'More about this survey',
-                cone_search_enabled: true,
-                cone_search_opened: true,
-                cone_search_column_ra: 1,
-                cone_search_column_dec: 2,
-                cone_search_plot_enabled: false,
-                download_enabled: true,
-                download_opened: true,
-                download_json: true,
-                download_csv: true,
-                download_ascii: true,
-                download_vo: true,
-                download_archive: true,
-                summary_enabled: false,
-                summary_opened: false,
-                server_link_enabled: false,
-                server_link_opened: true,
-                datatable_enabled: true,
-                datatable_opened: true,
-                datatable_selectable_rows: true
-            },
-            {
-                name: 'anotherDataset',
-                table_ref: 'table',
-                label: 'another dataset',
-                description: 'This is another dataset',
-                display: 1,
-                data_path: '/path',
-                survey_name: 'mySurvey',
-                id_dataset_family: 1,
-                public: true,
-                full_data_path: '/data/path',
-                info_survey_enabled: true,
-                info_survey_label: 'More about this survey',
-                cone_search_enabled: true,
-                cone_search_opened: true,
-                cone_search_column_ra: 1,
-                cone_search_column_dec: 2,
-                cone_search_plot_enabled: false,
-                download_enabled: false,
-                download_opened: false,
-                download_json: true,
-                download_csv: false,
-                download_ascii: false,
-                download_vo: false,
-                download_archive: false,
-                summary_enabled: true,
-                summary_opened: true,
-                server_link_enabled: true,
-                server_link_opened: true,
-                datatable_enabled: true,
-                datatable_opened: true,
-                datatable_selectable_rows: true
-            }
-        ];
-        component.datasetSelected = 'myDataset';
-        expect(component.isDownloadActivated()).toBeTruthy();
-        component.datasetSelected = 'anotherDataset';
-        expect(component.isDownloadActivated()).toBeFalsy();
-    });
-
-    it('#isDownloadOpened() should return if download tab has to be opened or not', () => {
-        component.datasetList = [
-            {
-                name: 'myDataset',
-                table_ref: 'table',
-                label: 'my dataset',
-                description: 'This is my dataset',
-                display: 1,
-                data_path: '/path',
-                survey_name: 'mySurvey',
-                id_dataset_family: 1,
-                public: true,
-                full_data_path: '/data/path',
-                info_survey_enabled: true,
-                info_survey_label: 'More about this survey',
-                cone_search_enabled: true,
-                cone_search_opened: true,
-                cone_search_column_ra: 1,
-                cone_search_column_dec: 2,
-                cone_search_plot_enabled: false,
-                download_enabled: true,
-                download_opened: true,
-                download_json: true,
-                download_csv: true,
-                download_ascii: true,
-                download_vo: true,
-                download_archive: true,
-                summary_enabled: false,
-                summary_opened: false,
-                server_link_enabled: false,
-                server_link_opened: false,
-                datatable_enabled: true,
-                datatable_opened: true,
-                datatable_selectable_rows: true
-            },
-            {
-                name: 'anotherDataset',
-                table_ref: 'table',
-                label: 'another dataset',
-                description: 'This is another dataset',
-                display: 1,
-                data_path: '/path',
-                survey_name: 'mySurvey',
-                id_dataset_family: 1,
-                public: true,
-                full_data_path: '/data/path',
-                info_survey_enabled: true,
-                info_survey_label: 'More about this survey',
-                cone_search_enabled: true,
-                cone_search_opened: true,
-                cone_search_column_ra: 1,
-                cone_search_column_dec: 2,
-                cone_search_plot_enabled: false,
-                download_enabled: false,
-                download_opened: false,
-                download_json: true,
-                download_csv: false,
-                download_ascii: false,
-                download_vo: false,
-                download_archive: false,
-                summary_enabled: true,
-                summary_opened: true,
-                server_link_enabled: true,
-                server_link_opened: true,
-                datatable_enabled: true,
-                datatable_opened: true,
-                datatable_selectable_rows: true
-            }
-        ];
-        component.datasetSelected = 'myDataset';
-        expect(component.isDownloadOpened()).toBeTruthy();
-        component.datasetSelected = 'anotherDataset';
-        expect(component.isDownloadOpened()).toBeFalsy();
-    });
-
-    it('#getConfigDownloadResultFormat() should return if download button for the given format has to be displayed', () => {
-        component.datasetList = [
-            {
-                name: 'myDataset',
-                table_ref: 'table',
-                label: 'my dataset',
-                description: 'This is my dataset',
-                display: 1,
-                data_path: '/path',
-                survey_name: 'mySurvey',
-                id_dataset_family: 1,
-                public: true,
-                full_data_path: '/data/path',
-                info_survey_enabled: true,
-                info_survey_label: 'More about this survey',
-                cone_search_enabled: true,
-                cone_search_opened: true,
-                cone_search_column_ra: 1,
-                cone_search_column_dec: 2,
-                cone_search_plot_enabled: false,
-                download_enabled: true,
-                download_opened: true,
-                download_json: true,
-                download_csv: true,
-                download_ascii: false,
-                download_vo: true,
-                download_archive: true,
-                summary_enabled: false,
-                summary_opened: false,
-                server_link_enabled: false,
-                server_link_opened: false,
-                datatable_enabled: true,
-                datatable_opened: true,
-                datatable_selectable_rows: true
-            },
-            {
-                name: 'anotherDataset',
-                table_ref: 'table',
-                label: 'another dataset',
-                description: 'This is another dataset',
-                display: 1,
-                data_path: '/path',
-                survey_name: 'mySurvey',
-                id_dataset_family: 1,
-                public: true,
-                full_data_path: '/data/path',
-                info_survey_enabled: true,
-                info_survey_label: 'More about this survey',
-                cone_search_enabled: true,
-                cone_search_opened: true,
-                cone_search_column_ra: 1,
-                cone_search_column_dec: 2,
-                cone_search_plot_enabled: false,
-                download_enabled: false,
-                download_opened: false,
-                download_json: true,
-                download_csv: false,
-                download_ascii: false,
-                download_vo: false,
-                download_archive: false,
-                summary_enabled: true,
-                summary_opened: true,
-                server_link_enabled: true,
-                server_link_opened: true,
-                datatable_enabled: true,
-                datatable_opened: true,
-                datatable_selectable_rows: true
-            }
-        ];
-        component.datasetSelected = 'myDataset';
-        expect(component.getConfigDownloadResultFormat('download_csv')).toBeTruthy();
-        expect(component.getConfigDownloadResultFormat('download_ascii')).toBeFalsy();
-    });
-});
diff --git a/client/src/app/instance/search/components/result/download.component.ts b/client/src/app/instance/search/components/result/download.component.ts
deleted file mode 100644
index 0beeeb88810888111b1a90aa48eee9f9dd28feda..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/result/download.component.ts
+++ /dev/null
@@ -1,93 +0,0 @@
-/**
- * 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, EventEmitter, Input, Output } from '@angular/core';
-
-import { Criterion, ConeSearch } from '../../../store/models';
-import { Dataset, Attribute } from 'src/app/metamodel/models';
-
-/**
- * @class
- * @classdesc Search result download component.
- */
-@Component({
-    selector: 'app-download',
-    templateUrl: 'download.component.html'
-})
-export class DownloadComponent {
-    @Input() datasetSelected: string;
-    @Input() datasetList: Dataset[];
-    @Input() attributeList: Attribute[];
-    @Input() criteriaList: Criterion[];
-    @Input() outputList: number[];
-    @Input() coneSearch: ConeSearch;
-    @Input() dataLength: number;
-    @Input() sampRegistered: boolean;
-    @Output() broadcast: EventEmitter<string> = new EventEmitter();
-    @Output() startTaskCreateResult: EventEmitter<{ format: string, selectedData: boolean, broadcastVo: boolean }> = new EventEmitter();
-    @Output() startTaskCreateArchive: EventEmitter<{ selectedData: boolean }> = new EventEmitter();
-
-    /**
-     * Checks if download tab has to be display.
-     *
-     * @return boolean
-     */
-    isDownloadActivated(): boolean {
-        return this.datasetList.find(d => d.name === this.datasetSelected).download_enabled;
-    }
-
-    /**
-     * Checks if download tab has to be open.
-     *
-     * @return boolean
-     */
-    isDownloadOpened(): boolean {
-        return this.datasetList.find(d => d.name === this.datasetSelected).download_opened;
-    }
-
-    /**
-     * Checks if the download format is allowed by Anis Admin configuration.
-     *
-     * @param  {string} format - The file format to download.
-     *
-     * @return boolean
-     */
-    getConfigDownloadResultFormat(format: string): boolean {
-        return this.datasetList.find(d => d.name === this.datasetSelected)[format];
-    }
-
-    isArchiveIsAvailable() {
-        return this.attributeList
-            .filter(attribute => this.outputList.includes(attribute.id))
-            .filter(attribute => attribute.archive)
-            .length > 0;
-    }
-
-    downloadResult(format: string) {
-        this.startTaskCreateResult.emit({
-            format,
-            selectedData: false,
-            broadcastVo: false
-        });
-    }
-
-    broadcastResult() {
-        this.startTaskCreateResult.emit({
-            format: 'votable',
-            selectedData: false,
-            broadcastVo: true
-        })
-    }
-
-    downloadArchive() {
-        this.startTaskCreateArchive.emit({
-            selectedData: false
-        });
-    }
-}
diff --git a/client/src/app/instance/search/components/result/image-list-result.component.html b/client/src/app/instance/search/components/result/image-list-result.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..f6f9ef6d245fc966aa2c963051994be0d5220800
--- /dev/null
+++ b/client/src/app/instance/search/components/result/image-list-result.component.html
@@ -0,0 +1,38 @@
+<app-spinner *ngIf="imageListIsLoading"></app-spinner>
+
+<div *ngIf="imageListIsLoaded" class="row">
+    <div *ngFor="let image of imageList" class="col-2">
+        <div class="row">
+            <span class="pointer">
+                <img (click)="openConeSearch(cs, getHref(image))" [src]="getHref(image)" height="100px" class="img-thumbnail mb-2" />
+            </span>
+        </div>
+        <div class="row justify-content-center">
+            <a class="btn btn-outline-primary btn-sm" [href]="getFitsCutUrl(image)" (click)="saveFitsCutFile($event, image)">
+                <span class="fa-solid fa-floppy-disk"></span>
+            </a>
+            &nbsp;
+            <a class="btn btn-outline-primary btn-sm" [class.disabled]="!sampRegistered" (click)="broadcast(image)">
+                <span class="fas fa-broadcast-tower"></span>
+            </a>
+        </div>
+    </div>
+</div>
+
+<ng-template #cs>
+    <div class="modal-header">
+        <h4 class="modal-title pull-left">Cone-search</h4>
+        <button type="button" class="close pull-right" aria-label="Close" (click)="modalRef.hide()">
+            <span aria-hidden="true">&times;</span>
+        </button>
+    </div>
+    <div class="modal-body">
+        <app-spinner *ngIf="dataIsLoading"></app-spinner>
+        <app-cone-search-plot *ngIf="dataIsLoaded"
+            [coneSearch]="coneSearch"
+            [dataset]="dataset"
+            [data]="getData()"
+            [backgroundHref]="backgroundHref">
+        </app-cone-search-plot>
+    </div>
+</ng-template>
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/result/image-list-result.component.ts b/client/src/app/instance/search/components/result/image-list-result.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ed7b109c5714ede334f00dd85a7aff249760be16
--- /dev/null
+++ b/client/src/app/instance/search/components/result/image-list-result.component.ts
@@ -0,0 +1,78 @@
+import { Component, Input, Output, EventEmitter, TemplateRef } from '@angular/core';
+
+import FileSaver from 'file-saver';
+import { BsModalService, BsModalRef } from 'ngx-bootstrap/modal';
+
+import { Dataset, Attribute, Image } from 'src/app/metamodel/models';
+import { ConeSearch } from 'src/app/instance/store/models';
+import { AppConfigService } from 'src/app/app-config.service';
+
+@Component({
+    selector: 'app-image-list-result',
+    templateUrl: 'image-list-result.component.html'
+})
+export class ImageListResultComponent {
+    @Input() dataset: Dataset;
+    @Input() attributeList: Attribute[];
+    @Input() coneSearch: ConeSearch;
+    @Input() data: any;
+    @Input() dataIsLoading: boolean;
+    @Input() dataIsLoaded: boolean;
+    @Input() imageList: Image[];
+    @Input() imageListIsLoading: boolean;
+    @Input() imageListIsLoaded: boolean;
+    @Input() sampRegistered: boolean;
+    @Output() broadcastImage: EventEmitter<string> = new EventEmitter();
+    @Output() downloadFile: EventEmitter<{url: string, filename: string}> = new EventEmitter();
+
+    modalRef: BsModalRef;
+    backgroundHref: string;
+
+    constructor(private config: AppConfigService, private modalService: BsModalService) { }
+
+    getHref(image: Image) {
+        let href = `${this.config.servicesUrl}/fits-cut-to-png/${this.dataset.name}?filename=${image.file_path}`;
+        href += `&ra=${this.coneSearch.ra}`;
+        href += `&dec=${this.coneSearch.dec}`;
+        href += `&radius=${this.coneSearch.radius}`;
+        href += `&stretch=${image.stretch}`;
+        href += `&pmin=${image.pmin}`;
+        href += `&pmax=${image.pmax}`;
+        href += `&axes=false`;
+        return href;
+    }
+
+    getFitsCutUrl(image: Image) {
+        let url = `${this.config.servicesUrl}/fits-cut/${this.dataset.name}?filename=${image.file_path}`;
+        url += `&ra=${this.coneSearch.ra}`;
+        url += `&dec=${this.coneSearch.dec}`;
+        url += `&radius=${this.coneSearch.radius}`;
+        return url;
+    }
+
+    saveFitsCutFile(event, image: Image) {
+        event.preventDefault();
+
+        const url = this.getFitsCutUrl(image);
+        const filename = image.file_path.substring(image.file_path.lastIndexOf('/') + 1);
+
+        this.downloadFile.emit({url, filename});
+    }
+
+    broadcast(image: Image) {
+        this.broadcastImage.emit(
+            this.getFitsCutUrl(image)
+        );
+    }
+
+    getData() {
+        const columnRa = this.attributeList.find(a => a.id === this.dataset.cone_search_column_ra);
+        const columnDec = this.attributeList.find(a => a.id === this.dataset.cone_search_column_dec);
+        return this.data.map(d => ({ "x": +d[columnRa.label], "y": +d[columnDec.label] }));
+    }
+
+    openConeSearch(template: TemplateRef<any>, backgroundHref: string): void {
+        this.backgroundHref = backgroundHref;
+        this.modalRef = this.modalService.show(template, { class: 'modal-lg' });
+    }
+}
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/result/index.ts b/client/src/app/instance/search/components/result/index.ts
index 80f3a16b03bf6dc2754b81ce5477fa14265ebdd6..550b053fa80c26002e2a762d39c89b3e7f27b735 100644
--- a/client/src/app/instance/search/components/result/index.ts
+++ b/client/src/app/instance/search/components/result/index.ts
@@ -1,21 +1,17 @@
-import { DatatableTabComponent } from './datatable-tab.component';
-import { DownloadComponent } from './download.component';
-import { ReminderComponent } from './reminder.component';
+import { DownloadResultComponent } from './download-result.component';
+import { ImageListResultComponent } from './image-list-result.component';
+import { DatatableActionsComponent } from './datatable-actions.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 { ConeSearchPlotComponent } from './cone-search-plot.component';
 import { rendererComponents } from './renderer';
 
 export const resultComponents = [
-    DatatableTabComponent,
-    DownloadComponent,
-    ReminderComponent,
     UrlDisplayComponent,
+    ImageListResultComponent,
     DatatableComponent,
     DatatableActionsComponent,
-    DownloadFileTabComponent,
     ConeSearchPlotComponent,
+    DownloadResultComponent,
     rendererComponents
 ];
diff --git a/client/src/app/instance/search/components/result/reminder.component.html b/client/src/app/instance/search/components/result/reminder.component.html
deleted file mode 100644
index 38372acef640c24c36328c786925527dd5ecfd8f..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/result/reminder.component.html
+++ /dev/null
@@ -1,78 +0,0 @@
-<accordion *ngIf="isSummaryActivated()" [isAnimated]="true">
-    <accordion-group #ag [isOpen]="isSummaryOpened()" [panelClass]="'custom-accordion'" class="my-2">
-        <button class="btn btn-link btn-block clearfix" accordion-heading>
-            <span class="pull-left float-left">
-                Search summary
-                &nbsp;
-                <span *ngIf="ag.isOpen">
-                    <span class="fas fa-chevron-up"></span>
-                </span>
-                <span *ngIf="!ag.isOpen">
-                    <span class="fas fa-chevron-down"></span>
-                </span>
-            </span>
-        </button>
-        <div>
-            <tabset [justified]="true">
-                <tab>
-                    <ng-template tabHeading>
-                        Criteria <span class="badge badge-pill badge-secondary">{{ nbCriteria() }}</span>
-                    </ng-template>
-                    <div class="tab-content container-fluid pt-3">
-                        <div *ngIf="nbCriteria() === 0" class="text-center font-weight-bold pt-5">
-                            No selected criteria
-                        </div>
-
-                        <div *ngIf="nbCriteria() > 0" class="row">
-                            <div *ngIf="coneSearch" class="col-12 col-md-6 col-xl-4 pb-3">
-                                <span class="title">Cone search</span>
-                                <ul class="list-unstyled pl-3">
-                                    <li>RA = {{ coneSearch.ra }}°</li>
-                                    <li>DEC = {{ coneSearch.dec }}°</li>
-                                    <li>radius = {{ coneSearch.radius }} arcsecond</li>
-                                </ul>
-                            </div>
-
-                            <ng-container *ngFor="let family of criteriaFamilyList">
-                                <ng-container *ngIf="criteriaByFamily(family.id).length > 0">
-                                    <div class="col-12 col-md-6 col-xl-4 pb-3">
-                                        <span class="title">{{ family.label }}</span>
-                                        <ul class="list-unstyled pl-3">
-                                            <li *ngFor="let criterion of criteriaByFamily(family.id)">
-                                                {{ getAttribute(criterion.id).form_label }} {{ printCriterion(criterion) }}
-                                            </li>
-                                        </ul>
-                                    </div>
-                                </ng-container>
-                            </ng-container>
-                        </div>
-                    </div>
-                </tab>
-                <tab>
-                    <ng-template tabHeading>
-                        Outputs <span class="badge badge-pill badge-secondary">{{ outputList.length }}</span>
-                    </ng-template>
-                    <div class="tab-content container-fluid pt-3">
-                        <div class="row">
-                            <ng-container *ngFor="let family of outputFamilyList">
-                                <ng-container *ngFor="let category of categoryListByFamily(family.id)">
-                                    <ng-container *ngIf="outputListByCategory(category.id).length > 0">
-                                        <div class="col-12 col-md-6 col-lg-4 col-xl-3 pb-3">
-                                            <span class="title">{{ family.label }}</span><br>
-                                            <span class="title pl-3">{{ category.label }}</span>
-                                            <ul class="list-unstyled pl-5">
-                                                <li *ngFor="let output of outputListByCategory(category.id)">
-                                                    {{ getAttribute(output).form_label }}
-                                                </li>
-                                            </ul>
-                                        </div>
-                                    </ng-container>
-                                </ng-container>
-                            </ng-container>
-                        </div>
-                    </div>
-                </tab>
-            </tabset>
-        </div>
-    </accordion-group>
-</accordion>
diff --git a/client/src/app/instance/search/components/result/reminder.component.scss b/client/src/app/instance/search/components/result/reminder.component.scss
deleted file mode 100644
index 2ae066f52c19712023452598c1cdfb6fab911bd8..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/result/reminder.component.scss
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- * 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.
- */
-
-.tab-content {
-    height: 250px;
-    border-bottom: #dee2e6 solid 1px;
-    border-left: #dee2e6 solid 1px;
-    border-right: #dee2e6 solid 1px;
-    overflow-y: auto;
-}
-
-.title {
-    color: #6c757d;
-    font-size: 90%;
-}
diff --git a/client/src/app/instance/search/components/result/reminder.component.spec.ts b/client/src/app/instance/search/components/result/reminder.component.spec.ts
deleted file mode 100644
index 727252824e92e5cc720799d2a7fcb9d082b8f7ca..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/result/reminder.component.spec.ts
+++ /dev/null
@@ -1,454 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
-
-import { AccordionModule } from 'ngx-bootstrap/accordion';
-
-import { ReminderComponent } from './reminder.component';
-import { FieldCriterion } from '../../../store/models/criterion';
-import { TabsModule } from 'ngx-bootstrap/tabs';
-import { Criterion } from '../../../store/models';
-
-describe('[Instance][Search][Component][Result] ReminderComponent', () => {
-    let component: ReminderComponent;
-    let fixture: ComponentFixture<ReminderComponent>;
-
-    beforeEach(() => {
-        TestBed.configureTestingModule({
-            declarations: [ReminderComponent],
-            imports: [
-                AccordionModule.forRoot(),
-                TabsModule.forRoot(),
-                BrowserAnimationsModule
-            ]
-        });
-        fixture = TestBed.createComponent(ReminderComponent);
-        component = fixture.componentInstance;
-    });
-
-    it('should create the component', () => {
-        expect(component).toBeTruthy();
-    });
-
-    it('#isSummaryActivated() should return if summary has to be enabled or not', () => {
-        component.datasetList = [
-            {
-                name: 'myDataset',
-                table_ref: 'table',
-                label: 'my dataset',
-                description: 'This is my dataset',
-                display: 1,
-                data_path: '/path',
-                survey_name: 'mySurvey',
-                id_dataset_family: 1,
-                public: true,
-                full_data_path: '/data/path',
-                info_survey_enabled: true,
-                info_survey_label: 'More about this survey',
-                cone_search_enabled: true,
-                cone_search_opened: true,
-                cone_search_column_ra: 1,
-                cone_search_column_dec: 2,
-                cone_search_plot_enabled: false,
-                download_enabled: true,
-                download_opened: true,
-                download_json: true,
-                download_csv: true,
-                download_ascii: true,
-                download_vo: true,
-                download_archive: true,
-                summary_enabled: false,
-                summary_opened: false,
-                server_link_enabled: false,
-                server_link_opened: true,
-                datatable_enabled: true,
-                datatable_opened: true,
-                datatable_selectable_rows: true
-            },
-            {
-                name: 'anotherDataset',
-                table_ref: 'table',
-                label: 'another dataset',
-                description: 'This is another dataset',
-                display: 1,
-                data_path: '/path',
-                survey_name: 'mySurvey',
-                id_dataset_family: 1,
-                public: true,
-                full_data_path: '/data/path',
-                info_survey_enabled: true,
-                info_survey_label: 'More about this survey',
-                cone_search_enabled: true,
-                cone_search_opened: true,
-                cone_search_column_ra: 1,
-                cone_search_column_dec: 2,
-                cone_search_plot_enabled: false,
-                download_enabled: true,
-                download_opened: true,
-                download_json: true,
-                download_csv: true,
-                download_ascii: true,
-                download_vo: true,
-                download_archive: true,
-                summary_enabled: true,
-                summary_opened: true,
-                server_link_enabled: true,
-                server_link_opened: true,
-                datatable_enabled: true,
-                datatable_opened: true,
-                datatable_selectable_rows: true
-            }
-        ];
-        component.datasetSelected = 'myDataset';
-        expect(component.isSummaryActivated()).toBeFalsy();
-        component.datasetSelected = 'anotherDataset';
-        expect(component.isSummaryActivated()).toBeTruthy();
-    });
-
-    it('#isSummaryOpened() should return if summary tab has to be opened or not', () => {
-        component.datasetList = [
-            {
-                name: 'myDataset',
-                table_ref: 'table',
-                label: 'my dataset',
-                description: 'This is my dataset',
-                display: 1,
-                data_path: '/path',
-                survey_name: 'mySurvey',
-                id_dataset_family: 1,
-                public: true,
-                full_data_path: '/data/path',
-                info_survey_enabled: true,
-                info_survey_label: 'More about this survey',
-                cone_search_enabled: true,
-                cone_search_opened: true,
-                cone_search_column_ra: 1,
-                cone_search_column_dec: 2,
-                cone_search_plot_enabled: false,
-                download_enabled: true,
-                download_opened: true,
-                download_json: true,
-                download_csv: true,
-                download_ascii: true,
-                download_vo: true,
-                download_archive: true,
-                summary_enabled: false,
-                summary_opened: false,
-                server_link_enabled: false,
-                server_link_opened: false,
-                datatable_enabled: true,
-                datatable_opened: true,
-                datatable_selectable_rows: true
-            },
-            {
-                name: 'anotherDataset',
-                table_ref: 'table',
-                label: 'another dataset',
-                description: 'This is another dataset',
-                display: 1,
-                data_path: '/path',
-                survey_name: 'mySurvey',
-                id_dataset_family: 1,
-                public: true,
-                full_data_path: '/data/path',
-                info_survey_enabled: true,
-                info_survey_label: 'More about this survey',
-                cone_search_enabled: true,
-                cone_search_opened: true,
-                cone_search_column_ra: 1,
-                cone_search_column_dec: 2,
-                cone_search_plot_enabled: false,
-                download_enabled: true,
-                download_opened: true,
-                download_json: true,
-                download_csv: true,
-                download_ascii: true,
-                download_vo: true,
-                download_archive: true,
-                summary_enabled: true,
-                summary_opened: true,
-                server_link_enabled: true,
-                server_link_opened: true,
-                datatable_enabled: true,
-                datatable_opened: true,
-                datatable_selectable_rows: true
-            }
-        ];
-        component.datasetSelected = 'myDataset';
-        expect(component.isSummaryOpened()).toBeFalsy();
-        component.datasetSelected = 'anotherDataset';
-        expect(component.isSummaryOpened()).toBeTruthy();
-    });
-
-    it('#nbCriteria() should return criteria amount', () => {
-        component.criteriaList = [];
-        expect(component.nbCriteria()).toEqual(0);
-        component.coneSearch = { ra: 1, dec: 2, radius: 3 };
-        expect(component.nbCriteria()).toEqual(1);
-    });
-
-    it('#criteriaByFamily() should return criteria for the given family', () => {
-        component.attributeList = [
-            {
-                id: 2,
-                name: 'name_two',
-                label: 'label_two',
-                form_label: 'form_label_two',
-                description : 'description_two',
-                primary_key: false,
-                type: '',
-                search_type : 'field',
-                operator : '=',
-                min: null,
-                max: null,
-                placeholder_min: null,
-                placeholder_max: null,
-                criteria_display: 1,
-                output_display: 1,
-                selected: true,
-                renderer: null,
-                renderer_config: null,
-                order_by: true,
-                archive: false,
-                detail: true,
-                display_detail: 1,
-                renderer_detail: null,
-                renderer_detail_config: null,
-                options: null,
-                vo_utype: null,
-                vo_ucd: null,
-                vo_unit: null,
-                vo_description: null,
-                vo_datatype: null,
-                vo_size: null,
-                id_criteria_family: 2,
-                id_output_category: null
-            },
-            {
-                id: 1,
-                name: 'name_one',
-                label: 'label_one',
-                form_label: 'form_label_one',
-                description: 'description_one',
-                primary_key: true,
-                type: 'integer',
-                search_type: 'field',
-                operator: '=',
-                min: null,
-                max: null,
-                placeholder_min: null,
-                placeholder_max: null,
-                criteria_display: 2,
-                output_display: 2,
-                selected: true,
-                renderer: null,
-                renderer_config: null,
-                order_by: true,
-                archive: false,
-                detail: true,
-                display_detail: 2,
-                renderer_detail: null,
-                renderer_detail_config: null,
-                options: null,
-                vo_utype: null,
-                vo_ucd: null,
-                vo_unit: null,
-                vo_description: null,
-                vo_datatype: null,
-                vo_size: null,
-                id_criteria_family: 1,
-                id_output_category: null
-            }
-        ];
-        component.criteriaList = [
-            {'id':1,'type':'field','operator':'eq','value':'one'} as FieldCriterion,
-            {'id':2,'type':'field','operator':'eq','value':'two'} as FieldCriterion
-        ];
-        expect(component.criteriaByFamily(1).length).toEqual(1);
-        expect(component.criteriaByFamily(1)[0].id).toEqual(1);
-    });
-
-    it('#getAttribute() should return the attribute with the given id', () => {
-        component.attributeList = [
-            {
-                id: 2,
-                name: 'name_two',
-                label: 'label_two',
-                form_label: 'form_label_two',
-                description : 'description_two',
-                primary_key: false,
-                type: '',
-                search_type : 'field',
-                operator : '=',
-                min: null,
-                max: null,
-                placeholder_min: null,
-                placeholder_max: null,
-                criteria_display: 1,
-                output_display: 1,
-                selected: true,
-                renderer: null,
-                renderer_config: null,
-                order_by: true,
-                archive: false,
-                detail: true,
-                display_detail: 1,
-                renderer_detail: null,
-                renderer_detail_config: null,
-                options: null,
-                vo_utype: null,
-                vo_ucd: null,
-                vo_unit: null,
-                vo_description: null,
-                vo_datatype: null,
-                vo_size: null,
-                id_criteria_family: null,
-                id_output_category: null
-            },
-            {
-                id: 1,
-                name: 'name_one',
-                label: 'label_one',
-                form_label: 'form_label_one',
-                description: 'description_one',
-                primary_key: true,
-                type: 'integer',
-                search_type: 'field',
-                operator: '=',
-                min: null,
-                max: null,
-                placeholder_min: null,
-                placeholder_max: null,
-                criteria_display: 2,
-                output_display: 2,
-                selected: true,
-                renderer: null,
-                renderer_config: null,
-                order_by: true,
-                archive: false,
-                detail: true,
-                display_detail: 2,
-                renderer_detail: null,
-                renderer_detail_config: null,
-                options: null,
-                vo_utype: null,
-                vo_ucd: null,
-                vo_unit: null,
-                vo_description: null,
-                vo_datatype: null,
-                vo_size: null,
-                id_criteria_family: null,
-                id_output_category: null
-            }
-        ];
-        expect(component.getAttribute(1).name).toBe('name_one');
-    });
-
-    it('#printCriterion() should return pretty criterion', () => {
-        const criterion: Criterion = {'id':1,'type':'field','operator':'eq','value':'one'} as FieldCriterion;
-        expect(component.printCriterion(criterion)).toBe('= one');
-    });
-
-    it('#categoryListByFamily() should return output categories for the given output family', () => {
-        component.outputCategoryList = [
-            {
-                id: 3,
-                label: 'The last output category',
-                display: 30,
-                id_output_family: 2
-            },
-            {
-                id: 1,
-                label: 'Another output category',
-                display: 10,
-                id_output_family: 1
-            },
-            {
-                id: 2,
-                label: 'Default output category',
-                display: 20,
-                id_output_family: 1
-            }
-        ];
-        expect(component.categoryListByFamily(1).length).toEqual(2);
-        expect(component.categoryListByFamily(1)[0].id).toEqual(1);
-        expect(component.categoryListByFamily(1)[1].id).toEqual(2);
-    });
-
-    it('#outputListByCategory() should return outputs for the given output category', () => {
-        component.attributeList = [
-            {
-                id: 2,
-                name: 'name_two',
-                label: 'label_two',
-                form_label: 'form_label_two',
-                description : 'description_two',
-                primary_key: false,
-                type: '',
-                search_type : 'field',
-                operator : '=',
-                min: null,
-                max: null,
-                placeholder_min: null,
-                placeholder_max: null,
-                criteria_display: 1,
-                output_display: 1,
-                selected: true,
-                renderer: null,
-                renderer_config: null,
-                order_by: true,
-                archive: false,
-                detail: true,
-                display_detail: 1,
-                renderer_detail: null,
-                renderer_detail_config: null,
-                options: null,
-                vo_utype: null,
-                vo_ucd: null,
-                vo_unit: null,
-                vo_description: null,
-                vo_datatype: null,
-                vo_size: null,
-                id_criteria_family: null,
-                id_output_category: 2
-            },
-            {
-                id: 1,
-                name: 'name_one',
-                label: 'label_one',
-                form_label: 'form_label_one',
-                description: 'description_one',
-                primary_key: true,
-                type: 'integer',
-                search_type: 'field',
-                operator: '=',
-                min: null,
-                max: null,
-                placeholder_min: null,
-                placeholder_max: null,
-                criteria_display: 2,
-                output_display: 2,
-                selected: true,
-                renderer: null,
-                renderer_config: null,
-                order_by: true,
-                archive: false,
-                detail: true,
-                display_detail: 2,
-                renderer_detail: null,
-                renderer_detail_config: null,
-                options: null,
-                vo_utype: null,
-                vo_ucd: null,
-                vo_unit: null,
-                vo_description: null,
-                vo_datatype: null,
-                vo_size: null,
-                id_criteria_family: null,
-                id_output_category: 1
-            }
-        ];
-        component.outputList = [1, 2];
-        expect(component.outputListByCategory(1).length).toEqual(1);
-        expect(component.outputListByCategory(1)[0]).toEqual(1);
-    });
-});
diff --git a/client/src/app/instance/search/components/result/reminder.component.ts b/client/src/app/instance/search/components/result/reminder.component.ts
deleted file mode 100644
index 71c94e903e64a2e33d28b4c0bf16f2e7b24b6d30..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/result/reminder.component.ts
+++ /dev/null
@@ -1,129 +0,0 @@
-/**
- * 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 { Criterion, ConeSearch, getPrettyCriterion } from 'src/app/instance/store/models';
-import { Dataset, Attribute, CriteriaFamily, OutputFamily, OutputCategory } from 'src/app/metamodel/models';
-
-/**
- * @class
- * @classdesc Search result reminder component.
- */
-@Component({
-    selector: 'app-reminder',
-    templateUrl: 'reminder.component.html',
-    styleUrls: ['reminder.component.scss']
-})
-export class ReminderComponent {
-    @Input() datasetSelected: string;
-    @Input() datasetList: Dataset[];
-    @Input() attributeList: Attribute[];
-    @Input() criteriaFamilyList: CriteriaFamily[];
-    @Input() outputFamilyList: OutputFamily[];
-    @Input() outputCategoryList: OutputCategory[];
-    @Input() criteriaList: Criterion[];
-    @Input() coneSearch: ConeSearch;
-    @Input() outputList: number[];
-
-    /**
-     * Checks if reminder has to be display.
-     *
-     * @return boolean
-     */
-    isSummaryActivated(): boolean {
-        return this.datasetList.find(d => d.name === this.datasetSelected).summary_enabled;
-    }
-
-    /**
-     * Checks if reminder has to be open.
-     *
-     * @return boolean
-     */
-    isSummaryOpened(): boolean {
-        return this.datasetList.find(d => d.name === this.datasetSelected).summary_opened;
-    }
-
-    /**
-     * Returns total of added criteria.
-     *
-     * @return number
-     */
-    nbCriteria(): number {
-        if (this.coneSearch) {
-            return this.criteriaList.length + 1;
-        }
-        return this.criteriaList.length;
-    }
-
-    /**
-     * Returns criteria list for the given criteria family ID.
-     *
-     * @param  {number} idFamily - The criteria family ID.
-     *
-     * @return Criterion[]
-     */
-    criteriaByFamily(idFamily: number): Criterion[] {
-        const attributeListByFamily: Attribute[] = this.attributeList
-            .filter(attribute => attribute.id_criteria_family === idFamily);
-        return this.criteriaList
-            .filter(criterion => attributeListByFamily.includes(
-                this.attributeList.find(attribute => attribute.id === criterion.id))
-            );
-    }
-
-    /**
-     * Returns attribute for the given attribute ID.
-     *
-     * @param  {number} id - The attribute ID.
-     *
-     * @return Attribute
-     */
-    getAttribute(id: number): Attribute {
-        return this.attributeList.find(attribute => attribute.id === id);
-    }
-
-    /**
-     * Returns criterion pretty printed.
-     *
-     * @param  {Criterion} criterion - The criterion.
-     *
-     * @return string
-     */
-    printCriterion(criterion: Criterion): string {
-        return getPrettyCriterion(criterion);
-    }
-
-    /**
-     * Returns output category list for the given criteria family ID.
-     *
-     * @param  {number} idFamily - The criteria family ID.
-     *
-     * @return OutputCategory[]
-     */
-    categoryListByFamily(idFamily: number): OutputCategory[] {
-        return this.outputCategoryList.filter(outputCategory => outputCategory.id_output_family === idFamily);
-    }
-
-    /**
-     * Returns output list for the given category ID.
-     *
-     * @param  {number} idCategory - The output category ID.
-     *
-     * @return number[]
-     */
-    outputListByCategory(idCategory: number): number[] {
-        const attributeListByCategory: Attribute[] = this.attributeList
-            .filter(attribute => attribute.id_output_category === idCategory);
-        return this.outputList
-            .filter(output => attributeListByCategory.includes(
-                this.attributeList.find(attribute => attribute.id === output))
-            );
-    }
-}
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 e2cfc9a344717640c30fa5c6dbfd2aee6b69d3ef..348b909cdd9f01cbeb0988790473564c29cc4ec0 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,7 +7,7 @@
  * file that was distributed with this source code.
  */
 
-import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
+import { Component, Input, ChangeDetectionStrategy, Output, EventEmitter } from '@angular/core';
 
 import { DownloadRendererConfig } from 'src/app/metamodel/models/renderers/download-renderer-config.model';
 import { getHost } from 'src/app/shared/utils';
@@ -27,7 +27,7 @@ export class DownloadRendererComponent {
     @Input() datasetName: string;
     @Input() datasetPublic: boolean;
     @Input() config: DownloadRendererConfig;
-    @Output() downloadFile: EventEmitter<{url: string, fileId: string, datasetName: string, filename: string}> = new EventEmitter();
+    @Output() downloadFile: EventEmitter<{url: string, filename: string}> = new EventEmitter();
 
     constructor(private appConfig: AppConfigService) { }
 
@@ -62,15 +62,6 @@ export class DownloadRendererComponent {
         const url = this.getHref();
         const filename = url.substring(url.lastIndexOf('/') + 1);
 
-        const n = Math.floor(Math.random() * 11);
-        const k = Math.floor(Math.random() * 1000000);
-        const m = String.fromCharCode(n) + k;
-
-        this.downloadFile.emit({
-            url,
-            fileId: m,
-            datasetName: this.datasetName,
-            filename
-        });
+        this.downloadFile.emit({ url, filename });
     }
 }
diff --git a/client/src/app/instance/search/components/result/url-display.component.html b/client/src/app/instance/search/components/result/url-display.component.html
index 109b122d3e7c0438f942653b9b75497d43986112..38d1db788b13717e658ad1577b3352f130c00e3f 100644
--- a/client/src/app/instance/search/components/result/url-display.component.html
+++ b/client/src/app/instance/search/components/result/url-display.component.html
@@ -1,29 +1,3 @@
-<accordion *ngIf="urlDisplayEnabled()" [isAnimated]="true">
-    <accordion-group #ag [isOpen]="urlDisplayOpened()" [panelClass]="'custom-accordion'" class="my-2">
-        <button class="btn btn-link btn-block clearfix" accordion-heading>
-            <span class="pull-left float-left">
-                Direct link to the result (JSON)
-                &nbsp;
-                <span *ngIf="ag.isOpen">
-                    <span class="fas fa-chevron-up"></span>
-                </span>
-                <span *ngIf="!ag.isOpen">
-                    <span class="fas fa-chevron-down"></span>
-                </span>
-            </span>
-        </button>
-        <div>
-            <div class="row">
-                <div class="col">
-                    <a target="_blank" [href]="getUrl()">{{ getUrl() }}</a>
-                </div>
-                <div class="col-2 align-self-center text-center">
-                    <button class="btn btn-sm btn-outline-primary" (click)="copyToClipboard()"
-                        title="Copy url to clipboard">
-                        COPY
-                    </button>
-                </div>
-            </div>
-        </div>
-    </accordion-group>
-</accordion>
+<div *ngIf="dataset.server_link_enabled" class="text-right mb-2">
+    Direct link to the result (JSON): <a target="_blank" [href]="getUrl('json')">{{ getUrl('json') }}</a> 
+</div>
diff --git a/client/src/app/instance/search/components/result/url-display.component.ts b/client/src/app/instance/search/components/result/url-display.component.ts
index ff9cd13c5bda793b49f1a852974b56a042186676..53688b2fff27a184e5372ea59dcb50b364a456e7 100644
--- a/client/src/app/instance/search/components/result/url-display.component.ts
+++ b/client/src/app/instance/search/components/result/url-display.component.ts
@@ -7,14 +7,9 @@
  * file that was distributed with this source code.
  */
 
-import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
+import { Component, ChangeDetectionStrategy } from '@angular/core';
 
-import { ToastrService } from 'ngx-toastr';
-
-import { Criterion, ConeSearch, criterionToString } from 'src/app/instance/store/models';
-import { Dataset } from 'src/app/metamodel/models';
-import { getHost } from 'src/app/shared/utils';
-import { AppConfigService } from 'src/app/app-config.service';
+import { AbstractDownloadComponent } from './abstract-download.component';
 
 /**
  * @class
@@ -25,59 +20,4 @@ import { AppConfigService } from 'src/app/app-config.service';
     templateUrl: 'url-display.component.html',
     changeDetection: ChangeDetectionStrategy.OnPush
 })
-export class UrlDisplayComponent {
-    @Input() datasetSelected: string;
-    @Input() datasetList: Dataset[];
-    @Input() criteriaList: Criterion[];
-    @Input() outputList: number[];
-    @Input() coneSearch: ConeSearch;
-
-    constructor(private toastr: ToastrService, private appConfig: AppConfigService) { }
-
-    /**
-     * Checks if URL display is enabled.
-     *
-     * @return boolean
-     */
-    urlDisplayEnabled(): boolean {
-        return this.datasetList.find(d => d.name === this.datasetSelected).server_link_enabled;
-    }
-
-    /**
-     * Checks if URL tab has to be opened.
-     *
-     * @return boolean
-     */
-    urlDisplayOpened(): boolean {
-        return this.datasetList.find(d => d.name === this.datasetSelected).server_link_opened;
-    }
-
-    /**
-     * Returns API URL to get data with user parameters.
-     *
-     * @return string
-     */
-    getUrl(): string {
-        let query: string = `${getHost(this.appConfig.apiUrl)}/search/${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;
-    }
-
-    /**
-     * Copies API URL to user clipboard.
-     */
-    copyToClipboard(): void {
-        const selBox = document.createElement('textarea');
-        selBox.value = this.getUrl();
-        document.body.appendChild(selBox);
-        selBox.select();
-        document.execCommand('copy');
-        document.body.removeChild(selBox);
-        this.toastr.success('Copied');
-    }
-}
+export class UrlDisplayComponent extends AbstractDownloadComponent { }
diff --git a/client/src/app/instance/search/containers/result.component.html b/client/src/app/instance/search/containers/result.component.html
index cb01ac25c98cb0ff8c8f0c56a97ebd7920a2b9a0..f375f3fe162d395930fa84f79f2ba9ff4500741b 100644
--- a/client/src/app/instance/search/containers/result.component.html
+++ b/client/src/app/instance/search/containers/result.component.html
@@ -19,41 +19,71 @@
             </div>
         </div>
         <ng-container *ngIf="(dataLength | async) > 0">
-            <div class="jumbotron mb-4 py-4">
-                <div class="lead">
-                    Dataset <span class="font-weight-bold">{{ (datasetList | async | datasetByName:(datasetSelected | async)).label }}</span> 
-                    selected with <span class="font-weight-bold">{{ dataLength | async }}</span> objects found.
+            <div class="jumbotron row mb-4 py-4">
+                <div class="col">
+                    <div class="lead">
+                        Dataset <span class="font-weight-bold">{{ (dataset | async).label }}</span> 
+                        selected with <span class="font-weight-bold">{{ dataLength | async }}</span> objects found.
+                    </div>
+                    <div class="lead mt-4">
+                        <app-download-result
+                            [instance]="instance | async"
+                            [dataset]="dataset | async"
+                            [attributeList]="attributeList | async"
+                            [criteriaList]="criteriaList | async"
+                            [outputList]="outputList | async"
+                            [coneSearch]="coneSearch | async"
+                            [sampRegistered]="sampRegistered | async"
+                            [archiveIsCreating]="archiveIsCreating | async"
+                            (sampRegister)="sampRegister()"
+                            (sampUnregister)="sampUnregister()"
+                            (downloadFile)="downloadFile($event)"
+                            (broadcastVotable)="broadcastVotable($event)"
+                            (startTaskCreateArchive)="startTaskCreateArchive($event)">
+                        </app-download-result>
+                    </div>
                 </div>
-                <div class="lead mt-3">
-                    Download results: 
-                    <a class="btn btn-primary" title="Download results in JSON format">
-                        <span class="fas fa-file"></span> JSON
-                    </a>
-                    &nbsp;
-                    <a class="btn btn-primary" title="Download results in CSV format">
-                        <span class="fas fa-file-csv"></span> CSV
-                    </a>
-                    &nbsp;
-                    <a class="btn btn-primary" title="Download results in ASCII format">
-                        <span class="fas fa-file"></span> ASCII
-                    </a>
-                    &nbsp;
-                    <a class="btn btn-primary" title="Download results in VO format">
-                        <span class="fas fa-file"></span> VOtable
-                    </a>
+                <div class="col-md-6" *ngIf="coneSearch | async">
+                    <app-image-list-result
+                        [dataset]="dataset | async"
+                        [attributeList]="attributeList | async"
+                        [coneSearch]="coneSearch | async"
+                        [data]="data | async"
+                        [dataIsLoading]="dataIsLoading | async"
+                        [dataIsLoaded]="dataIsLoaded | async"
+                        [imageList]="imageList | async"
+                        [imageListIsLoading]="imageListIsLoading | async"
+                        [imageListIsLoaded]="imageListIsLoaded | async"
+                        [sampRegistered]="sampRegistered | async"
+                        (downloadFile)="downloadFile($event)">
+                    </app-image-list-result>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-2">
+                    <app-datatable-actions 
+                        [dataset]="dataset | async"
+                        [criteriaList]="criteriaList | async"
+                        [outputList]="outputList | async"
+                        [coneSearch]="coneSearch | async"
+                        [attributeList]="attributeList | async"
+                        [selectedData]="selectedData | async"
+                        [sampRegistered]="sampRegistered | async"
+                        [archiveIsCreating]="archiveIsCreating | async"
+                        (downloadFile)="downloadFile($event)"
+                        (broadcastVotable)="broadcastVotable($event)"
+                        (startTaskCreateArchive)="startTaskCreateArchive($event)">
+                    </app-datatable-actions>
+                </div>
+                <div class="col align-self-center">
+                    <app-url-display
+                        [dataset]="dataset | async"
+                        [criteriaList]="criteriaList | async"
+                        [outputList]="outputList | async"
+                        [coneSearch]="coneSearch | async">
+                    </app-url-display>
                 </div>
             </div>
-
-            <app-datatable-actions 
-                [selectedData]="selectedData | async"
-                [datasetSelected]="datasetSelected | async"
-                [datasetList]="datasetList | async"
-                [sampRegistered]="sampRegistered | async"
-                (broadcast)="broadcastVotable($event)"
-                (startTaskCreateResult)="startTaskCreateResult($event)"
-                (startTaskCreateArchive)="startTaskCreateArchive($event)">
-            </app-datatable-actions>
-
             <app-datatable
                 [dataset]="dataset | async"
                 [instance]="instance | async"
@@ -70,80 +100,6 @@
                 (deleteSelectedData)="deleteSearchData($event)"
                 (downloadFile)="downloadFile($event)">
             </app-datatable>
-
-            <!-- <div *ngIf="(instance | async).samp_enabled" class="jumbotron mb-4 py-4">
-                <div class="lead">
-                    <ng-container *ngIf="!(sampRegistered | async)">
-                        You are not connected to a SAMP-hub
-                        <button (click)="sampRegister()" class="btn btn-outline-primary">Try to register</button>
-                    </ng-container>
-                    <ng-container *ngIf="(sampRegistered | async)">
-                        You are connected to a SAMP-hub
-                        <button (click)="sampUnregister()" class="btn btn-outline-primary">Unregister</button>
-                    </ng-container>
-                </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"
-                [attributeList]="attributeList | async"
-                [criteriaList]="criteriaList | async"
-                [outputList]="outputList | async"
-                [coneSearch]="coneSearch | async"
-                [dataLength]="dataLength | async"
-                [sampRegistered]="sampRegistered | async"
-                (broadcast)="broadcastVotable($event)"
-                (startTaskCreateResult)="startTaskCreateResult($event)"
-                (startTaskCreateArchive)="startTaskCreateArchive($event)">
-            </app-download>
-            <app-reminder
-                [datasetSelected]="datasetSelected | async"
-                [datasetList]="datasetList | async"
-                [attributeList]="attributeList | async | sortByOutputDisplay"
-                [criteriaFamilyList]="criteriaFamilyList | async"
-                [outputFamilyList]="outputFamilyList | async"
-                [outputCategoryList]="outputCategoryList | async"
-                [criteriaList]="criteriaList | async"
-                [coneSearch]="coneSearch | async"
-                [outputList]="outputList | async">
-            </app-reminder>
-            <app-url-display
-                [datasetSelected]="datasetSelected | async"
-                [datasetList]="datasetList | async"
-                [criteriaList]="criteriaList | async"
-                [outputList]="outputList | async"
-                [coneSearch]="coneSearch | async">
-            </app-url-display>
-            <app-datatable-tab
-                [datasetSelected]="datasetSelected | async"
-                [instance]="instance | async"
-                [datasetList]="datasetList | async"
-                [attributeList]="attributeList | async | sortByOutputDisplay"
-                [criteriaList]="criteriaList | async"
-                [outputList]="outputList | async"
-                [coneSearch]="coneSearch | async"
-                [queryParams]="queryParams | async"
-                [dataLength]="dataLength | async"
-                [sampRegistered]="sampRegistered | async"
-                [data]="data | async"
-                [dataIsLoading]="dataIsLoading | async"
-                [dataIsLoaded]="dataIsLoaded | async"
-                [selectedData]="selectedData | async"
-                [imageList]="imageList | async"
-                [imageListIsLoading]="imageListIsLoading | async"
-                [imageListIsLoaded]="imageListIsLoaded | async"
-                (retrieveData)="retrieveData($event)"
-                (addSelectedData)="addSearchData($event)"
-                (deleteSelectedData)="deleteSearchData($event)"
-                (broadcast)="broadcastVotable($event)"
-                (startTaskCreateResult)="startTaskCreateResult($event)"
-                (startTaskCreateArchive)="startTaskCreateArchive($event)"
-                (downloadFile)="downloadFile($event)">
-            </app-datatable-tab> -->
         </ng-container>
     </div>
 </div>
diff --git a/client/src/app/instance/search/containers/result.component.ts b/client/src/app/instance/search/containers/result.component.ts
index 979bd1dca280e87454f1b182e83c1c3ad686c640..66d38e4ccb9c510d40fa71253455b40d393c2ef1 100644
--- a/client/src/app/instance/search/containers/result.component.ts
+++ b/client/src/app/instance/search/containers/result.component.ts
@@ -13,15 +13,15 @@ import { Store } from '@ngrx/store';
 import { Observable, Subscription } from 'rxjs';
 
 import { AbstractSearchComponent } from './abstract-search.component';
-import { Pagination, DownloadFile } from '../../store/models';
+import { Pagination } from '../../store/models';
 import { Instance, Dataset, Image } 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 'src/app/samp/samp.actions';
 import * as sampSelector from 'src/app/samp/samp.selector';
-import * as downloadFileActions from '../../store/actions/download-file.actions';
-import * as downloadFileSelector from '../../store/selectors/download-file.selector';
+import * as archiveActions from '../../store/actions/archive.actions';
+import * as archiveSelector from '../../store/selectors/archive.selector';
 import * as imageActions from 'src/app/metamodel/actions/image.actions';
 import * as imageSelector from 'src/app/metamodel/selectors/image.selector';
 import * as datasetSelector from 'src/app/metamodel/selectors/dataset.selector';
@@ -48,10 +48,10 @@ export class ResultComponent extends AbstractSearchComponent {
     public dataIsLoaded: Observable<boolean>;
     public selectedData: Observable<any>;
     public sampRegistered: Observable<boolean>;
-    public downloadedFiles: Observable<DownloadFile[]>;
     public imageList: Observable<Image[]>;
     public imageListIsLoading: Observable<boolean>;
     public imageListIsLoaded: Observable<boolean>;
+    public archiveIsCreating: Observable<boolean>;
     public pristineSubscription: Subscription;
 
     constructor(protected store: Store<{ }>) {
@@ -66,10 +66,10 @@ 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);
         this.imageList = this.store.select(imageSelector.selectAllImages);
         this.imageListIsLoading = this.store.select(imageSelector.selectImageListIsLoading);
         this.imageListIsLoaded = this.store.select(imageSelector.selectImageListIsLoaded);
+        this.archiveIsCreating = this.store.select(archiveSelector.selectArchiveIsCreating);
     }
 
     ngOnInit(): void {
@@ -136,30 +136,16 @@ export class ResultComponent extends AbstractSearchComponent {
         this.store.dispatch(searchActions.deleteSelectedData({ id }));
     }
 
-    /**
-     * Dispatches action to starts downloading file.
-     *
-     * @param  {url: string, fileId: string, datasetName: string, filename: string} download - Info about file to download
-     */
-    downloadFile(download: {url: string, fileId: string, datasetName: string, filename: string}): void {
-        this.store.dispatch(downloadFileActions.downloadFile(download));
-    }
-
-    /**
-     * Dispatches action to starts task create result and download
-     *
-     * @param string format - Info about result format
-     */
-    startTaskCreateResult(event: { format: string, selectedData: boolean, broadcastVo: boolean }) {
-        this.store.dispatch(downloadFileActions.startTaskCreateResult(event));
+    downloadFile(download: {url: string, filename: string}): void {
+        this.store.dispatch(searchActions.downloadFile(download));
     }
 
     /**
      * Dispatches action to starts task create archive and download
      *
      */
-    startTaskCreateArchive(event: { selectedData: boolean }) {
-        this.store.dispatch(downloadFileActions.startTaskCreateArchive(event));
+    startTaskCreateArchive(query: string) {
+        this.store.dispatch(archiveActions.startTaskCreateArchive({ query }));
     }
 
     /**
@@ -167,6 +153,7 @@ export class ResultComponent extends AbstractSearchComponent {
      */
     ngOnDestroy(): void {
         this.store.dispatch(searchActions.destroyResults());
+        this.store.dispatch(archiveActions.resetArchive());
         if (this.pristineSubscription) this.pristineSubscription.unsubscribe();
         super.ngOnDestroy();
     }
diff --git a/client/src/app/instance/store/actions/archive.actions.ts b/client/src/app/instance/store/actions/archive.actions.ts
new file mode 100644
index 0000000000000000000000000000000000000000..66275bcea8f51501166a9725e4c12b89f7e923b4
--- /dev/null
+++ b/client/src/app/instance/store/actions/archive.actions.ts
@@ -0,0 +1,18 @@
+/**
+ * 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 startTaskCreateArchive = createAction('[Archive] Start Task Create Archive', props<{ query: string }>());
+export const startTaskCreateArchiveSuccess = createAction('[Archive] Start Task Create Archive Success', props<{ fileId: string, filename: string, datasetName: string }>());
+export const startTaskCreateArchiveFail = createAction('[Archive] Start Task Create Archive Fail');
+export const isArchiveAvailable = createAction('[Archive] Is Archive Available', props<{ fileId: string, filename: string, datasetName: string }>());
+export const isArchiveAvailableSuccess = createAction('[Archive] Is Archive Available Success', props<{ url: string, filename: string }>());
+export const isArchiveAvailableFail = createAction('[Archive] Is Archive Available Fail');
+export const resetArchive = createAction('[Archive] Reset Archive');
\ No newline at end of file
diff --git a/client/src/app/instance/store/actions/download-file.actions.ts b/client/src/app/instance/store/actions/download-file.actions.ts
deleted file mode 100644
index 60fefb6f1cadd470066f5b081b2afcf553d23a93..0000000000000000000000000000000000000000
--- a/client/src/app/instance/store/actions/download-file.actions.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * 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 startTaskCreateResult = createAction('[File] Start Task Create Result', props<{ format: string, selectedData: boolean, broadcastVo: boolean }>());
-export const startTaskCreateResultSuccess = createAction('[File] Start Task Create Result Success', props<{ fileId: string, datasetName: string, filename: string, broadcastVo: boolean }>());
-export const startTaskCreateResultFail = createAction('[File] Start Task Create Result Fail');
-export const isResultAvailable = createAction('[File] Is Result Available', props<{ fileId: string, datasetName: string, filename: string, broadcastVo: boolean }>());
-export const isResultAvailableFail = createAction('[File] Is Result Available Fail');
-
-export const startTaskCreateArchive = createAction('[File] Start Task Create Archive', props<{ selectedData: boolean }>());
-export const startTaskCreateArchiveSuccess = createAction('[File] Start Task Create Archive Success', props<{ fileId: string, datasetName: string, filename: string }>());
-export const startTaskCreateArchiveFail = createAction('[File] Start Task Create Archive Fail');
-export const isArchiveAvailable = createAction('[File] Is Archive Available', props<{ fileId: string, datasetName: string, filename: string }>());
-export const isArchiveAvailableFail = createAction('[File] Is Archive Available Fail');
-
-export const downloadFile = createAction('[File] Download File', props<{ url: string, fileId: string, datasetName: string, filename: string }>());
-
-export const startsDownloadingFile = createAction('[File] Starts Downloading File', props<{ url: string, filename: string, fileId: string }>());
-export const updateDownloadProgress = createAction('[File] Update Download Progress', props<{ progress: number, fileId: string }>());
-export const fileDownloaded = createAction('[File] File Downloaded', props<{ fileId: string }>());
diff --git a/client/src/app/instance/store/actions/search.actions.ts b/client/src/app/instance/store/actions/search.actions.ts
index b3fe3a958d3326a647afe189347b4ff8c645040b..86333f653f87a2d19ab33f8ed3e092e20654105c 100644
--- a/client/src/app/instance/store/actions/search.actions.ts
+++ b/client/src/app/instance/store/actions/search.actions.ts
@@ -34,3 +34,4 @@ export const retrieveDataSuccess = createAction('[Search] Retrieve Data Success'
 export const retrieveDataFail = createAction('[Search] Retrieve Data Fail');
 export const addSelectedData = createAction('[Search] Add Selected Data', props<{ id: number | string }>());
 export const deleteSelectedData = createAction('[Search] Delete Selected Data', props<{ id: number | string }>());
+export const downloadFile = createAction('[Search] Download File', props<{ url: string, filename: string }>());
diff --git a/client/src/app/instance/store/effects/archive.effects.ts b/client/src/app/instance/store/effects/archive.effects.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4ce9973e4d576eff539f20f95567d4a6e3e53076
--- /dev/null
+++ b/client/src/app/instance/store/effects/archive.effects.ts
@@ -0,0 +1,123 @@
+/**
+ * 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 { Actions, createEffect, ofType, concatLatestFrom } from '@ngrx/effects';
+import { Store } from '@ngrx/store';
+import { of, timer, mapTo, takeUntil, Subject } from 'rxjs';
+import { map, tap, mergeMap, switchMap, catchError } from 'rxjs/operators';
+import { ToastrService } from 'ngx-toastr';
+
+import { ArchiveService } from '../services/archive.service';
+import * as archiveActions from '../actions/archive.actions';
+import * as searchSelector from '../selectors/search.selector';
+import * as searchActions from '../actions/search.actions';
+import { AppConfigService } from 'src/app/app-config.service';
+import { getHost } from 'src/app/shared/utils';
+
+/**
+ * @class
+ * @classdesc File effects.
+ */
+@Injectable()
+export class ArchiveEffects {
+    startTaskCreateArchive$ = createEffect(() =>
+        this.actions$.pipe(
+            ofType(archiveActions.startTaskCreateArchive),
+            concatLatestFrom(() => this.store.select(searchSelector.selectCurrentDataset)),
+            mergeMap(([action, datasetName]) => {
+                return this.archiveService.startTaskCreateArchive(action.query)
+                    .pipe(
+                        map((response) => archiveActions.startTaskCreateArchiveSuccess({
+                            fileId: response.archive_id,
+                            filename: response.archive_name,
+                            datasetName
+                        })),
+                        catchError(() => of(archiveActions.startTaskCreateArchiveFail()))
+                    )
+            })
+        )
+    );
+
+    startTaskCreateArchiveSuccess$ = createEffect(() =>
+        this.actions$.pipe(
+            ofType(archiveActions.startTaskCreateArchiveSuccess),
+            tap(action => this.kill$ = new Subject()),
+            switchMap(action => timer(0, 1000)
+                .pipe(
+                    mapTo(archiveActions.isArchiveAvailable(action)),
+                    takeUntil(this.kill$)
+                )
+            )
+        )
+    );
+
+    startTaskCreateArchiveFail$ = createEffect(() =>
+        this.actions$.pipe(
+            ofType(archiveActions.startTaskCreateArchiveFail),
+            tap(() => this.toastr.error('The creation of the archive file failed', 'Start async task failed'))
+        ), { dispatch: false}
+    );
+
+    isArchiveAvailable$ = createEffect(() =>
+        this.actions$.pipe(
+            ofType(archiveActions.isArchiveAvailable),
+            switchMap(action => this.archiveService.isArchiveAvailable(action.fileId)
+                .pipe(
+                    map(result => {
+                        if (result.archive_is_available) {
+                            this.kill$.next({});
+                            this.kill$.unsubscribe();
+
+                            return archiveActions.isArchiveAvailableSuccess({
+                                url: `${getHost(this.config.apiUrl)}/download-archive/${action.datasetName}/${action.fileId}`,
+                                filename: action.filename
+                            });
+                        } else {
+                            return { type: '[No Action] Is Archive Available' };
+                        }
+                    }),
+                    catchError(() => of(archiveActions.isArchiveAvailableFail()))
+                )
+            )
+        )
+    );
+
+    isArchiveAvailableSuccess$ = createEffect(() =>
+        this.actions$.pipe(
+            ofType(archiveActions.isArchiveAvailableSuccess),
+            map(action => searchActions.downloadFile(action))
+        )
+    );
+
+    isArchiveAvailableFail$ = createEffect(() =>
+        this.actions$.pipe(
+            ofType(archiveActions.isArchiveAvailableFail),
+            tap(() => this.toastr.error('The creation of the archive has encountered a problem', 'Archive result download failed'))
+        ), { dispatch: false}
+    );
+
+    resetArchive$ = createEffect(() =>
+        this.actions$.pipe(
+            ofType(archiveActions.resetArchive),
+            tap(() => this.kill$.unsubscribe())
+        ), { dispatch: false}
+    );
+
+    private kill$: Subject<{}>;
+
+    constructor(
+        private actions$: Actions,
+        private archiveService: ArchiveService,
+        private store: Store<{ }>,
+        private toastr: ToastrService,
+        private config: AppConfigService
+    ) {}
+}
diff --git a/client/src/app/instance/store/effects/download-file.effects.ts b/client/src/app/instance/store/effects/download-file.effects.ts
deleted file mode 100644
index 437e0eb6e040315479695f9743e0cd271290385e..0000000000000000000000000000000000000000
--- a/client/src/app/instance/store/effects/download-file.effects.ts
+++ /dev/null
@@ -1,272 +0,0 @@
-/**
- * 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, timer, mapTo, takeUntil, Subject } from 'rxjs';
-import { map, tap, mergeMap, switchMap, catchError } from 'rxjs/operators';
-import { ToastrService } from 'ngx-toastr';
-
-import { AppConfigService } from 'src/app/app-config.service';
-import { DownloadFileService } from '../services/download-file.service';
-import * as downloadFileActions from '../actions/download-file.actions';
-import * as sampActions from 'src/app/samp/samp.actions';
-import * as searchSelector from '../selectors/search.selector';
-import * as attributeSelector from 'src/app/metamodel/selectors/attribute.selector';
-import * as coneSearchSelector from '../selectors/cone-search.selector';
-
-/**
- * @class
- * @classdesc File effects.
- */
-@Injectable()
-export class DownloadFileEffects {
-    startTaskCreateResult$ = createEffect(() =>
-        this.actions$.pipe(
-            ofType(downloadFileActions.startTaskCreateResult),
-            concatLatestFrom(() => [
-                this.store.select(searchSelector.selectCurrentDataset),
-                this.store.select(attributeSelector.selectAllAttributes),
-                this.store.select(searchSelector.selectCriteriaListByRoute),
-                this.store.select(coneSearchSelector.selectConeSearchByRoute),
-                this.store.select(searchSelector.selectOutputListByRoute),
-                this.store.select(searchSelector.selectSelectedData)
-            ]),
-            mergeMap(([action, currentDataset, attributeList, criteriaList, coneSearch, outputList, selectedData]) => {
-                const attributeId = attributeList.find(a => a.primary_key);
-                let query: string = `${currentDataset}?a=${outputList}`;
-                if (criteriaList) {
-                    query += `&c=${criteriaList}`;
-                    if (selectedData && action.selectedData) {
-                        query += `;${attributeId.id}::in::${selectedData.join('|')}`;
-                    }
-                } else if (selectedData && action.selectedData) {
-                    query += `&c=${attributeId.id}::in::${selectedData.join('|')}`;
-                }
-                if (coneSearch) {
-                    query += `&cs=${coneSearch}`;
-                }
-                query += `&f=${action.format}`;
-                
-                return this.downloadFileService.startTaskCreateResult(query)
-                    .pipe(
-                        map((response) => downloadFileActions.startTaskCreateResultSuccess({
-                            fileId: response.file_id,
-                            filename: response.file_name,
-                            datasetName: currentDataset,
-                            broadcastVo: action.broadcastVo
-                        })),
-                        catchError(() => of(downloadFileActions.startTaskCreateResultFail()))
-                    )
-            })
-        )
-    );
-
-    startTaskCreateResultSuccess$ = createEffect(() =>
-        this.actions$.pipe(
-            ofType(downloadFileActions.startTaskCreateResultSuccess),
-            tap(action => this.kill$[action.fileId] = new Subject()),
-            switchMap(action => timer(0, 1000)
-                .pipe(
-                    mapTo(downloadFileActions.isResultAvailable(action)),
-                    takeUntil(this.kill$[action.fileId])
-                )
-            )
-        )
-    );
-
-    startTaskCreateResultFail$ = createEffect(() =>
-        this.actions$.pipe(
-            ofType(downloadFileActions.startTaskCreateResultFail),
-            tap(() => this.toastr.error('The creation of the result file failed', 'Start async task failed'))
-        ), { dispatch: false}
-    );
-
-    isResultAvailable$ = createEffect(() =>
-        this.actions$.pipe(
-            ofType(downloadFileActions.isResultAvailable),
-            switchMap(action => this.downloadFileService.isResultAvailable(action.fileId)
-                .pipe(
-                    switchMap(result => {
-                        if (result.file_is_available) {
-                            this.kill$[action.fileId].next({});
-                            this.kill$[action.fileId].unsubscribe();
-                            if (action.broadcastVo) {
-                                return [
-                                    sampActions.broadcastVotable({
-                                        url: `${this.config.apiUrl}/download-result/${action.datasetName}/${action.fileId}`
-                                    }),
-                                    downloadFileActions.fileDownloaded({ fileId: action.fileId })
-                                ];
-                            } else {
-                                return [
-                                    downloadFileActions.startsDownloadingFile({
-                                        fileId: action.fileId,
-                                        filename: action.filename,
-                                        url: `${this.config.apiUrl}/download-result/${action.datasetName}/${action.fileId}`
-                                    })
-                                ];
-                            }
-                        } else {
-                            return [{ type: '[No Action] Is Result Available' }];
-                        }
-                    }),
-                    catchError(() => of(downloadFileActions.isResultAvailableFail()))
-                )
-            )
-        )
-    );
-
-    isResultAvailableFail$ = createEffect(() =>
-        this.actions$.pipe(
-            ofType(downloadFileActions.isResultAvailableFail),
-            tap(() => this.toastr.error('The creation of the file has encountered a problem', 'File result download failed'))
-        ), { dispatch: false}
-    );
-
-    startTaskCreateArchive$ = createEffect(() =>
-        this.actions$.pipe(
-            ofType(downloadFileActions.startTaskCreateArchive),
-            concatLatestFrom(() => [
-                this.store.select(searchSelector.selectCurrentDataset),
-                this.store.select(attributeSelector.selectAllAttributes),
-                this.store.select(searchSelector.selectCriteriaListByRoute),
-                this.store.select(coneSearchSelector.selectConeSearchByRoute),
-                this.store.select(searchSelector.selectOutputListByRoute),
-                this.store.select(searchSelector.selectSelectedData)
-            ]),
-            mergeMap(([action, currentDataset, attributeList, criteriaList, coneSearch, outputList, selectedData]) => {
-                const attributeId = attributeList.find(a => a.primary_key);
-                let query: string = `${currentDataset}?a=${outputList}`;
-                if (criteriaList) {
-                    query += `&c=${criteriaList}`;
-                    if (selectedData && action.selectedData) {
-                        query += `;${attributeId.id}::in::${selectedData.join('|')}`;
-                    }
-                } else if (selectedData && action.selectedData) {
-                    query += `&c=${attributeId.id}::in::${selectedData.join('|')}`;
-                }
-                if (coneSearch) {
-                    query += `&cs=${coneSearch}`;
-                }
-                
-                return this.downloadFileService.startTaskCreateArchive(query)
-                    .pipe(
-                        map((response) => downloadFileActions.startTaskCreateArchiveSuccess({
-                            fileId: response.archive_id,
-                            filename: response.archive_name,
-                            datasetName: currentDataset
-                        })),
-                        catchError(() => of(downloadFileActions.startTaskCreateArchiveFail()))
-                    )
-            })
-        )
-    );
-
-    startTaskCreateArchiveSuccess$ = createEffect(() =>
-        this.actions$.pipe(
-            ofType(downloadFileActions.startTaskCreateArchiveSuccess),
-            tap(action => this.kill$[action.fileId] = new Subject()),
-            switchMap(action => timer(0, 1000)
-                .pipe(
-                    mapTo(downloadFileActions.isArchiveAvailable(action)),
-                    takeUntil(this.kill$[action.fileId])
-                )
-            )
-        )
-    );
-
-    startTaskCreateArchiveFail$ = createEffect(() =>
-        this.actions$.pipe(
-            ofType(downloadFileActions.startTaskCreateArchiveFail),
-            tap(() => this.toastr.error('The creation of the archive file failed', 'Start async task failed'))
-        ), { dispatch: false}
-    );
-
-    isArchiveAvailable$ = createEffect(() =>
-        this.actions$.pipe(
-            ofType(downloadFileActions.isArchiveAvailable),
-            switchMap(action => this.downloadFileService.isArchiveAvailable(action.fileId)
-                .pipe(
-                    map(result => {
-                        if (result.archive_is_available) {
-                            this.kill$[action.fileId].next({});
-                            this.kill$[action.fileId].unsubscribe();
-                            return downloadFileActions.startsDownloadingFile({
-                                fileId: action.fileId,
-                                filename: action.filename,
-                                url: `${this.config.apiUrl}/download-archive/${action.datasetName}/${action.fileId}`
-                            });
-                        } else {
-                            return { type: '[No Action] Is Archive Available' };
-                        }
-                    }),
-                    catchError(() => of(downloadFileActions.isArchiveAvailableFail()))
-                )
-            )
-        )
-    );
-
-    isArchiveAvailableFail$ = createEffect(() =>
-        this.actions$.pipe(
-            ofType(downloadFileActions.isArchiveAvailableFail),
-            tap(() => this.toastr.error('The creation of the archive has encountered a problem', 'Archive result download failed'))
-        ), { dispatch: false}
-    );
-
-    downloadFile$ = createEffect(() =>
-        this.actions$.pipe(
-            ofType(downloadFileActions.downloadFile),
-            map(action => downloadFileActions.startsDownloadingFile({
-                url: action.url,
-                fileId: action.fileId,
-                filename: action.filename
-            }))
-        )
-    );
-
-    /**
-     * 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),
-                                fileId: action.fileId
-                            }));
-                        }
-
-                        if (event.type === HttpEventType.Response) {
-                            this.store.dispatch(downloadFileActions.fileDownloaded({ fileId: action.fileId }));
-                            this.downloadFileService.saveDownloadedFile(event.body as Blob, action.filename);
-                        }
-                    })
-                )
-            )
-        ), { dispatch: false }
-    );
-
-    private kill$ = [];
-
-    constructor(
-        private actions$: Actions,
-        private downloadFileService: DownloadFileService,
-        private store: Store<{ }>,
-        private toastr: ToastrService,
-        private config: AppConfigService
-    ) {}
-}
diff --git a/client/src/app/instance/store/effects/index.ts b/client/src/app/instance/store/effects/index.ts
index ddaa3d86df6a14dc0dababa0aa9fafafd4dad401..54904d02d268a79b5399d5e74f70dc83bdb5c542 100644
--- a/client/src/app/instance/store/effects/index.ts
+++ b/client/src/app/instance/store/effects/index.ts
@@ -3,7 +3,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';
+import { ArchiveEffects } from './archive.effects';
 
 export const instanceEffects = [
     SearchEffects,
@@ -11,5 +11,5 @@ export const instanceEffects = [
     ConeSearchEffects,
     DetailEffects,
     SvomJsonKwEffects,
-    DownloadFileEffects
+    ArchiveEffects
 ];
diff --git a/client/src/app/instance/store/effects/search.effects.ts b/client/src/app/instance/store/effects/search.effects.ts
index 157f1b275b37819acf76c9e0495d81e2b19ba250..9516c082b3af6a4a318ad23cd7c6f458bf8bb16e 100644
--- a/client/src/app/instance/store/effects/search.effects.ts
+++ b/client/src/app/instance/store/effects/search.effects.ts
@@ -14,6 +14,8 @@ import { Store, Action  } from '@ngrx/store';
 import { of } from 'rxjs';
 import { map, tap, mergeMap, catchError } from 'rxjs/operators';
 import { ToastrService } from 'ngx-toastr';
+import { KeycloakService } from 'keycloak-angular';
+import FileSaver from 'file-saver';
 
 import { ConeSearch, criterionToString, stringToCriterion } from '../models';
 import { SearchService } from '../services/search.service';
@@ -27,6 +29,7 @@ import * as datasetSelector from 'src/app/metamodel/selectors/dataset.selector';
 import * as searchSelector from '../selectors/search.selector';
 import * as coneSearchActions from '../actions/cone-search.actions';
 import * as coneSearchSelector from '../selectors/cone-search.selector';
+import * as authSelector from 'src/app/auth/auth.selector';
 
 /**
  * @class
@@ -249,10 +252,31 @@ export class SearchEffects {
         ), { dispatch: false}
     );
 
+    downloadFile$ = createEffect(() =>
+        this.actions$.pipe(
+            ofType(searchActions.downloadFile),
+            concatLatestFrom(() => this.store.select(authSelector.selectIsAuthenticated)),
+            tap(([action, isAuthenticated]) => {
+                if (isAuthenticated) {
+                    this.keycloak.getToken().then(token => {
+                        let separator = '?';
+                        if (action.url.indexOf('?') > -1) {
+                            separator = '&';
+                        }
+                        FileSaver.saveAs(`${action.url}${separator}token=${token}`, action.filename);
+                    });
+                } else {
+                    FileSaver.saveAs(action.url, action.filename);
+                }
+            })
+        ), { dispatch: false}
+    );
+
     constructor(
         private actions$: Actions,
         private searchService: SearchService,
         private store: Store<{ }>,
-        private toastr: ToastrService
+        private toastr: ToastrService,
+        private keycloak: KeycloakService,
     ) {}
 }
diff --git a/client/src/app/instance/store/models/download-file.model.ts b/client/src/app/instance/store/models/download-file.model.ts
deleted file mode 100644
index 0d21ff574b88548eb8067f65b2b617377bba56f9..0000000000000000000000000000000000000000
--- a/client/src/app/instance/store/models/download-file.model.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-/**
- * 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 {
-    id: string;
-    datasetName: string;
-    fileName: 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 3b2241bcfbdd3629726f8766f5220e0b74b9ccf1..67be1059cf6ae8f9f0530f91f275da1cf1252ae6 100644
--- a/client/src/app/instance/store/models/index.ts
+++ b/client/src/app/instance/store/models/index.ts
@@ -8,4 +8,3 @@ export * from './resolver.model';
 export * from './search-multiple-dataset-length';
 export * from './search-multiple-dataset-data';
 export * from './svom-keyword.model';
-export * from './download-file.model';
diff --git a/client/src/app/instance/store/reducers/archive.reducer.ts b/client/src/app/instance/store/reducers/archive.reducer.ts
new file mode 100644
index 0000000000000000000000000000000000000000..05400b0dd047c325445dfdf700d13c62d00c11ad
--- /dev/null
+++ b/client/src/app/instance/store/reducers/archive.reducer.ts
@@ -0,0 +1,42 @@
+/**
+ * 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 * as archiveActions from '../actions/archive.actions';
+
+/**
+ * Interface for file state.
+ *
+ * @interface State
+ */
+export interface State {
+    archiveIsCreating: boolean;
+}
+
+export const initialState: State = {
+    archiveIsCreating: false
+};
+
+export const archiveReducer = createReducer(
+    initialState,
+    on(archiveActions.startTaskCreateArchiveSuccess, state => ({
+        ...state,
+        archiveIsCreating: true
+    })),
+    on(archiveActions.isArchiveAvailableSuccess, state => ({
+        ...state,
+        archiveIsCreating: false
+    })),
+    on(archiveActions.resetArchive, () => ({
+        ...initialState
+    }))
+);
+
+export const selectArchiveIsCreating = (state: State) => state.archiveIsCreating;
diff --git a/client/src/app/instance/store/reducers/download-file.reducer.ts b/client/src/app/instance/store/reducers/download-file.reducer.ts
deleted file mode 100644
index 372e24a9c543725a6aa317d31a5ac80f449892d0..0000000000000000000000000000000000000000
--- a/client/src/app/instance/store/reducers/download-file.reducer.ts
+++ /dev/null
@@ -1,84 +0,0 @@
-/**
- * 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.startTaskCreateResultSuccess, (state, {fileId, datasetName, filename }) => ({
-        ...state,
-        downloadedFiles: [...state.downloadedFiles, {
-            id: fileId,
-            datasetName,
-            fileName: filename,
-            state: 'PENDING',
-            progress: 0
-        }]
-    })),
-    on(downloadFileActions.startTaskCreateArchiveSuccess, (state, {fileId, datasetName, filename }) => ({
-        ...state,
-        downloadedFiles: [...state.downloadedFiles, {
-            id: fileId,
-            datasetName,
-            fileName: filename,
-            state: 'PENDING',
-            progress: 0
-        }]
-    })),
-    on(downloadFileActions.downloadFile, (state, {fileId, datasetName, filename }) => ({
-        ...state,
-        downloadedFiles: [...state.downloadedFiles, {
-            id: fileId,
-            datasetName,
-            fileName: filename,
-            state: 'PENDING',
-            progress: 0
-        }]
-    })),
-    on(downloadFileActions.updateDownloadProgress, (state, { progress, fileId }) => ({
-        ...state,
-        downloadedFiles: [
-            ...state.downloadedFiles.filter(f => f.id !== fileId),
-            {
-                ...state.downloadedFiles.find(f => f.id === fileId),
-                progress: progress,
-                state: 'IN_PROGRESS'
-            }
-        ]
-    })),
-    on(downloadFileActions.fileDownloaded, (state, { fileId }) => ({
-        ...state,
-        downloadedFiles: [
-            ...state.downloadedFiles.filter(f => f.id !== fileId),
-            {
-                ...state.downloadedFiles.find(f => f.id === fileId),
-                state: 'DONE',
-                progress: 100
-            }
-        ]
-    }))
-);
-
-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/archive.selector.ts
similarity index 55%
rename from client/src/app/instance/store/selectors/download-file.selector.ts
rename to client/src/app/instance/store/selectors/archive.selector.ts
index b955521d86de25bbf5c6eef631a4b77e4f2bd982..fbcf2238778109a69a81f211c0297ddc14c5dcd5 100644
--- a/client/src/app/instance/store/selectors/download-file.selector.ts
+++ b/client/src/app/instance/store/selectors/archive.selector.ts
@@ -10,14 +10,14 @@
 import { createSelector } from '@ngrx/store';
 
 import * as reducer from '../../instance.reducer';
-import * as fromDownloadFile from '../reducers/download-file.reducer';
+import * as fromArchive from '../reducers/archive.reducer';
 
-export const selectDownloadFileState = createSelector(
+export const selectArchiveState = createSelector(
     reducer.getInstanceState,
-    (state: reducer.State) => state.downloadFile
+    (state: reducer.State) => state.archive
 );
 
-export const selectDownloadedFiles = createSelector(
-    selectDownloadFileState,
-    fromDownloadFile.selectDownloadedFiles
+export const selectArchiveIsCreating = createSelector(
+    selectArchiveState,
+    fromArchive.selectArchiveIsCreating
 );
diff --git a/client/src/app/instance/store/services/archive.service.ts b/client/src/app/instance/store/services/archive.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fc7c948477f5ed0b1c19f6aef0683f21f6cf4676
--- /dev/null
+++ b/client/src/app/instance/store/services/archive.service.ts
@@ -0,0 +1,26 @@
+/**
+ * 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 } from '@angular/common/http';
+
+import { AppConfigService } from 'src/app/app-config.service';
+
+@Injectable({providedIn: 'root'})
+export class ArchiveService {
+    constructor(private http: HttpClient, private config: AppConfigService) { }
+
+    startTaskCreateArchive(query: string) {
+        return this.http.get<{"archive_name": string, "archive_id": string}>(`${this.config.apiUrl}/start-task-create-archive/${query}`);
+    }
+
+    isArchiveAvailable(id: string) {
+        return this.http.get<{"archive_is_available": boolean}>(`${this.config.apiUrl}/is-archive-available/${id}`);
+    }
+}
\ No newline at end of file
diff --git a/client/src/app/instance/store/services/download-file.service.ts b/client/src/app/instance/store/services/download-file.service.ts
deleted file mode 100644
index 25c26182e9f69d10ee75b025fde4cbed781d3a91..0000000000000000000000000000000000000000
--- a/client/src/app/instance/store/services/download-file.service.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- * 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';
-
-import { AppConfigService } from 'src/app/app-config.service';
-
-@Injectable({providedIn: 'root'})
-export class DownloadFileService {
-    constructor(private http: HttpClient, private config: AppConfigService) { }
-
-    /**
-     * Returns file name and file ID
-     *
-     * @param  {string} query - The query.
-     *
-     * @return Observable<{"file_name": string, "file_id": string}>
-     */
-    startTaskCreateResult(query: string) {
-        return this.http.get<{"file_name": string, "file_id": string}>(`${this.config.apiUrl}/start-task-create-result/${query}`);
-    }
-
-    isResultAvailable(id: string) {
-        return this.http.get<{"file_is_available": boolean}>(`${this.config.apiUrl}/is-result-available/${id}`);
-    }
-
-    startTaskCreateArchive(query: string) {
-        return this.http.get<{"archive_name": string, "archive_id": string}>(`${this.config.apiUrl}/start-task-create-archive/${query}`);
-    }
-
-    isArchiveAvailable(id: string) {
-        return this.http.get<{"archive_is_available": boolean}>(`${this.config.apiUrl}/is-archive-available/${id}`);
-    }
-
-    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');
-        const url = window.URL.createObjectURL(body);
-        downloadLink.href = url;
-        downloadLink.setAttribute('download', filename);
-        downloadLink.click();
-        window.URL.revokeObjectURL(url);
-    }
-}
\ 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 4f4c2847d6d9b98de06b4daef5509ffb8e82a3ba..6ea85f7ae67883198ce83ba02b1e902d97d7e002 100644
--- a/client/src/app/instance/store/services/index.ts
+++ b/client/src/app/instance/store/services/index.ts
@@ -2,12 +2,12 @@ import { SearchService } from './search.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';
+import { ArchiveService } from './archive.service';
 
 export const instanceServices = [
     SearchService,
     ConeSearchService,
     DetailService,
     SvomJsonKwService,
-    DownloadFileService
+    ArchiveService
 ];
diff --git a/client/src/app/samp/samp.actions.ts b/client/src/app/samp/samp.actions.ts
index eb290e47161431a877a1b76dd5f99d298f2be7a4..bb4b36ab3931e7786d1fd3b4fe3666b4b697b15e 100644
--- a/client/src/app/samp/samp.actions.ts
+++ b/client/src/app/samp/samp.actions.ts
@@ -14,3 +14,4 @@ export const registerSuccess = createAction('[Samp] Register Success');
 export const registerFail = createAction('[Samp] Register Fail');
 export const unregister = createAction('[Samp] Unregister');
 export const broadcastVotable = createAction('[Samp] Broadcast Votable', props<{ url: string }>());
+export const broadcastImage = createAction('[Samp] Broadcast Image', props<{ url: string }>());
diff --git a/client/src/app/samp/samp.effects.ts b/client/src/app/samp/samp.effects.ts
index 2db3bc713e9c4a035757d7c63310494503c396ef..59327391c5c72a021aa8d86117389915a8f3fdda 100644
--- a/client/src/app/samp/samp.effects.ts
+++ b/client/src/app/samp/samp.effects.ts
@@ -87,7 +87,7 @@ export class SampEffects {
             tap(([action, isAuthenticated]) => {
                 if (isAuthenticated) {
                     this.keycloak.getToken().then(token => {
-                        this.sampService.broadcast('table.load.votable', `${action.url}?token=${token}`);
+                        this.sampService.broadcast('table.load.votable', `${action.url}&token=${token}`);
                     });
                 } else {
                     this.sampService.broadcast('table.load.votable', action.url);
@@ -97,6 +97,26 @@ export class SampEffects {
         { dispatch: false }
     );
 
+    /**
+     * Calls actions to broadcast image
+     */
+    broadcastImage$ = createEffect(() =>
+        this.actions$.pipe(
+            ofType(sampActions.broadcastImage),
+            concatLatestFrom(() => this.store.select(authSelector.selectIsAuthenticated)),
+            tap(([action, isAuthenticated]) => {
+                if (isAuthenticated) {
+                    this.keycloak.getToken().then(token => {
+                        this.sampService.broadcast('image.load.fits', `${action.url}&token=${token}`);
+                    });
+                } else {
+                    this.sampService.broadcast('image.load.fits', action.url);
+                }
+            })
+        ),
+        { dispatch: false }
+    );
+
     constructor(
         private actions$: Actions,
         private sampService: SampService,
diff --git a/client/yarn.lock b/client/yarn.lock
index 7aa60ec7ff6641d3e5f1a3656096d066accfb8e2..6418171403d03c5fd92b58ce44d9bfc89db43592 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -2262,6 +2262,11 @@
     "@types/qs" "*"
     "@types/serve-static" "*"
 
+"@types/file-saver@^2.0.5":
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/@types/file-saver/-/file-saver-2.0.5.tgz#9ee342a5d1314bb0928375424a2f162f97c310c7"
+  integrity sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ==
+
 "@types/geojson@*":
   version "7946.0.8"
   resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.8.tgz#30744afdb385e2945e22f3b033f897f76b1f12ca"
@@ -4608,6 +4613,11 @@ figures@^3.0.0:
   dependencies:
     escape-string-regexp "^1.0.5"
 
+file-saver@^2.0.5:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-2.0.5.tgz#d61cfe2ce059f414d899e9dd6d4107ee25670c38"
+  integrity sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==
+
 fill-range@^7.0.1:
   version "7.0.1"
   resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
diff --git a/server/app/dependencies.php b/server/app/dependencies.php
index 366ed5022924150380976ea3f51456c67f120001..34f5e152b1eb36009e85f66a750802a411715db7 100644
--- a/server/app/dependencies.php
+++ b/server/app/dependencies.php
@@ -237,18 +237,6 @@ $container->set('App\Action\SearchAction', function (ContainerInterface $c) {
     );
 });
 
-$container->set('App\Action\StartTaskCreateResultAction', function (ContainerInterface $c) {
-    return new App\Action\StartTaskCreateResultAction($c->get('em'), $c->get('rmq'), $c->get(SETTINGS)['token']);
-});
-
-$container->set('App\Action\IsResultAvailableAction', function (ContainerInterface $c) {
-    return new App\Action\IsResultAvailableAction($c->get('em'), $c->get('settings')['data_path'], $c->get('settings')['result_folder']);
-});
-
-$container->set('App\Action\DownloadResultAction', function (ContainerInterface $c) {
-    return new App\Action\DownloadResultAction($c->get('em'), $c->get('settings')['data_path'], $c->get('settings')['result_folder'], $c->get(SETTINGS)['token']);
-});
-
 $container->set('App\Action\StartTaskCreateArchiveAction', function (ContainerInterface $c) {
     return new App\Action\StartTaskCreateArchiveAction($c->get('em'), $c->get('rmq'), $c->get(SETTINGS)['token']);
 });
diff --git a/server/app/routes.php b/server/app/routes.php
index b4e79e38125c50084438d6f0dff456ba8e19f5a9..19aa875e63d1ba24dda5af9644e69cf9ce82eec2 100644
--- a/server/app/routes.php
+++ b/server/app/routes.php
@@ -78,11 +78,6 @@ $app->group('', function (RouteCollectorProxy $group) {
 // Search actions
 $app->get('/search/{dname}', App\Action\SearchAction::class);
 
-// Result actions
-$app->get('/start-task-create-result/{dname}', App\Action\StartTaskCreateResultAction::class);
-$app->get('/is-result-available/{id}', App\Action\IsResultAvailableAction::class);
-$app->get('/download-result/{dname}/{id}', App\Action\DownloadResultAction::class);
-
 // Archive actions
 $app->get('/start-task-create-archive/{dname}', App\Action\StartTaskCreateArchiveAction::class);
 $app->get('/is-archive-available/{id}', App\Action\IsArchiveAvailableAction::class);
diff --git a/server/src/Action/DownloadResultAction.php b/server/src/Action/DownloadResultAction.php
deleted file mode 100644
index 41a996c7925976d00b187cfc9def5591bab759fb..0000000000000000000000000000000000000000
--- a/server/src/Action/DownloadResultAction.php
+++ /dev/null
@@ -1,133 +0,0 @@
-<?php
-
-/*
- * This file is part of Anis Server.
- *
- * (c) 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.
- */
-declare(strict_types=1);
-
-namespace App\Action;
-
-use Psr\Http\Message\ServerRequestInterface;
-use Psr\Http\Message\ResponseInterface;
-use Doctrine\ORM\EntityManagerInterface;
-use Slim\Exception\HttpNotFoundException;
-use Nyholm\Psr7\Factory\Psr17Factory;
-
-/**
- * @author François Agneray <francois.agneray@lam.fr>
- * @package App\Action
- */
-final class DownloadResultAction extends AbstractAction
-{
-    /**
-     * Contains anis-server data path
-     *
-     * @var string
-     */
-    private $dataPath;
-
-    /**
-     * Contains result folder path
-     *
-     * @var array
-     */
-    private $resultFolder;
-
-    /**
-     * Contains settings to handle Json Web Token
-     *
-     * @var array
-     */
-    private $settings;
-
-    /**
-     * Create the classe before call __invoke to execute the action
-     *
-     * @param EntityManagerInterface $em           Doctrine Entity Manager Interface
-     * @param string                 $dataPath     Contains anis-server data path
-     * @param string                 $resultFolder Contains result folder path
-     * @param array                  $settings     Settings about token
-     */
-    public function __construct(EntityManagerInterface $em, string $dataPath, string $resultFolder, array $settings)
-    {
-        parent::__construct($em);
-        $this->dataPath = $dataPath;
-        $this->resultFolder = $resultFolder;
-        $this->settings = $settings;
-    }
-
-    /**
-     * `GET` Returns the file found
-     *
-     * @param  ServerRequestInterface $request  PSR-7 This object represents the HTTP request
-     * @param  ResponseInterface      $response PSR-7 This object represents the HTTP response
-     * @param  string[]               $args     This table contains information transmitted in the URL (see routes.php)
-     *
-     * @return ResponseInterface
-     */
-    public function __invoke(
-        ServerRequestInterface $request,
-        ResponseInterface $response,
-        array $args
-    ): ResponseInterface {
-        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 instance is private and authorization enabled
-        $instance = $dataset->getDatasetFamily()->getInstance();
-        if (!$instance->getPublic() && boolval($this->settings['enabled'])) {
-            $this->verifyInstanceAuthorization(
-                $request,
-                $instance->getName(),
-                explode(',', $this->settings['admin_roles'])
-            );
-        }
-
-        // If dataset is private and authorization enabled
-        if (!$dataset->getPublic() && boolval($this->settings['enabled'])) {
-            $this->verifyDatasetAuthorization(
-                $request,
-                $dataset->getName(),
-                explode(',', $this->settings['admin_roles'])
-            );
-        }
-
-        // Search the file
-        $fileId = $args['id'];
-        $filePath = $this->dataPath . $this->resultFolder . '/' . $fileId;
-
-        // If the file not found 404
-        if (!file_exists($filePath)) {
-            throw new HttpNotFoundException(
-                $request,
-                'Result file with name ' . $fileId . ' is not found'
-            );
-        }
-
-        // If the file found so stream it
-        $psr17Factory = new Psr17Factory();
-        $stream = $psr17Factory->createStreamFromFile($filePath, 'r');
-
-        return $response->withBody($stream)
-            ->withHeader('Content-Disposition', 'attachment; filename=' . basename($filePath) . ';')
-            ->withHeader('Content-Type', mime_content_type($filePath))
-            ->withHeader('Content-Length', filesize($filePath));
-    }
-}
diff --git a/server/src/Action/IsResultAvailableAction.php b/server/src/Action/IsResultAvailableAction.php
deleted file mode 100644
index 98019b4aa91f345df3af0b9183e7cf58d78e9abb..0000000000000000000000000000000000000000
--- a/server/src/Action/IsResultAvailableAction.php
+++ /dev/null
@@ -1,85 +0,0 @@
-<?php
-
-/*
- * This file is part of Anis Server.
- *
- * (c) 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.
- */
-declare(strict_types=1);
-
-namespace App\Action;
-
-use Psr\Http\Message\ServerRequestInterface;
-use Psr\Http\Message\ResponseInterface;
-use Doctrine\ORM\EntityManagerInterface;
-
-/**
- * @author François Agneray <francois.agneray@lam.fr>
- * @package App\Action
- */
-final class IsResultAvailableAction extends AbstractAction
-{
-    /**
-     * Contains anis-server data path
-     *
-     * @var string
-     */
-    private $dataPath;
-
-    /**
-     * Contains result folder path
-     *
-     * @var array
-     */
-    private $resultFolder;
-
-    /**
-     * Create the classe before call __invoke to execute the action
-     *
-     * @param EntityManagerInterface $em           Doctrine Entity Manager Interface
-     * @param string                 $dataPath     Contains anis-server data path
-     * @param string                 $resultFolder Contains result folder path
-     */
-    public function __construct(EntityManagerInterface $em, string $dataPath, string $resultFolder)
-    {
-        parent::__construct($em);
-        $this->dataPath = $dataPath;
-        $this->resultFolder = $resultFolder;
-    }
-
-    /**
-     * `GET` Returns the file found
-     *
-     * @param  ServerRequestInterface $request  PSR-7 This object represents the HTTP request
-     * @param  ResponseInterface      $response PSR-7 This object represents the HTTP response
-     * @param  string[]               $args     This table contains information transmitted in the URL (see routes.php)
-     *
-     * @return ResponseInterface
-     */
-    public function __invoke(
-        ServerRequestInterface $request,
-        ResponseInterface $response,
-        array $args
-    ): ResponseInterface {
-        if ($request->getMethod() === OPTIONS) {
-            return $response->withHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
-        }
-
-        $fileId = $args['id'];
-
-        // Search the file
-        $filePath = $this->dataPath . $this->resultFolder . '/' . $fileId;
-
-        $isAvailable = false;
-        if (file_exists($filePath)) {
-            $isAvailable = true;
-        }
-
-        $payload = json_encode(array('file_is_available' => $isAvailable));
-        $response->getBody()->write($payload);
-        return $response;
-    }
-}
diff --git a/server/src/Action/StartTaskCreateResultAction.php b/server/src/Action/StartTaskCreateResultAction.php
deleted file mode 100644
index ebe428496b3ed464bdf0d88bcfc4935ef7b07062..0000000000000000000000000000000000000000
--- a/server/src/Action/StartTaskCreateResultAction.php
+++ /dev/null
@@ -1,157 +0,0 @@
-<?php
-
-/*
- * This file is part of Anis Server.
- *
- * (c) 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.
- */
-declare(strict_types=1);
-
-namespace App\Action;
-
-use Psr\Http\Message\ServerRequestInterface;
-use Psr\Http\Message\ResponseInterface;
-use Slim\Exception\HttpBadRequestException;
-use Slim\Exception\HttpNotFoundException;
-use Doctrine\ORM\EntityManagerInterface;
-use PhpAmqpLib\Connection\AbstractConnection;
-use PhpAmqpLib\Message\AMQPMessage;
-
-/**
- * @author François Agneray <francois.agneray@lam.fr>
- * @package App\Action
- */
-final class StartTaskCreateResultAction extends AbstractAction
-{
-    /**
-     * Contains RabbitMQ connection socket
-     *
-     * @var AbstractConnection
-     */
-    private $rmq;
-
-    /**
-     * Contains settings to handle Json Web Token (app/settings.php)
-     *
-     * @var array
-     */
-    private $settings;
-
-    /**
-     * Create the classe before call __invoke to execute the action
-     *
-     * @param EntityManagerInterface $em       Doctrine Entity Manager Interface
-     * @param AbstractConnection     $rmq      RabbitMQ connection socket
-     * @param array                  $settings Settings about token
-     */
-    public function __construct(
-        EntityManagerInterface $em,
-        AbstractConnection $rmq,
-        array $settings
-    ) {
-        parent::__construct($em);
-        $this->rmq = $rmq;
-        $this->settings = $settings;
-    }
-
-    /**
-     * `GET` Starts an asynchronous task, through rabbitmq, to build an archive
-     *
-     * @param  ServerRequestInterface $request  PSR-7 This object represents the HTTP request
-     * @param  ResponseInterface      $response PSR-7 This object represents the HTTP response
-     * @param  string[]               $args     This table contains information transmitted in the URL (see routes.php)
-     *
-     * @return ResponseInterface
-     */
-    public function __invoke(
-        ServerRequestInterface $request,
-        ResponseInterface $response,
-        array $args
-    ): ResponseInterface {
-        if ($request->getMethod() === OPTIONS) {
-            return $response->withHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
-        }
-
-        // Search the correct dataset with primary key
-        $datasetName = $args['dname'];
-        $dataset = $this->em->find('App\Entity\Dataset', $datasetName);
-
-        // If dataset is not found 404
-        if (is_null($dataset)) {
-            throw new HttpNotFoundException(
-                $request,
-                'Dataset with name ' . $datasetName . ' is not found'
-            );
-        }
-
-        $token = '';
-
-        // If instance is private and authorization enabled
-        $instance = $dataset->getDatasetFamily()->getInstance();
-        if (!$instance->getPublic() && boolval($this->settings['enabled'])) {
-            $this->verifyInstanceAuthorization(
-                $request,
-                $instance->getName(),
-                explode(',', $this->settings['admin_roles'])
-            );
-            $token = $request->getHeader('Authorization')[0];
-        }
-
-        // If dataset is private and authorization enabled
-        if (!$dataset->getPublic() && boolval($this->settings['enabled'])) {
-            $this->verifyDatasetAuthorization(
-                $request,
-                $dataset->getName(),
-                explode(',', $this->settings['admin_roles'])
-            );
-            $token = $request->getHeader('Authorization')[0];
-        }
-
-        $queryParams = $request->getQueryParams();
-
-        // The parameter "a" is mandatory
-        if (!array_key_exists('a', $queryParams)) {
-            throw new HttpBadRequestException(
-                $request,
-                'Param a is required for this request'
-            );
-        }
-
-        // Search extension
-        if ($queryParams['f'] === 'json') {
-            $extension = '.json';
-        } elseif ($queryParams['f'] === 'csv') {
-            $extension = '.csv';
-        } elseif ($queryParams['f'] === 'ascii') {
-            $extension = '.txt';
-        } elseif ($queryParams['f'] === 'votable') {
-            $extension = '.xml';
-        } else {
-            $extension = '.json';
-        }
-
-        // Create the name of the future archive
-        $fileName = 'result_' . $dataset->getName() . '_' . (new \DateTime())->format('Y-m-d\TH:i:s') . $extension;
-        $fileId = 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(
-            'file_id' => $fileId,
-            'dataset_name' => $datasetName,
-            'query' => $request->getUri()->getQuery(),
-            'token' => $token
-        )));
-        $channel->basic_publish($msg, '', 'result');
-
-        // Just returns the future archive name
-        $payload = json_encode(array('file_name' => $fileName, 'file_id' => $fileId));
-        $response->getBody()->write($payload);
-        return $response;
-    }
-}