由系统提供的storagefile类提供的copy方法并没有提供可以提供显示进度的方法,在复制大文件时就会很不友好。
下面我提供一种显示进度的方法。
复制顾名思义,就是读取再写入。
所以我们通过storagefile读取文件流并写入另一个文件流。
在每一次读取时更新进度。
但是这有一个问题。根据不同缓存区的大小,这个东西不会太实时。如果用来获取速度,由于缓存区大小设置,获得的速度不准确。
有人说吧缓存区调小一点,我明确的告诉你,这个会影响拷贝的速度。
以下是具体代码
我创建了三个CopyFIle方法使可以支持文件夹
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
| private async Task CopyFile(IStorageItem s1, StorageFolder s2) { RfileID = 0; if (s1 is StorageFile s3) { await CopyFile(s3, s2); } else if (s1 is StorageFolder s4) { await CopyFile(s4, s2); } if (token) { CurrentCopyStatus = CopyStatus.paused; return; }
} private async Task CopyFile(StorageFolder s1, StorageFolder s2) { var items = await s1.GetItemsAsync(); var s3 = await s2.CreateFolderAsync(s1.Name, CreationCollisionOption.OpenIfExists); foreach (var item in items) { if (item is StorageFile file) { await CopyFile(file, s3); } else if (item is StorageFolder folder) { await CopyFile(folder, s3); } if (token) { CurrentCopyStatus = CopyStatus.paused; return; } }
} private async Task CopyFile(StorageFile s1, StorageFolder s2) { if (fileID > RfileID) { RfileID++; } else {
CurrentFileName = s1.Name; var size = Convert.ToDouble((await s1.GetBasicPropertiesAsync()).Size); Fin = FinishFileSize; using (var ReadStream = await s1.OpenAsync(FileAccessMode.Read)) { using (var WriteStream = await (await s2.CreateFileAsync(s1.Name, CreationCollisionOption.ReplaceExisting)).OpenAsync(FileAccessMode.ReadWrite)) { ulong Position = filePosion; for (int i = 0; ; i++) { var buffer = new Windows.Storage.Streams.Buffer(BufferSize); ReadStream.Seek(Position); await ReadStream.ReadAsync(buffer, buffer.Capacity, InputStreamOptions.Partial); WriteStream.Seek(Position);
Progress<uint> ip = new Progress<uint>(FileProgress); await WriteStream.WriteAsync(buffer).AsTask(ip);
FinishFileSize += buffer.Capacity;
await WriteStream.FlushAsync();
Position = WriteStream.Position; filePosion = Position;
if (buffer.Length < buffer.Capacity) { filePosion = 0; break; } if (token) { CurrentCopyStatus = CopyStatus.paused; return; } GC.Collect(); } RfileID++; fileID++; } } } }
|
其中以下代码通过用缓冲区获得Buffer再写入到文件,在写入后更新总共写入的文件字节。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| using (var ReadStream = await s1.OpenAsync(FileAccessMode.Read)) { using (var WriteStream = await (await s2.CreateFileAsync(s1.Name, CreationCollisionOption.ReplaceExisting)).OpenAsync(FileAccessMode.ReadWrite)) { ulong Position = filePosion; for (int i = 0; ; i++) { var buffer = new Windows.Storage.Streams.Buffer(BufferSize); ReadStream.Seek(Position); await ReadStream.ReadAsync(buffer, buffer.Capacity, InputStreamOptions.Partial); WriteStream.Seek(Position);
await WriteStream.WriteAsync(buffer);
FinishFileSize += buffer.Capacity;
await WriteStream.FlushAsync();
Position = WriteStream.Position; filePosion = Position;
if (buffer.Length < buffer.Capacity)//是buffer的容量,不是大小 { filePosion = 0; break; } if (token) { CurrentCopyStatus = CopyStatus.paused; return; } GC.Collect();//清理 } RfileID++; fileID++; } }
|
然后在更新总写入字节数时根据之前算好的文件大小获得进度数据
1 2 3 4 5
| public ulong FinishFileSize { get => _finishFileSize; set { _finishFileSize = value; CurrentProgress = (double)value / AllFileSize * 100; OnPropertyChanged(); } }
|
如果使用数据绑定到UI使用以下代码更新,注意相关类要继承INotifyPropertyChanged
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #region 用于数据绑定更新 public event PropertyChangedEventHandler PropertyChanged = delegate { };
public async void OnPropertyChanged([CallerMemberName] string propertyName = null) { try { await CoreApplication.GetCurrentView().Dispatcher.AwaitableRunAsync(() => { this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); }); } catch (Exception) {
} } #endregion
|
当然你可以设置计时器来计算copy的大致上的速度,这里不再描述。