Skip to content

Commit

Permalink
Revert "improve download, allow download of big files, works on ff an…
Browse files Browse the repository at this point in the history
…d chrome"

This reverts commit ede3b9c.
  • Loading branch information
kittaakos authored Jul 15, 2019
1 parent 3b54e9e commit 86fb143
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 280 deletions.
2 changes: 2 additions & 0 deletions packages/filesystem/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"@theia/core": "^0.8.0",
"@types/body-parser": "^1.17.0",
"@types/fs-extra": "^4.0.2",
"@types/mime-types": "^2.1.0",
"@types/rimraf": "^2.0.2",
"@types/tar-fs": "^1.16.1",
"@types/touch": "0.0.1",
Expand All @@ -14,6 +15,7 @@
"drivelist": "^6.4.3",
"fs-extra": "^4.0.2",
"http-status-codes": "^1.3.0",
"mime-types": "^2.1.18",
"minimatch": "^3.0.4",
"mv": "^2.1.1",
"rimraf": "^2.6.2",
Expand Down
94 changes: 52 additions & 42 deletions packages/filesystem/src/browser/download/file-download-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,18 @@ import { inject, injectable } from 'inversify';
import URI from '@theia/core/lib/common/uri';
import { ILogger } from '@theia/core/lib/common/logger';
import { Endpoint } from '@theia/core/lib/browser/endpoint';
import { StatusBar, StatusBarAlignment } from '@theia/core/lib/browser/status-bar';
import { FileSystem } from '../../common/filesystem';
import { FileDownloadData } from '../../common/download/file-download-data';
import { MessageService } from '@theia/core/lib/common/message-service';
import { addClipboardListener } from '@theia/core/lib/browser/widgets';

