将文件夹移至UWP中另一个位置的最快方法



我有SSD存储,此代码需要32秒才能将〜200个文件和〜40个文件夹移至相同的存储在调试或发布模式下。文件夹的总尺寸为〜30 mb。

我该如何更快地制作?

// moves content from local folder to target folder.
async Task MoveContent(IStorageFolder source, IStorageFolder destination)
{
    foreach(var item in await source.GetItemsAsync())
    {
        switch (item)
        {
            case IStorageFile sourceFile:
                await sourceFile.MoveAsync(destination, sourceFile.Name, NameCollisionOption.ReplaceExisting);
                break;
            case IStorageFolder sourceSubFolder:
                var destinationSubFolder = await destination.CreateFolderAsync(sourceSubFolder.Name, CreationCollisionOption.ReplaceExisting);
                await MoveContent(sourceSubFolder, destinationSubFolder);
                break;
        }
    }
}

我称之为这个

await MoveContent(extractionFolder, targetFolder);

请注意,extractionFolderApplicationData.Current.LocalCacheFolder中,targetFolder是用户通过FolderPicker

选择的任何文件夹

您发布的代码有几个问题:

  1. 您将文件I/O操作驱动一一,然后等待其完成。由于UWP中的文件I/O是经过的,因此涉及致电另一个过程。由于大部分时间都是在过程之间进行交流的,因此您会被自己的等待所束缚。在此期间,您的磁盘根本没有活动。

  2. winrt文件I/O API是明智的垃圾性能。您想尽可能多地避免它。由于您可以正确访问源路径,因此应使用C#DirectoryInfo类列举文件。然后,使用c#file i/o。

  3. 而不是使用moveAsync(因为您不再具有源为iStorageiTem)

随着这些更改,IT设法完成了我的合成测试用例(40个文件夹,每个文件中有5个文件)需要300毫秒,而使用您的代码为12秒。那快30倍。如果我们被允许使用Win32 API(例如MoveFile),但不幸的是,这可能会变得更快,但是不幸的是,目前无法使用文件/文件夹选择器选择的文件夹和文件。

这是代码。

        async Task MoveContentFast(IStorageFolder source, IStorageFolder destination)
        {
            await Task.Run(() =>
            {
                MoveContextImpl(new DirectoryInfo(source.Path), destination);
            });
        }
        private void MoveContextImpl(DirectoryInfo sourceFolderInfo, IStorageFolder destination)
        {
            var tasks = new List<Task>();
            var destinationAccess = destination as IStorageFolderHandleAccess;
            foreach (var item in sourceFolderInfo.EnumerateFileSystemInfos())
            {
                if ((item.Attributes & System.IO.FileAttributes.Directory) != 0)
                {
                    tasks.Add(destination.CreateFolderAsync(item.Name, CreationCollisionOption.ReplaceExisting).AsTask().ContinueWith((destinationSubFolder) =>
                    {
                        MoveContextImpl((DirectoryInfo)item, destinationSubFolder.Result);
                    }));
                }
                else
                {
                    if (destinationAccess == null)
                    {
                        // Slower, pre 14393 OS build path
                        tasks.Add(WindowsRuntimeStorageExtensions.OpenStreamForWriteAsync(destination, item.Name, CreationCollisionOption.ReplaceExisting).ContinueWith((openTask) =>
                        {
                            using (var stream = openTask.Result)
                            {
                                var sourceBytes = File.ReadAllBytes(item.FullName);
                                stream.Write(sourceBytes, 0, sourceBytes.Length);
                            }
                            File.Delete(item.FullName);
                        }));
                    }
                    else
                    {
                        int hr = destinationAccess.Create(item.Name, HANDLE_CREATION_OPTIONS.CREATE_ALWAYS, HANDLE_ACCESS_OPTIONS.WRITE, HANDLE_SHARING_OPTIONS.SHARE_NONE, HANDLE_OPTIONS.NONE, IntPtr.Zero, out SafeFileHandle file);
                        if (hr < 0)
                            Marshal.ThrowExceptionForHR(hr);
                        using (file)
                        {
                            using (var stream = new FileStream(file, FileAccess.Write))
                            {
                                var sourceBytes = File.ReadAllBytes(item.FullName);
                                stream.Write(sourceBytes, 0, sourceBytes.Length);
                            }
                        }
                        File.Delete(item.FullName);
                    }
                }
            }
            Task.WaitAll(tasks.ToArray());
        }
        [ComImport]
        [Guid("DF19938F-5462-48A0-BE65-D2A3271A08D6")]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        internal interface IStorageFolderHandleAccess
        {
            [PreserveSig]
            int Create(
                [MarshalAs(UnmanagedType.LPWStr)] string fileName,
                HANDLE_CREATION_OPTIONS creationOptions,
                HANDLE_ACCESS_OPTIONS accessOptions,
                HANDLE_SHARING_OPTIONS sharingOptions,
                HANDLE_OPTIONS options,
                IntPtr oplockBreakingHandler,
                out SafeFileHandle interopHandle); // using Microsoft.Win32.SafeHandles
        }
        internal enum HANDLE_CREATION_OPTIONS : uint
        {
            CREATE_NEW = 0x1,
            CREATE_ALWAYS = 0x2,
            OPEN_EXISTING = 0x3,
            OPEN_ALWAYS = 0x4,
            TRUNCATE_EXISTING = 0x5,
        }
        [Flags]
        internal enum HANDLE_ACCESS_OPTIONS : uint
        {
            NONE = 0,
            READ_ATTRIBUTES = 0x80,
            // 0x120089
            READ = SYNCHRONIZE | READ_CONTROL | READ_ATTRIBUTES | FILE_READ_EA | FILE_READ_DATA,
            // 0x120116
            WRITE = SYNCHRONIZE | READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | FILE_WRITE_DATA,
            DELETE = 0x10000,
            READ_CONTROL = 0x00020000,
            SYNCHRONIZE = 0x00100000,
            FILE_READ_DATA = 0x00000001,
            FILE_WRITE_DATA = 0x00000002,
            FILE_APPEND_DATA = 0x00000004,
            FILE_READ_EA = 0x00000008,
            FILE_WRITE_EA = 0x00000010,
            FILE_EXECUTE = 0x00000020,
            FILE_WRITE_ATTRIBUTES = 0x00000100,
        }
        [Flags]
        internal enum HANDLE_SHARING_OPTIONS : uint
        {
            SHARE_NONE = 0,
            SHARE_READ = 0x1,
            SHARE_WRITE = 0x2,
            SHARE_DELETE = 0x4
        }
        [Flags]
        internal enum HANDLE_OPTIONS : uint
        {
            NONE = 0,
            OPEN_REQUIRING_OPLOCK = 0x40000,
            DELETE_ON_CLOSE = 0x4000000,
            SEQUENTIAL_SCAN = 0x8000000,
            RANDOM_ACCESS = 0x10000000,
            NO_BUFFERING = 0x20000000,
            OVERLAPPED = 0x40000000,
            WRITE_THROUGH = 0x80000000
        }

