-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathFileCopier.cs
More file actions
109 lines (95 loc) · 4.34 KB
/
Copy pathFileCopier.cs
File metadata and controls
109 lines (95 loc) · 4.34 KB
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
using System;
using System.IO;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
namespace RoboCopyApp
{
public class FileCopier
{
public static async Task CopyFileAsync(
FileTransferTask task,
int bufferSize,
int maxRetries,
bool verifyChecksum,
Action<long> onProgress,
CancellationToken cancellationToken)
{
int currentTry = 0;
while (currentTry <= maxRetries)
{
try
{
task.State = TransferState.Transferring;
Directory.CreateDirectory(Path.GetDirectoryName(task.DestinationPath)!);
using (var sourceStream = new FileStream(task.SourcePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize, FileOptions.Asynchronous | FileOptions.SequentialScan))
using (var destStream = new FileStream(task.DestinationPath, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize, FileOptions.Asynchronous))
{
byte[] buffer = new byte[bufferSize];
int bytesRead;
while ((bytesRead = await sourceStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0)
{
await destStream.WriteAsync(buffer, 0, bytesRead, cancellationToken);
onProgress(bytesRead);
}
}
if (verifyChecksum)
{
task.State = TransferState.Verifying;
bool isValid = await VerifyChecksumsAsync(task.SourcePath, task.DestinationPath, bufferSize, cancellationToken);
if (!isValid)
{
throw new Exception("Checksum verification failed. Data corrupted during transfer.");
}
}
task.State = TransferState.Completed;
return; // Success
}
catch (OperationCanceledException)
{
task.State = TransferState.Failed;
throw;
}
catch (Exception ex)
{
currentTry++;
task.RetryCount = currentTry;
task.ErrorMessage = ex.Message;
if (currentTry > maxRetries)
{
task.State = TransferState.Failed;
throw;
}
// Wait before retrying (exponential backoff)
await Task.Delay(1000 * currentTry, cancellationToken);
}
}
}
private static async Task<bool> VerifyChecksumsAsync(string source, string dest, int bufferSize, CancellationToken ct)
{
using var sha256 = SHA256.Create();
byte[] hashSource;
byte[] hashDest;
using (var fsSource = new FileStream(source, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize, FileOptions.Asynchronous | FileOptions.SequentialScan))
{
hashSource = await ComputeHashAsync(sha256, fsSource, bufferSize, ct);
}
using (var fsDest = new FileStream(dest, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize, FileOptions.Asynchronous | FileOptions.SequentialScan))
{
hashDest = await ComputeHashAsync(sha256, fsDest, bufferSize, ct);
}
return CryptographicOperations.FixedTimeEquals(hashSource, hashDest);
}
private static async Task<byte[]> ComputeHashAsync(HashAlgorithm algorithm, Stream stream, int bufferSize, CancellationToken ct)
{
byte[] buffer = new byte[bufferSize];
int bytesRead;
while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, ct)) > 0)
{
algorithm.TransformBlock(buffer, 0, bytesRead, buffer, 0);
}
algorithm.TransformFinalBlock(buffer, 0, 0);
return algorithm.Hash!;
}
}
}