@injectable()
export class FileDownloadService {

protected static PREPARING_DOWNLOAD_ID = 'theia-preparing-download';

protected anchor: HTMLAnchorElement | undefined;
protected downloadQueue: number[] = [];
protected downloadCounter: number = 0;

@inject(ILogger)
Expand All @@ -35,71 +38,60 @@ export class FileDownloadService {
@inject(FileSystem)
protected readonly fileSystem: FileSystem;

@inject(StatusBar)
protected readonly statusBar: StatusBar;

@inject(MessageService)
protected readonly messageService: MessageService;

protected handleCopy(event: ClipboardEvent, downloadUrl: string) {
if (downloadUrl) {
event.clipboardData.setData('text/plain', downloadUrl);
event.preventDefault();
this.messageService.info('Download link copied!');
}
}

async cancelDownload(id: string) {
await fetch(`${this.endpoint()}/download/?id=${id}&cancel=true`);
}

async download(uris: URI[]): Promise<void> {
let cancel = false;
if (uris.length === 0) {
return;
}
let downloadId: number | undefined;
try {
const [progress, response] = await Promise.all([
this.messageService.showProgress({
text: 'Preparing download link...', options: { cancelable: true }
}, () => { cancel = true; }),
fetch(this.request(uris))
]);
const jsonResponse = await response.json();
if (cancel) {
this.cancelDownload(jsonResponse.id);
return;
downloadId = this.downloadCounter++;
if (this.downloadQueue.length === 0) {
await this.statusBar.setElement(FileDownloadService.PREPARING_DOWNLOAD_ID, {
alignment: StatusBarAlignment.RIGHT,
text: '$(spinner~spin) Preparing download...',
tooltip: 'Preparing download...',
priority: 1
});
}
this.downloadQueue.push(downloadId);
const response = await fetch(this.request(uris));
await this.statusBar.removeElement(FileDownloadService.PREPARING_DOWNLOAD_ID);
const title = await this.title(response, uris);
const { status, statusText } = response;
if (status === 200) {
progress.cancel();
const downloadUrl = `${this.endpoint()}/download/?id=${jsonResponse.id}`;
this.messageService.info(downloadUrl, 'Download', 'Copy Download Link').then(action => {
if (action === 'Download') {
this.forceDownload(jsonResponse.id, decodeURIComponent(jsonResponse.name));
this.messageService.info('Download started!');
} else if (action === 'Copy Download Link') {
if (document.documentElement) {
addClipboardListener(document.documentElement, 'copy', e => this.handleCopy(e, downloadUrl));
document.execCommand('copy');
}
} else {
this.cancelDownload(jsonResponse.id);
}
});
await this.forceDownload(response, decodeURIComponent(title));
} else {
throw new Error(`Received unexpected status code: ${status}. [${statusText}]`);
}
} catch (e) {
this.logger.error(`Error occurred when downloading: ${uris.map(u => u.toString(true))}.`, e);
} finally {
if (downloadId !== undefined) {
const indexOf = this.downloadQueue.indexOf(downloadId);
if (indexOf !== -1) {
this.downloadQueue.splice(indexOf, 1);
}
if (this.downloadQueue.length === 0) {
this.statusBar.removeElement(FileDownloadService.PREPARING_DOWNLOAD_ID);
}
}
}
}

protected async forceDownload(id: string, title: string): Promise<void> {
protected async forceDownload(response: Response, title: string): Promise<void> {
let url: string | undefined;
try {
const blob = await response.blob();
url = URL.createObjectURL(blob);
if (this.anchor === undefined) {
this.anchor = document.createElement('a');
}
const endpoint = this.endpoint();
url = `${endpoint}/download/?id=${id}`;
this.anchor.href = url;
this.anchor.style.display = 'none';
this.anchor.download = title;
Expand All @@ -116,6 +108,24 @@ export class FileDownloadService {
}
}

protected async title(response: Response, uris: URI[]): Promise<string> {
let title = (response.headers.get('Content-Disposition') || '').split('attachment; filename=').pop();
if (title) {
return title;
}
// tslint:disable-next-line:whitespace
const [uri,] = uris;
if (uris.length === 1) {
const stat = await this.fileSystem.getFileStat(uri.toString());
if (stat === undefined) {
throw new Error(`Unexpected error occurred when downloading file. Files does not exist. URI: ${uri.toString(true)}.`);
}
title = uri.path.base;
return stat.isDirectory ? `${title}.tar` : title;
}
return `${uri.parent.path.name}.tar`;
}

protected request(uris: URI[]): Request {
const url = this.url(uris);
const init = this.requestInit(uris);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,13 @@
import { ContainerModule } from 'inversify';
import { BackendApplicationContribution } from '@theia/core/lib/node/backend-application';
import { FileDownloadEndpoint } from './file-download-endpoint';
import { FileDownloadHandler, SingleFileDownloadHandler, MultiFileDownloadHandler, DownloadLinkHandler } from './file-download-handler';
import { FileDownloadHandler, SingleFileDownloadHandler, MultiFileDownloadHandler } from './file-download-handler';
import { DirectoryArchiver } from './directory-archiver';
import { FileDownloadCache } from './file-download-cache';

export default new ContainerModule(bind => {
bind(FileDownloadEndpoint).toSelf().inSingletonScope();
bind(BackendApplicationContribution).toService(FileDownloadEndpoint);
bind(FileDownloadCache).toSelf().inSingletonScope();
bind(FileDownloadHandler).to(SingleFileDownloadHandler).inSingletonScope().whenTargetNamed(FileDownloadHandler.SINGLE);
bind(FileDownloadHandler).to(MultiFileDownloadHandler).inSingletonScope().whenTargetNamed(FileDownloadHandler.MULTI);
bind(FileDownloadHandler).to(DownloadLinkHandler).inSingletonScope().whenTargetNamed(FileDownloadHandler.DOWNLOAD_LINK);
bind(DirectoryArchiver).toSelf().inSingletonScope();
});
88 changes: 0 additions & 88 deletions packages/filesystem/src/node/download/file-download-cache.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,8 @@ export class FileDownloadEndpoint implements BackendApplicationContribution {
@named(FileDownloadHandler.MULTI)
protected readonly multiFileDownloadHandler: FileDownloadHandler;

@inject(FileDownloadHandler)
@named(FileDownloadHandler.DOWNLOAD_LINK)
protected readonly downloadLinkHandler: FileDownloadHandler;

configure(app: Application): void {
const router = Router();
router.get('/download', (request, response) => this.downloadLinkHandler.handle(request, response));
router.get('/', (request, response) => this.singleFileDownloadHandler.handle(request, response));
router.put('/', (request, response) => this.multiFileDownloadHandler.handle(request, response));
// Content-Type: application/json
Expand Down
Loading

0 comments on commit 86fb143

Please sign in to comment.