|
6 | 6 | using System.Collections.Generic; |
7 | 7 | using System.Collections.ObjectModel; |
8 | 8 | using System.ComponentModel; |
| 9 | +using System.Diagnostics; |
9 | 10 | using System.Diagnostics.CodeAnalysis; |
10 | 11 | using System.Globalization; |
11 | 12 | using System.IO; |
|
18 | 19 | using System.Security; |
19 | 20 | using System.Security.AccessControl; |
20 | 21 | using System.Text; |
| 22 | +using System.Threading.Tasks; |
21 | 23 | using System.Xml; |
22 | 24 | using System.Xml.XPath; |
23 | 25 |
|
@@ -58,6 +60,8 @@ public sealed partial class FileSystemProvider : NavigationCmdletProvider, |
58 | 60 | // copy script will accommodate the new value. |
59 | 61 | private const int FILETRANSFERSIZE = 4 * 1024 * 1024; |
60 | 62 |
|
| 63 | + private const int COPY_FILE_ACTIVITY_ID = 0; |
| 64 | + |
61 | 65 | // The name of the key in an exception's Data dictionary when attempting |
62 | 66 | // to copy an item onto itself. |
63 | 67 | private const string SelfCopyDataKey = "SelfCopy"; |
@@ -3548,14 +3552,73 @@ protected override void CopyItem( |
3548 | 3552 | } |
3549 | 3553 | else // Copy-Item local |
3550 | 3554 | { |
| 3555 | + if (Context != null && Context.ExecutionContext.SessionState.PSVariable.Get(SpecialVariables.ProgressPreferenceVarPath.UserPath).Value is ActionPreference progressPreference && progressPreference == ActionPreference.Continue) |
| 3556 | + { |
| 3557 | + { |
| 3558 | + Task.Run(() => |
| 3559 | + { |
| 3560 | + GetTotalFiles(path, recurse); |
| 3561 | + }); |
| 3562 | + _copyStopwatch.Start(); |
| 3563 | + } |
| 3564 | + } |
| 3565 | + |
3551 | 3566 | CopyItemLocalOrToSession(path, destinationPath, recurse, Force, null); |
| 3567 | + if (_totalFiles > 0) |
| 3568 | + { |
| 3569 | + _copyStopwatch.Stop(); |
| 3570 | + var progress = new ProgressRecord(COPY_FILE_ACTIVITY_ID, " ", " "); |
| 3571 | + progress.RecordType = ProgressRecordType.Completed; |
| 3572 | + WriteProgress(progress); |
| 3573 | + } |
3552 | 3574 | } |
3553 | 3575 | } |
3554 | 3576 |
|
3555 | 3577 | _excludeMatcher.Clear(); |
3556 | 3578 | _excludeMatcher = null; |
3557 | 3579 | } |
3558 | 3580 |
|
| 3581 | + private void GetTotalFiles(string path, bool recurse) |
| 3582 | + { |
| 3583 | + bool isContainer = IsItemContainer(path); |
| 3584 | + |
| 3585 | + try |
| 3586 | + { |
| 3587 | + if (isContainer) |
| 3588 | + { |
| 3589 | + var enumOptions = new EnumerationOptions() |
| 3590 | + { |
| 3591 | + IgnoreInaccessible = true, |
| 3592 | + AttributesToSkip = 0, |
| 3593 | + RecurseSubdirectories = recurse |
| 3594 | + }; |
| 3595 | + |
| 3596 | + var directory = new DirectoryInfo(path); |
| 3597 | + foreach (var file in directory.EnumerateFiles("*", enumOptions)) |
| 3598 | + { |
| 3599 | + if (!SessionStateUtilities.MatchesAnyWildcardPattern(file.Name, _excludeMatcher, defaultValue: false)) |
| 3600 | + { |
| 3601 | + _totalFiles++; |
| 3602 | + _totalBytes += file.Length; |
| 3603 | + } |
| 3604 | + } |
| 3605 | + } |
| 3606 | + else |
| 3607 | + { |
| 3608 | + var file = new FileInfo(path); |
| 3609 | + if (!SessionStateUtilities.MatchesAnyWildcardPattern(file.Name, _excludeMatcher, defaultValue: false)) |
| 3610 | + { |
| 3611 | + _totalFiles++; |
| 3612 | + _totalBytes += file.Length; |
| 3613 | + } |
| 3614 | + } |
| 3615 | + } |
| 3616 | + catch |
| 3617 | + { |
| 3618 | + // ignore exception |
| 3619 | + } |
| 3620 | + } |
| 3621 | + |
3559 | 3622 | private void CopyItemFromRemoteSession(string path, string destinationPath, bool recurse, bool force, PSSession fromSession) |
3560 | 3623 | { |
3561 | 3624 | using (System.Management.Automation.PowerShell ps = System.Management.Automation.PowerShell.Create()) |
@@ -3864,6 +3927,22 @@ private void CopyFileInfoItem(FileInfo file, string destinationPath, bool force, |
3864 | 3927 |
|
3865 | 3928 | FileInfo result = new FileInfo(destinationPath); |
3866 | 3929 | WriteItemObject(result, destinationPath, false); |
| 3930 | + |
| 3931 | + if (_totalFiles > 0) |
| 3932 | + { |
| 3933 | + _copiedFiles++; |
| 3934 | + _copiedBytes += file.Length; |
| 3935 | + double speed = (double)(_copiedBytes / 1024 / 1024) / _copyStopwatch.Elapsed.TotalSeconds; |
| 3936 | + var progress = new ProgressRecord( |
| 3937 | + COPY_FILE_ACTIVITY_ID, |
| 3938 | + StringUtil.Format(FileSystemProviderStrings.CopyingLocalFileActivity, _copiedFiles, _totalFiles), |
| 3939 | + StringUtil.Format(FileSystemProviderStrings.CopyingLocalBytesStatus, Utils.DisplayHumanReadableFileSize(_copiedBytes), Utils.DisplayHumanReadableFileSize(_totalBytes), speed) |
| 3940 | + ); |
| 3941 | + var percentComplete = (int)Math.Min(_copiedBytes * 100 / _totalBytes, 100); |
| 3942 | + progress.PercentComplete = percentComplete; |
| 3943 | + progress.RecordType = ProgressRecordType.Processing; |
| 3944 | + WriteProgress(progress); |
| 3945 | + } |
3867 | 3946 | } |
3868 | 3947 | else |
3869 | 3948 | { |
@@ -4788,6 +4867,12 @@ private bool PathIsReservedDeviceName(string destinationPath, string errorId) |
4788 | 4867 | return pathIsReservedDeviceName; |
4789 | 4868 | } |
4790 | 4869 |
|
| 4870 | + private long _totalFiles; |
| 4871 | + private long _totalBytes; |
| 4872 | + private long _copiedFiles; |
| 4873 | + private long _copiedBytes; |
| 4874 | + private readonly Stopwatch _copyStopwatch = new Stopwatch(); |
| 4875 | + |
4791 | 4876 | #endregion CopyItem |
4792 | 4877 |
|
4793 | 4878 | #endregion ContainerCmdletProvider members |
|
0 commit comments