public async Task <IStorageHistory> CopyItemsAsync(IEnumerable <IStorageItemWithPath> source, IEnumerable <string> destination, IEnumerable <FileNameConflictResolveOptionType> collisions, IProgress <float> progress, IProgress <FileSystemStatusCode> errorCode, CancellationToken cancellationToken) { var connection = await AppServiceConnectionHelper.Instance; if (connection == null || source.Any(x => string.IsNullOrWhiteSpace(x.Path) || x.Path.StartsWith(@"\\?\")) || destination.Any(x => string.IsNullOrWhiteSpace(x) || x.StartsWith(@"\\?\"))) { // Fallback to builtin file operations return(await filesystemOperations.CopyItemsAsync(source, destination, collisions, progress, errorCode, cancellationToken)); } source = source.Where((src, index) => collisions.ElementAt(index) != FileNameConflictResolveOptionType.Skip).ToList(); destination = destination.Where((src, index) => collisions.ElementAt(index) != FileNameConflictResolveOptionType.Skip).ToList(); collisions = collisions.Where(c => c != FileNameConflictResolveOptionType.Skip).ToList(); var operationID = Guid.NewGuid().ToString(); using var _ = cancellationToken.Register(CancelOperation, operationID, false); EventHandler <Dictionary <string, object> > handler = (s, e) => OnProgressUpdated(s, e, progress); connection.RequestReceived += handler; var sourceReplace = source.Where((src, index) => collisions.ElementAt(index) == FileNameConflictResolveOptionType.ReplaceExisting); var destinationReplace = destination.Where((src, index) => collisions.ElementAt(index) == FileNameConflictResolveOptionType.ReplaceExisting); var sourceRename = source.Where((src, index) => collisions.ElementAt(index) != FileNameConflictResolveOptionType.ReplaceExisting); var destinationRename = destination.Where((src, index) => collisions.ElementAt(index) != FileNameConflictResolveOptionType.ReplaceExisting); var result = (FilesystemResult)true; var copiedItems = new List <string>(); var copiedSources = new List <string>(); if (sourceRename.Any()) { var(status, response) = await connection.SendMessageForResponseAsync(new ValueSet() { { "Arguments", "FileOperation" }, { "fileop", "CopyItem" }, { "operationID", operationID }, { "filepath", string.Join('|', sourceRename.Select(s => s.Path)) }, { "destpath", string.Join('|', destinationRename) }, { "overwrite", false } }); result &= (FilesystemResult)(status == AppServiceResponseStatus.Success && response.Get("Success", false)); copiedItems.AddRange(JsonConvert.DeserializeObject <IEnumerable <string> >(response.Get("CopiedItems", "")) ?? Enumerable.Empty <string>()); copiedSources.AddRange(JsonConvert.DeserializeObject <IEnumerable <string> >(response.Get("CopiedSources", "")) ?? Enumerable.Empty <string>()); } if (sourceReplace.Any()) { var(status, response) = await connection.SendMessageForResponseAsync(new ValueSet() { { "Arguments", "FileOperation" }, { "fileop", "CopyItem" }, { "operationID", operationID }, { "filepath", string.Join('|', sourceReplace.Select(s => s.Path)) }, { "destpath", string.Join('|', destinationReplace) }, { "overwrite", true } }); result &= (FilesystemResult)(status == AppServiceResponseStatus.Success && response.Get("Success", false)); copiedSources.AddRange(JsonConvert.DeserializeObject <IEnumerable <string> >(response.Get("CopiedSources", "")) ?? Enumerable.Empty <string>()); } if (connection != null) { connection.RequestReceived -= handler; } if (result) { progress?.Report(100.0f); errorCode?.Report(FileSystemStatusCode.Success); if (sourceRename.Any() && copiedItems.Count() == sourceRename.Count()) { return(new StorageHistory(FileOperationType.Copy, sourceRename, copiedItems.Select((item, index) => StorageItemHelpers.FromPathAndType(item, sourceRename.ElementAt(index).ItemType)))); } return(null); // Cannot undo overwrite operation } else { // Retry failed operations var copiedZip = source.Zip(destination, (src, dest) => new { src, dest }).Zip(collisions, (z1, coll) => new { z1.src, z1.dest, coll }).Where(x => !copiedSources.Contains(x.src.Path)); return(await filesystemOperations.CopyItemsAsync(copiedZip.Select(x => x.src), copiedZip.Select(x => x.dest), copiedZip.Select(x => x.coll), progress, errorCode, cancellationToken)); } }
public async Task <IStorageHistory> CopyItemsAsync(IList <IStorageItemWithPath> source, IList <string> destination, IList <FileNameConflictResolveOptionType> collisions, IProgress <float> progress, IProgress <FileSystemStatusCode> errorCode, CancellationToken cancellationToken) { var connection = await AppServiceConnectionHelper.Instance; if (connection == null || source.Any(x => string.IsNullOrWhiteSpace(x.Path) || x.Path.StartsWith(@"\\?\", StringComparison.Ordinal)) || destination.Any(x => string.IsNullOrWhiteSpace(x) || x.StartsWith(@"\\?\", StringComparison.Ordinal) || FtpHelpers.IsFtpPath(x))) { // Fallback to builtin file operations return(await filesystemOperations.CopyItemsAsync(source, destination, collisions, progress, errorCode, cancellationToken)); } var sourceNoSkip = source.Zip(collisions, (src, coll) => new { src, coll }).Where(item => item.coll != FileNameConflictResolveOptionType.Skip).Select(item => item.src); var destinationNoSkip = destination.Zip(collisions, (src, coll) => new { src, coll }).Where(item => item.coll != FileNameConflictResolveOptionType.Skip).Select(item => item.src); var collisionsNoSkip = collisions.Where(c => c != FileNameConflictResolveOptionType.Skip); var operationID = Guid.NewGuid().ToString(); using var r = cancellationToken.Register(CancelOperation, operationID, false); EventHandler <Dictionary <string, object> > handler = (s, e) => OnProgressUpdated(s, e, operationID, progress); connection.RequestReceived += handler; var sourceReplace = sourceNoSkip.Zip(collisionsNoSkip, (src, coll) => new { src, coll }).Where(item => item.coll == FileNameConflictResolveOptionType.ReplaceExisting).Select(item => item.src); var destinationReplace = destinationNoSkip.Zip(collisionsNoSkip, (src, coll) => new { src, coll }).Where(item => item.coll == FileNameConflictResolveOptionType.ReplaceExisting).Select(item => item.src); var sourceRename = sourceNoSkip.Zip(collisionsNoSkip, (src, coll) => new { src, coll }).Where(item => item.coll != FileNameConflictResolveOptionType.ReplaceExisting).Select(item => item.src); var destinationRename = destinationNoSkip.Zip(collisionsNoSkip, (src, coll) => new { src, coll }).Where(item => item.coll != FileNameConflictResolveOptionType.ReplaceExisting).Select(item => item.src); var result = (FilesystemResult)true; var copyResult = new ShellOperationResult(); if (sourceRename.Any()) { var(status, response) = await connection.SendMessageForResponseAsync(new ValueSet() { { "Arguments", "FileOperation" }, { "fileop", "CopyItem" }, { "operationID", operationID }, { "filepath", string.Join('|', sourceRename.Select(s => s.Path)) }, { "destpath", string.Join('|', destinationRename) }, { "overwrite", false }, { "HWND", NativeWinApiHelper.CoreWindowHandle.ToInt64() } }); result &= (FilesystemResult)(status == AppServiceResponseStatus.Success && response.Get("Success", false)); var shellOpResult = JsonConvert.DeserializeObject <ShellOperationResult>(response.Get("Result", "")); copyResult.Items.AddRange(shellOpResult?.Items ?? Enumerable.Empty <ShellOperationItemResult>()); } if (sourceReplace.Any()) { var(status, response) = await connection.SendMessageForResponseAsync(new ValueSet() { { "Arguments", "FileOperation" }, { "fileop", "CopyItem" }, { "operationID", operationID }, { "filepath", string.Join('|', sourceReplace.Select(s => s.Path)) }, { "destpath", string.Join('|', destinationReplace) }, { "overwrite", true }, { "HWND", NativeWinApiHelper.CoreWindowHandle.ToInt64() } }); result &= (FilesystemResult)(status == AppServiceResponseStatus.Success && response.Get("Success", false)); var shellOpResult = JsonConvert.DeserializeObject <ShellOperationResult>(response.Get("Result", "")); copyResult.Items.AddRange(shellOpResult?.Items ?? Enumerable.Empty <ShellOperationItemResult>()); } if (connection != null) { connection.RequestReceived -= handler; } result &= (FilesystemResult)copyResult.Items.All(x => x.Succeeded); if (result) { progress?.Report(100.0f); errorCode?.Report(FileSystemStatusCode.Success); var copiedSources = copyResult.Items.Where(x => x.Succeeded && x.Destination != null && x.Source != x.Destination); if (copiedSources.Any()) { var sourceMatch = await copiedSources.Select(x => sourceRename.SingleOrDefault(s => s.Path == x.Source)).Where(x => x != null).ToListAsync(); return(new StorageHistory(FileOperationType.Copy, sourceMatch, await copiedSources.Zip(sourceMatch, (rSrc, oSrc) => new { rSrc, oSrc }) .Select(item => StorageHelpers.FromPathAndType(item.rSrc.Destination, item.oSrc.ItemType)).ToListAsync())); } return(null); // Cannot undo overwrite operation } else { // Retry failed operations var failedSources = copyResult.Items.Where(x => !x.Succeeded); var copyZip = sourceNoSkip.Zip(destinationNoSkip, (src, dest) => new { src, dest }).Zip(collisionsNoSkip, (z1, coll) => new { z1.src, z1.dest, coll }); var sourceMatch = await failedSources.Select(x => copyZip.SingleOrDefault(s => s.src.Path == x.Source)).Where(x => x != null).ToListAsync(); return(await filesystemOperations.CopyItemsAsync( await sourceMatch.Select(x => x.src).ToListAsync(), await sourceMatch.Select(x => x.dest).ToListAsync(), await sourceMatch.Select(x => x.coll).ToListAsync(), progress, errorCode, cancellationToken)); } }