Skip to content

Commit

Permalink
Improve performance of file operations
Browse files Browse the repository at this point in the history
  • Loading branch information
hez2010 committed Sep 5, 2021
1 parent ea39852 commit 026a5f2
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 85 deletions.
23 changes: 13 additions & 10 deletions Files/DataModels/FilesystemItemsOperationDataModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Files.Helpers;
using Files.ViewModels.Dialogs;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
Expand Down Expand Up @@ -59,16 +60,16 @@ public FilesystemItemsOperationDataModel(FilesystemOperationType operationType,

public async Task<List<FilesystemOperationItemViewModel>> ToItems(Action updatePrimaryButtonEnabled, Action optionGenerateNewName, Action optionReplaceExisting, Action optionSkip)
{
List<FilesystemOperationItemViewModel> items = new List<FilesystemOperationItemViewModel>();
ConcurrentBag<(int Index, FilesystemOperationItemViewModel Model)> items = new ConcurrentBag<(int Index, FilesystemOperationItemViewModel Model)>();

List<FilesystemItemsOperationItemModel> nonConflictingItems = IncomingItems.Except(ConflictingItems).ToList();

// Add conflicting items first
foreach (var item in ConflictingItems)
await Task.WhenAll(ConflictingItems.Select(async (item, index) =>
{
var iconData = await FileThumbnailHelper.LoadIconFromPathAsync(item.SourcePath, 64u, Windows.Storage.FileProperties.ThumbnailMode.ListView);

items.Add(new FilesystemOperationItemViewModel(updatePrimaryButtonEnabled, optionGenerateNewName, optionReplaceExisting, optionSkip)
items.Add((index, new FilesystemOperationItemViewModel(updatePrimaryButtonEnabled, optionGenerateNewName, optionReplaceExisting, optionSkip)
{
IsConflict = true,
ItemIcon = iconData != null ? await iconData.ToBitmapAsync() : null,
Expand All @@ -78,15 +79,17 @@ public async Task<List<FilesystemOperationItemViewModel>> ToItems(Action updateP
ConflictResolveOption = FileNameConflictResolveOptionType.GenerateNewName,
ItemOperation = item.OperationType,
ActionTaken = false
});
}
}));
}));

var baseIndex = ConflictingItems.Count;

// Then add non-conflicting items
foreach (var item in nonConflictingItems)
await Task.WhenAll(nonConflictingItems.Select(async (item, index) =>
{
var iconData = await FileThumbnailHelper.LoadIconFromPathAsync(item.SourcePath, 64u, Windows.Storage.FileProperties.ThumbnailMode.ListView);

items.Add(new FilesystemOperationItemViewModel(updatePrimaryButtonEnabled, optionGenerateNewName, optionReplaceExisting, optionSkip)
items.Add((baseIndex + index, new FilesystemOperationItemViewModel(updatePrimaryButtonEnabled, optionGenerateNewName, optionReplaceExisting, optionSkip)
{
IsConflict = false,
ItemIcon = iconData != null ? await iconData.ToBitmapAsync() : null,
Expand All @@ -96,10 +99,10 @@ public async Task<List<FilesystemOperationItemViewModel>> ToItems(Action updateP
ConflictResolveOption = FileNameConflictResolveOptionType.NotAConflict,
ItemOperation = item.OperationType,
ActionTaken = true
});
}
}));
}));

return items;
return items.OrderBy(i => i.Index).Select(i => i.Model).ToList();
}

private string GetOperationIconGlyph(FilesystemOperationType operationType)
Expand Down
128 changes: 73 additions & 55 deletions Files/Helpers/UIFilesystemHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
using Files.Interacts;
using Microsoft.Toolkit.Uwp;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Windows.ApplicationModel.AppService;
Expand All @@ -24,7 +26,7 @@ public static async void CutItem(IShellPage associatedInstance)
{
RequestedOperation = DataPackageOperation.Move
};
List<IStorageItem> items = new List<IStorageItem>();
ConcurrentBag<IStorageItem> items = new ConcurrentBag<IStorageItem>();
FilesystemResult result = (FilesystemResult)false;

