Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Align upload & download progress between desktop and web #111580

Open
bpasero opened this issue Dec 1, 2020 · 16 comments
Open

Align upload & download progress between desktop and web #111580

bpasero opened this issue Dec 1, 2020 · 16 comments
Assignees
Labels
feature-request Request for new features or functionality file-explorer Explorer widget issues remote Remote system operations issues workspace-edit
Milestone

Comments

@bpasero
Copy link
Member

bpasero commented Dec 1, 2020

Testing #111250

I think the download progress for web is more detailed compared to what we have now for desktop and it would be great to align. Though I am not sure if this possibly requires some infrastructure on the file service layer.

recording

Update from #146226: The same applies to upload progress which is much nicer in web.

We have a few places where we show progress for uploading, downloading and importing:

const uploadPromise = this.progressService.withProgress(

const importPromise = this.progressService.withProgress(

const downloadPromise = this.progressService.withProgress(

In all three cases we can use the IProgressStep to update the message and overall progress counts.

We may not get all the information we need such as knowing the overall size, but if we do we should make sure that the label for the progress is as accurate as possible.

This sounds like a fun area for a contribution where someone can geek out on providing the best progress info possible. As such, opening for help wanted.

@isidorn
Copy link
Contributor

isidorn commented Dec 1, 2020

Web uses a custom solution which only works for that use case, I want a general approach for all file operations.
I would not change this for now -> backlog.

Custom solution code pointer

const dropPromise = this.progressService.withProgress({

General solution code pointer

const promise = this.progressService.withProgress({

@isidorn isidorn added file-explorer Explorer widget issues under-discussion Issue is under discussion for relevance, priority, approach labels Dec 1, 2020
@isidorn
Copy link
Contributor

isidorn commented Dec 1, 2020

To make the general solution as powerful as the custom one for upload we would need some infrastrucutre on the file service layer as you mentioned.
Before we have that assigning this to On-Deck

@sbatten
Copy link
Member

sbatten commented Dec 2, 2020

I was testing one of the e2e scenarios and notice that in web, the status bar indicator is easily missed. I could not tell that it was still downloading until I opened the locally and saw it was still populating.

@isidorn isidorn assigned JacksonKearl and unassigned isidorn Aug 12, 2021
@bpasero bpasero changed the title Align download progress between remote and web Align upload & download progress between remote and web Apr 17, 2022
@bpasero bpasero added feature-request Request for new features or functionality and removed under-discussion Issue is under discussion for relevance, priority, approach labels Apr 17, 2022
@bpasero bpasero changed the title Align upload & download progress between remote and web Align upload & download progress between desktop and web Apr 17, 2022
@bpasero bpasero added the remote Remote system operations issues label Apr 17, 2022
@lramos15 lramos15 assigned lramos15 and unassigned JacksonKearl May 25, 2022
@DanTaranis
Copy link

@lramos15 how is this going?

@lramos15
Copy link
Member

lramos15 commented Sep 7, 2022

@lramos15 how is this going?

There has been no progress here, PRs are welcome

@kkneze01
Copy link

We are missing this feature in VSCode

@v4r4rth
Copy link

v4r4rth commented Dec 8, 2023

Adding to the noise - would love this feature..

@JalinWang
Copy link

#83565 (comment) The web version is very nice

@JalinWang
Copy link

JalinWang commented Dec 29, 2023

I am attempting to address this issue and leaving some notes to ensure at least a little contribution, just in case I decide to give up at some point. 😥
Feel free to point out my mistakes.

@JalinWang
Copy link

JalinWang commented Dec 29, 2023

The uploading operation starts in FileDragAndDrop.drop() function in src/vs/workbench/contrib/files/browser/views/explorerViewer.ts (as well as uploading menu in browser

const uploadFileHandler = async (accessor: ServicesAccessor) => {
)

// External file DND (Import/Upload file)
if (data instanceof NativeDragAndDropData) {
// Use local file import when supported
if (!isWeb || (isTemporaryWorkspace(this.contextService.getWorkspace()) && WebFileSystemAccess.supported(mainWindow))) {
const fileImport = this.instantiationService.createInstance(ExternalFileImport);
await fileImport.import(resolvedTarget, originalEvent, mainWindow);
}
// Otherwise fallback to browser based file upload
else {
const browserUpload = this.instantiationService.createInstance(BrowserFileUpload);
await browserUpload.upload(target, originalEvent);
}
}
// In-Explorer DND (Move/Copy file)
else {
await this.handleExplorerDrop(data as ElementsDragAndDropData<ExplorerItem, ExplorerItem[]>, resolvedTarget, originalEvent);
}

Codes here choose different behaviors for different platforms.
Currently, BrowserFileUpload for web already has a nice progress bar.
image

Thus, we should focus on ExternalFileImport class in src/vs/workbench/contrib/files/browser/fileImportExport.ts.

For downloading, the platform switching logic is located in FileDownload.doDownload() in src/vs/workbench/contrib/files/browser/fileImportExport.ts and we should improve FileDownload.doDownloadNative() function.

// Web: use DOM APIs to download files with optional support
// for folders and large files
if (isWeb) {
await this.doDownloadBrowser(source.resource, progress, cts);
}
// Native: use working copy file service to get at the contents
else {
await this.doDownloadNative(source, progress, cts);
}

@JalinWang
Copy link

JalinWang commented Dec 29, 2023

Take downloading as an example, there are several progress indicators:

  1. in file tree view:
    /**
    * Explorer file view id.
    */
    export const VIEW_ID = 'workbench.explorer.fileView';

    // Also indicate progress in the files view
    this.progressService.withProgress({ location: VIEW_ID, delay: 500 }, () => downloadPromise);
  2. notification of the coping state for the whole operation:
    // Indicate progress globally
    const downloadPromise = this.progressService.withProgress(
    {
    location: ProgressLocation.Window,
    delay: 800,
    cancellable: isWeb,
    title: localize('downloadingFiles', "Downloading")
    },
    async progress => this.doDownload(source, progress, cts),
    () => cts.dispose(true)
    );
  3. notification of the coping state for current file:
    // Remember as last used download folder
    this.storageService.store(FileDownload.LAST_USED_DOWNLOAD_PATH_STORAGE_KEY, dirname(destination).fsPath, StorageScope.APPLICATION, StorageTarget.MACHINE);
    // Perform download
    await this.explorerService.applyBulkEdit([new ResourceFileEdit(explorerItem.resource, destination, { overwrite: true, copy: true })], {
    undoLabel: localize('downloadBulkEdit', "Download {0}", explorerItem.name),
    progressLabel: localize('downloadingBulkEdit', "Downloading {0}", explorerItem.name),
    progressLocation: ProgressLocation.Window
    });
  4. indicator in file view for individual file:
    await this.progressService.withProgress({ location: ProgressLocation.Explorer, delay: 500 }, () => promise);

@JalinWang
Copy link

JalinWang commented Dec 30, 2023

The operations are actually performed in bulkEditService by delegation from https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/files/browser/explorerService.ts#L185-L204

const resources = await bulkEdit.perform();

async perform(): Promise<readonly URI[]> {

@nom
Copy link

nom commented Apr 9, 2024

+1 this would be a great feature

@JalinWang
Copy link

JalinWang commented May 7, 2024

BTW, the uploading operation is not cancelable for remote dev although there are cancel buttons :(
image

Downloading instead doesn't show the cancel option.
image

updated: root cause
https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/files/browser/explorerService.ts#L190

@JalinWang
Copy link

JalinWang commented May 7, 2024

As isidorn & bpasero say, we'd better

  1. create an abstraction/interface to replace the bulkEdit usage for uploading/downloading as well as unify the web&native version. (if I got it correctly);
  2. make the fs layer report copied/transfered bytes.

@JalinWang
Copy link

The copy op happens here:

// copy source => target
if (mode === 'copy') {
// same provider with fast copy: leverage copy() functionality
if (sourceProvider === targetProvider && hasFileFolderCopyCapability(sourceProvider)) {
await sourceProvider.copy(source, target, { overwrite });
}
// when copying via buffer/unbuffered, we have to manually
// traverse the source if it is a folder and not a file
else {
const sourceFile = await this.resolve(source);
if (sourceFile.isDirectory) {
await this.doCopyFolder(sourceProvider, sourceFile, targetProvider, target);
} else {
await this.doCopyFile(sourceProvider, source, targetProvider, target);
}
}
return mode;
}

For remote scenarios, the doCopyFile does the actual work.

private async doCopyFile(sourceProvider: IFileSystemProvider, source: URI, targetProvider: IFileSystemProvider, target: URI): Promise<void> {
// copy: source (buffered) => target (buffered)
if (hasOpenReadWriteCloseCapability(sourceProvider) && hasOpenReadWriteCloseCapability(targetProvider)) {
return this.doPipeBuffered(sourceProvider, source, targetProvider, target);
}
// copy: source (buffered) => target (unbuffered)
if (hasOpenReadWriteCloseCapability(sourceProvider) && hasReadWriteCapability(targetProvider)) {
return this.doPipeBufferedToUnbuffered(sourceProvider, source, targetProvider, target);
}
// copy: source (unbuffered) => target (buffered)
if (hasReadWriteCapability(sourceProvider) && hasOpenReadWriteCloseCapability(targetProvider)) {
return this.doPipeUnbufferedToBuffered(sourceProvider, source, targetProvider, target);
}
// copy: source (unbuffered) => target (unbuffered)
if (hasReadWriteCapability(sourceProvider) && hasReadWriteCapability(targetProvider)) {
return this.doPipeUnbuffered(sourceProvider, source, targetProvider, target);
}
}

One change of infra may do something with doPipe* API series here ( to report download/uploading progress)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature-request Request for new features or functionality file-explorer Explorer widget issues remote Remote system operations issues workspace-edit
Projects
None yet
Development

No branches or pull requests

10 participants