要改善代码的性能,您可以尝试一次枚举文件夹和子文件夹中的所有文件,而不是在整个文件夹结构中(文件夹):

var results = storageFolder.CreateFileQueryWithOptions(
                  new QueryOptions() { FolderDepth = FolderDepth.Deep } );
var files = (await results.GetFilesAsync()).ToArray();

其中 storageFolder是要移动的文件夹。自定义文件查询将FolderDepth设置设置为Deep,以便从整个文件夹结构中返回所有文件。运行此操作后,files数组将包含所有文件,然后您可以移动它们。这将比一一列举所有文件夹要快一点。您只需要确保始终检查在目标位置创建适当的子文件夹即可。

最后,您可以尝试并行化MOVE Tasks - 例如一次移动三个文件。您可以使用Task.WhenAll创建多个Task实例,并使用await

复制 - 帕斯特解决方案

另一个快速而肮脏的解决方案将是使用StorageFolder.CopyAsync()方法将文件夹复制到新位置并删除原件(甚至在文档中建议):

当前没有" moveasync"或类似方法。移动文件夹的一个简单实现可能是获取所需的文件夹,将其复制到所需的位置,然后删除原始文件夹。

但是,额外存储空间的成本不是很吸引人,甚至可能不会提高性能,因为复制比移动更为昂贵。

uwp当前没有MoveAsync之类的东西。截至2019年8月。此答案的行为与moveAsync函数相似,并且假设您在UWP应用程序sandbox/local中工作声明,因为在沙箱中,您可以使用.NET使用经典的System.IO方法。只需在沙箱中使用后者,否则您可以使用此临时:

public static async Task Move_Directory_Async(
    StorageFolder           sourceDir,
    StorageFolder           destParentDir,
    CreationCollisionOption repDirOpt,
    NameCollisionOption     repFilesOpt)
{
    try
    {
        if (sourceDir == null)
            return;
        List<Task> copies = new List<Task>();
        var files = await sourceDir.GetFilesAsync();
        if (files == null || files.Count == 0)
            await destParentDir.CreateFolderAsync(sourceDir.Name);
        else
        {
            await destParentDir.CreateFolderAsync(sourceDir.Name, repDirOpt);
            foreach (var file in files)
                copies.Add(file.CopyAsync(destParentDir, file.Name, repFilesOpt).AsTask());
        }
        await sourceDir.DeleteAsync(StorageDeleteOption.PermanentDelete);
        await Task.WhenAll(copies);
    }
    catch(Exception ex)
    {
        //Handle any needed cleanup tasks here
        throw new Exception(
          $"A fatal exception triggered within Move_Directory_Async:rn{ex.Message}", ex);
    }
}

相关内容

  • 没有找到相关文章

最新更新