var canFlush = true;
Expand All @@ -33,46 +35,54 @@ public static async void CutItem(IShellPage associatedInstance)
// First, reset DataGrid Rows that may be in "cut" command mode
associatedInstance.SlimContentPage.ItemManipulationModel.RefreshItemsOpacity();

foreach (ListedItem listedItem in associatedInstance.SlimContentPage.SelectedItems.ToList())
try
{
await Task.WhenAll(associatedInstance.SlimContentPage.SelectedItems.ToList().Select(async listedItem =>
{
// FTP don't support cut, fallback to copy
if (listedItem is not FtpItem)
{
{
// Dim opacities accordingly
listedItem.Opacity = Constants.UI.DimItemOpacity;
}

if (listedItem is FtpItem ftpItem)
{
canFlush = false;
if (listedItem.PrimaryItemAttribute == StorageItemTypes.File)
{
items.Add(await new FtpStorageFile(ftpItem).ToStorageFileAsync());
}
else if (listedItem.PrimaryItemAttribute == StorageItemTypes.Folder)

if (listedItem is FtpItem ftpItem)
{
items.Add(new FtpStorageFolder(ftpItem));
canFlush = false;
if (listedItem.PrimaryItemAttribute == StorageItemTypes.File)
{
items.Add(await new FtpStorageFile(ftpItem).ToStorageFileAsync());
}
else if (listedItem.PrimaryItemAttribute == StorageItemTypes.Folder)
{
items.Add(new FtpStorageFolder(ftpItem));
}
}
}
else if (listedItem.PrimaryItemAttribute == StorageItemTypes.File || listedItem is ZipItem)
{
result = await associatedInstance.FilesystemViewModel.GetFileFromPathAsync(listedItem.ItemPath)
.OnSuccess(t => items.Add(t));
if (!result)
else if (listedItem.PrimaryItemAttribute == StorageItemTypes.File || listedItem is ZipItem)
{
break;
result = await associatedInstance.FilesystemViewModel.GetFileFromPathAsync(listedItem.ItemPath)
.OnSuccess(t => items.Add(t));
if (!result)
{
throw new IOException($"Failed to process {listedItem.ItemPath}.");
}
}
}
else
{
result = await associatedInstance.FilesystemViewModel.GetFolderFromPathAsync(listedItem.ItemPath)
.OnSuccess(t => items.Add(t));
if (!result)
else
{
break;
result = await associatedInstance.FilesystemViewModel.GetFolderFromPathAsync(listedItem.ItemPath)
.OnSuccess(t => items.Add(t));
if (!result)
{
throw new IOException($"Failed to process {listedItem.ItemPath}.");
}
}
}
}));
}
catch
{
return;
}

if (result.ErrorCode == FileSystemStatusCode.NotFound)
{
associatedInstance.SlimContentPage.ItemManipulationModel.RefreshItemsOpacity();
Expand Down Expand Up @@ -105,7 +115,7 @@ public static async void CutItem(IShellPage associatedInstance)
var onlyStandard = items.All(x => x is StorageFile || x is StorageFolder || x is SystemStorageFile || x is SystemStorageFolder);
if (onlyStandard)
{
items = await items.ToStandardStorageItemsAsync();
items = new ConcurrentBag<IStorageItem>(await items.ToStandardStorageItemsAsync());
}
if (!items.Any())
{
Expand All @@ -132,47 +142,55 @@ public static async Task CopyItem(IShellPage associatedInstance)
{
RequestedOperation = DataPackageOperation.Copy
};
List<IStorageItem> items = new List<IStorageItem>();
ConcurrentBag<IStorageItem> items = new ConcurrentBag<IStorageItem>();

string copySourcePath = associatedInstance.FilesystemViewModel.WorkingDirectory;
FilesystemResult result = (FilesystemResult)false;

var canFlush = true;
if (associatedInstance.SlimContentPage.IsItemSelected)
{
foreach (ListedItem listedItem in associatedInstance.SlimContentPage.SelectedItems.ToList())
try
{
if (listedItem is FtpItem ftpItem)
await Task.WhenAll(associatedInstance.SlimContentPage.SelectedItems.ToList().Select(async listedItem =>
{
canFlush = false;
if (listedItem.PrimaryItemAttribute == StorageItemTypes.File)
if (listedItem is FtpItem ftpItem)
{
items.Add(await new FtpStorageFile(ftpItem).ToStorageFileAsync());
canFlush = false;
if (listedItem.PrimaryItemAttribute == StorageItemTypes.File)
{
items.Add(await new FtpStorageFile(ftpItem).ToStorageFileAsync());
}
else if (listedItem.PrimaryItemAttribute == StorageItemTypes.Folder)
{
items.Add(new FtpStorageFolder(ftpItem));
}
}
else if (listedItem.PrimaryItemAttribute == StorageItemTypes.Folder)
{
items.Add(new FtpStorageFolder(ftpItem));
}
}
else if (listedItem.PrimaryItemAttribute == StorageItemTypes.File || listedItem is ZipItem)
{
result = await associatedInstance.FilesystemViewModel.GetFileFromPathAsync(listedItem.ItemPath)
.OnSuccess(t => items.Add(t));
if (!result)
else if (listedItem.PrimaryItemAttribute == StorageItemTypes.File || listedItem is ZipItem)
{
break;
result = await associatedInstance.FilesystemViewModel.GetFileFromPathAsync(listedItem.ItemPath)
.OnSuccess(t => items.Add(t));
if (!result)
{
throw new IOException($"Failed to process {listedItem.ItemPath}.");
}
}
}
else
{
result = await associatedInstance.FilesystemViewModel.GetFolderFromPathAsync(listedItem.ItemPath)
.OnSuccess(t => items.Add(t));
if (!result)
else
{
break;
result = await associatedInstance.FilesystemViewModel.GetFolderFromPathAsync(listedItem.ItemPath)
.OnSuccess(t => items.Add(t));
if (!result)
{
throw new IOException($"Failed to process {listedItem.ItemPath}.");
}
}
}
}));
}
catch
{
return;
}

if (result.ErrorCode == FileSystemStatusCode.Unauthorized)
{
// Try again with fulltrust process
Expand All @@ -195,7 +213,7 @@ await connection.SendMessageAsync(new ValueSet()
var onlyStandard = items.All(x => x is StorageFile || x is StorageFolder || x is SystemStorageFile || x is SystemStorageFolder);
if (onlyStandard)
{
items = await items.ToStandardStorageItemsAsync();
items = new ConcurrentBag<IStorageItem>(await items.ToStandardStorageItemsAsync());
}
if (!items.Any())
{
Expand Down
7 changes: 3 additions & 4 deletions Files/Interacts/BaseLayoutCommandImplementationModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -199,11 +199,10 @@ await FilesystemHelpers.RestoreFromTrashAsync(StorageItemHelpers.FromPathAndType

public virtual async void DeleteItem(RoutedEventArgs e)
{
await FilesystemHelpers.DeleteItemsAsync(
SlimContentPage.SelectedItems.Select((item) => StorageItemHelpers.FromPathAndType(
var items = await Task.WhenAll(SlimContentPage.SelectedItems.Select((item) => Task.Run(() => StorageItemHelpers.FromPathAndType(
item.ItemPath,
item.PrimaryItemAttribute == StorageItemTypes.File ? FilesystemItemType.File : FilesystemItemType.Directory)).ToList(),
true, false, true);
item.PrimaryItemAttribute == StorageItemTypes.File ? FilesystemItemType.File : FilesystemItemType.Directory))));
await FilesystemHelpers.DeleteItemsAsync(items, true, false, true);
}

public virtual void ShowFolderProperties(RoutedEventArgs e)
Expand Down
14 changes: 6 additions & 8 deletions Files/Views/ColumnShellPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -616,11 +616,10 @@ private async void KeyboardAccelerator_Invoked(KeyboardAccelerator sender, Keybo
case (false, true, false, true, VirtualKey.Delete): // shift + delete, PermanentDelete
if (ContentPage.IsItemSelected && !NavToolbarViewModel.IsEditModeEnabled && !InstanceViewModel.IsPageTypeSearchResults)
{
await FilesystemHelpers.DeleteItemsAsync(
ContentPage.SelectedItems.Select((item) => StorageItemHelpers.FromPathAndType(
var items = await Task.WhenAll(ContentPage.SelectedItems.Select((item) => Task.Run(() => StorageItemHelpers.FromPathAndType(
item.ItemPath,
item.PrimaryItemAttribute == StorageItemTypes.File ? FilesystemItemType.File : FilesystemItemType.Directory)).ToList(),
true, true, true);
item.PrimaryItemAttribute == StorageItemTypes.File ? FilesystemItemType.File : FilesystemItemType.Directory))));
await FilesystemHelpers.DeleteItemsAsync(items, true, true, true);
}

break;
Expand Down Expand Up @@ -661,11 +660,10 @@ await FilesystemHelpers.DeleteItemsAsync(
case (false, false, false, true, VirtualKey.Delete): // delete, delete item
if (ContentPage.IsItemSelected && !ContentPage.IsRenamingItem && !InstanceViewModel.IsPageTypeSearchResults)
{
await FilesystemHelpers.DeleteItemsAsync(
ContentPage.SelectedItems.Select((item) => StorageItemHelpers.FromPathAndType(
var items = await Task.WhenAll(ContentPage.SelectedItems.Select((item) => Task.Run(() => StorageItemHelpers.FromPathAndType(
item.ItemPath,
item.PrimaryItemAttribute == StorageItemTypes.File ? FilesystemItemType.File : FilesystemItemType.Directory)).ToList(),
true, false, true);
item.PrimaryItemAttribute == StorageItemTypes.File ? FilesystemItemType.File : FilesystemItemType.Directory))));
await FilesystemHelpers.DeleteItemsAsync(items, true, false, true);
}

break;
Expand Down
14 changes: 6 additions & 8 deletions Files/Views/ModernShellPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -661,11 +661,10 @@ private async void KeyboardAccelerator_Invoked(KeyboardAccelerator sender, Keybo
case (false, true, false, true, VirtualKey.Delete): // shift + delete, PermanentDelete
if (ContentPage.IsItemSelected && !NavToolbarViewModel.IsEditModeEnabled && !InstanceViewModel.IsPageTypeSearchResults)
{
await FilesystemHelpers.DeleteItemsAsync(
ContentPage.SelectedItems.Select((item) => StorageItemHelpers.FromPathAndType(
var items = await Task.WhenAll(ContentPage.SelectedItems.Select((item) => Task.Run(() => StorageItemHelpers.FromPathAndType(
item.ItemPath,
item.PrimaryItemAttribute == StorageItemTypes.File ? FilesystemItemType.File : FilesystemItemType.Directory)).ToList(),
true, true, true);
item.PrimaryItemAttribute == StorageItemTypes.File ? FilesystemItemType.File : FilesystemItemType.Directory))));
await FilesystemHelpers.DeleteItemsAsync(items, true, true, true);
}

break;
Expand Down Expand Up @@ -706,11 +705,10 @@ await FilesystemHelpers.DeleteItemsAsync(
case (false, false, false, true, VirtualKey.Delete): // delete, delete item
if (ContentPage.IsItemSelected && !ContentPage.IsRenamingItem && !InstanceViewModel.IsPageTypeSearchResults)
{
await FilesystemHelpers.DeleteItemsAsync(
ContentPage.SelectedItems.Select((item) => StorageItemHelpers.FromPathAndType(
var items = await Task.WhenAll(ContentPage.SelectedItems.Select((item) => Task.Run(() => StorageItemHelpers.FromPathAndType(
item.ItemPath,
item.PrimaryItemAttribute == StorageItemTypes.File ? FilesystemItemType.File : FilesystemItemType.Directory)).ToList(),
true, false, true);
item.PrimaryItemAttribute == StorageItemTypes.File ? FilesystemItemType.File : FilesystemItemType.Directory))));
await FilesystemHelpers.DeleteItemsAsync(items, true, false, true);
}

break;
Expand Down

0 comments on commit 026a5f2

Please sign in to comment.