未验证 提交 d0509cbc 编写于 作者: B Benjamin Pasero 提交者: GitHub

Web: allow to download from the explorer (#83220)

* wip

* finish
上级 fcb807c7
......@@ -1212,3 +1212,18 @@ export function asCSSUrl(uri: URI): string {
}
return `url('${asDomUri(uri).toString(true).replace(/'/g, '%27')}')`;
}
export function triggerDownload(uri: URI, name: string): void {
// In order to download from the browser, the only way seems
// to be creating a <a> element with download attribute that
// points to the file to download.
// See also https://developers.google.com/web/updates/2011/08/Downloading-resources-in-HTML5-a-download
const anchor = document.createElement('a');
document.body.appendChild(anchor);
anchor.download = name;
anchor.href = uri.toString(true);
anchor.click();
// Ensure to remove the element from DOM eventually
setTimeout(() => document.body.removeChild(anchor));
}
......@@ -5,7 +5,7 @@
import * as nls from 'vs/nls';
import { Registry } from 'vs/platform/registry/common/platform';
import { ToggleAutoSaveAction, GlobalNewUntitledFileAction, FocusFilesExplorer, GlobalCompareResourcesAction, SaveAllAction, ShowActiveFileInExplorer, CollapseExplorerView, RefreshExplorerView, CompareWithClipboardAction, NEW_FILE_COMMAND_ID, NEW_FILE_LABEL, NEW_FOLDER_COMMAND_ID, NEW_FOLDER_LABEL, TRIGGER_RENAME_LABEL, MOVE_FILE_TO_TRASH_LABEL, COPY_FILE_LABEL, PASTE_FILE_LABEL, FileCopiedContext, renameHandler, moveFileToTrashHandler, copyFileHandler, pasteFileHandler, deleteFileHandler, cutFileHandler, DOWNLOAD_COMMAND_ID, openFilePreserveFocusHandler } from 'vs/workbench/contrib/files/browser/fileActions';
import { ToggleAutoSaveAction, GlobalNewUntitledFileAction, FocusFilesExplorer, GlobalCompareResourcesAction, SaveAllAction, ShowActiveFileInExplorer, CollapseExplorerView, RefreshExplorerView, CompareWithClipboardAction, NEW_FILE_COMMAND_ID, NEW_FILE_LABEL, NEW_FOLDER_COMMAND_ID, NEW_FOLDER_LABEL, TRIGGER_RENAME_LABEL, MOVE_FILE_TO_TRASH_LABEL, COPY_FILE_LABEL, PASTE_FILE_LABEL, FileCopiedContext, renameHandler, moveFileToTrashHandler, copyFileHandler, pasteFileHandler, deleteFileHandler, cutFileHandler, DOWNLOAD_COMMAND_ID, openFilePreserveFocusHandler, DOWNLOAD_LABEL } from 'vs/workbench/contrib/files/browser/fileActions';
import { revertLocalChangesCommand, acceptLocalChangesCommand, CONFLICT_RESOLUTION_CONTEXT } from 'vs/workbench/contrib/files/browser/saveErrorHandler';
import { SyncActionDescriptor, MenuId, MenuRegistry, ILocalizedString } from 'vs/platform/actions/common/actions';
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
......@@ -14,7 +14,7 @@ import { openWindowCommand, COPY_PATH_COMMAND_ID, REVEAL_IN_EXPLORER_COMMAND_ID,
import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { isMacintosh } from 'vs/base/common/platform';
import { isMacintosh, isWeb } from 'vs/base/common/platform';
import { FilesExplorerFocusCondition, ExplorerRootContext, ExplorerFolderContext, ExplorerResourceNotReadonlyContext, ExplorerResourceCut, IExplorerService, ExplorerResourceMoveableToTrash, ExplorerViewletVisibleContext } from 'vs/workbench/contrib/files/common/files';
import { ADD_ROOT_FOLDER_COMMAND_ID, ADD_ROOT_FOLDER_LABEL } from 'vs/workbench/browser/actions/workspaceCommands';
import { CLOSE_SAVED_EDITORS_COMMAND_ID, CLOSE_EDITORS_IN_GROUP_COMMAND_ID, CLOSE_EDITOR_COMMAND_ID, CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands';
......@@ -23,7 +23,7 @@ import { ResourceContextKey } from 'vs/workbench/common/resources';
import { WorkbenchListDoubleSelection } from 'vs/platform/list/browser/listService';
import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
import { IsWebContext, WorkspaceFolderCountContext } from 'vs/workbench/browser/contextkeys';
import { WorkspaceFolderCountContext } from 'vs/workbench/browser/contextkeys';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { OpenFileFolderAction, OpenFileAction, OpenFolderAction, OpenWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions';
import { ActiveEditorIsSaveableContext } from 'vs/workbench/common/editor';
......@@ -218,7 +218,6 @@ export function appendToCommandPalette(id: string, title: ILocalizedString, cate
});
}
const downloadLabel = nls.localize('download', "Download");
appendToCommandPalette(COPY_PATH_COMMAND_ID, { value: nls.localize('copyPathOfActive', "Copy Path of Active File"), original: 'Copy Path of Active File' }, category);
appendToCommandPalette(COPY_RELATIVE_PATH_COMMAND_ID, { value: nls.localize('copyRelativePathOfActive', "Copy Relative Path of Active File"), original: 'Copy Relative Path of Active File' }, category);
appendToCommandPalette(SAVE_FILE_COMMAND_ID, { value: SAVE_FILE_LABEL, original: 'Save' }, category);
......@@ -231,7 +230,7 @@ appendToCommandPalette(SAVE_FILE_AS_COMMAND_ID, { value: SAVE_FILE_AS_LABEL, ori
appendToCommandPalette(CLOSE_EDITOR_COMMAND_ID, { value: nls.localize('closeEditor', "Close Editor"), original: 'Close Editor' }, { value: nls.localize('view', "View"), original: 'View' });
appendToCommandPalette(NEW_FILE_COMMAND_ID, { value: NEW_FILE_LABEL, original: 'New File' }, category, WorkspaceFolderCountContext.notEqualsTo('0'));
appendToCommandPalette(NEW_FOLDER_COMMAND_ID, { value: NEW_FOLDER_LABEL, original: 'New Folder' }, category, WorkspaceFolderCountContext.notEqualsTo('0'));
appendToCommandPalette(DOWNLOAD_COMMAND_ID, { value: downloadLabel, original: 'Download' }, category, ContextKeyExpr.and(IsWebContext.toNegated(), ResourceContextKey.Scheme.notEqualsTo(Schemas.file)));
appendToCommandPalette(DOWNLOAD_COMMAND_ID, { value: DOWNLOAD_LABEL, original: 'Download' }, category, ContextKeyExpr.and(ResourceContextKey.Scheme.notEqualsTo(Schemas.file)));
// Menu registration - open editors
......@@ -465,15 +464,24 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
when: ExplorerFolderContext
});
MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
group: '5_cutcopypaste',
order: 30,
command: {
id: DOWNLOAD_COMMAND_ID,
title: downloadLabel,
},
when: ContextKeyExpr.and(IsWebContext.toNegated(), ResourceContextKey.Scheme.notEqualsTo(Schemas.file))
});
MenuRegistry.appendMenuItem(MenuId.ExplorerContext, (() => {
const downloadMenuItem = {
group: '5_cutcopypaste',
order: 30,
command: {
id: DOWNLOAD_COMMAND_ID,
title: DOWNLOAD_LABEL,
},
when: ContextKeyExpr.and(ResourceContextKey.Scheme.notEqualsTo(Schemas.file))
};
// Web: currently not supporting download of folders
if (isWeb) {
downloadMenuItem.when = ContextKeyExpr.and(ResourceContextKey.Scheme.notEqualsTo(Schemas.file), ExplorerFolderContext.toNegated());
}
return downloadMenuItem;
})());
MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
group: '6_copypath',
......
......@@ -6,7 +6,7 @@
import 'vs/css!./media/fileactions';
import * as nls from 'vs/nls';
import * as types from 'vs/base/common/types';
import { isWindows } from 'vs/base/common/platform';
import { isWindows, isWeb } from 'vs/base/common/platform';
import * as extpath from 'vs/base/common/extpath';
import { extname, basename } from 'vs/base/common/path';
import * as resources from 'vs/base/common/resources';
......@@ -45,6 +45,8 @@ import { coalesce } from 'vs/base/common/arrays';
import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree';
import { ExplorerItem, NewExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel';
import { onUnexpectedError, getErrorMessage } from 'vs/base/common/errors';
import { asDomUri, triggerDownload } from 'vs/base/browser/dom';
import { mnemonicButtonLabel } from 'vs/base/common/labels';
export const NEW_FILE_COMMAND_ID = 'explorer.newFile';
export const NEW_FILE_LABEL = nls.localize('newFile', "New File");
......@@ -62,6 +64,8 @@ export const PASTE_FILE_LABEL = nls.localize('pasteFile', "Paste");
export const FileCopiedContext = new RawContextKey<boolean>('fileCopied', false);
export const DOWNLOAD_LABEL = nls.localize('download', "Download");
const CONFIRM_DELETE_SETTING_KEY = 'explorer.confirmDelete';
function onError(notificationService: INotificationService, error: any): void {
......@@ -1050,11 +1054,25 @@ const downloadFileHandler = (accessor: ServicesAccessor) => {
if (explorerContext.stat) {
const stats = explorerContext.selection.length > 1 ? explorerContext.selection : [explorerContext.stat];
stats.forEach(async s => {
const destination = await fileDialogService.showSaveDialog({
availableFileSystems: [Schemas.file]
});
if (destination) {
await fileService.copy(s.resource, destination);
if (isWeb) {
if (!s.isDirectory) {
triggerDownload(asDomUri(s.resource), s.name);
}
} else {
let defaultUri = s.isDirectory ? fileDialogService.defaultFolderPath() : fileDialogService.defaultFilePath();
if (defaultUri && !s.isDirectory) {
defaultUri = resources.joinPath(defaultUri, s.name);
}
const destination = await fileDialogService.showSaveDialog({
availableFileSystems: [Schemas.file],
saveLabel: mnemonicButtonLabel(nls.localize('download', "Download")),
title: s.isDirectory ? nls.localize('downloadFolder', "Download Folder") : nls.localize('downloadFile', "Download File"),
defaultUri
});
if (destination) {
await fileService.copy(s.resource, destination);
}
}
});
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册