root / trunk / Pithos.Core / Agents / Downloader.cs @ 2f5fcd2f
History | View | Annotate | Download (19 kB)
1 | d21f3c77 | pkanavos | using System; |
---|---|---|---|
2 | d21f3c77 | pkanavos | using System.ComponentModel.Composition; |
3 | d21f3c77 | pkanavos | using System.Diagnostics.Contracts; |
4 | d21f3c77 | pkanavos | using System.IO; |
5 | d21f3c77 | pkanavos | using System.Reflection; |
6 | d21f3c77 | pkanavos | using System.Threading; |
7 | d21f3c77 | pkanavos | using System.Threading.Tasks; |
8 | d21f3c77 | pkanavos | using Pithos.Interfaces; |
9 | d21f3c77 | pkanavos | using Pithos.Network; |
10 | d21f3c77 | pkanavos | using log4net; |
11 | d21f3c77 | pkanavos | |
12 | d21f3c77 | pkanavos | namespace Pithos.Core.Agents |
13 | d21f3c77 | pkanavos | { |
14 | d21f3c77 | pkanavos | [Export(typeof(Downloader))] |
15 | d21f3c77 | pkanavos | public class Downloader |
16 | d21f3c77 | pkanavos | { |
17 | d21f3c77 | pkanavos | private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
18 | d21f3c77 | pkanavos | |
19 | d21f3c77 | pkanavos | [Import] |
20 | d21f3c77 | pkanavos | private IStatusKeeper StatusKeeper { get; set; } |
21 | d21f3c77 | pkanavos | |
22 | f2adbfc9 | pkanavos | [Import] |
23 | f2adbfc9 | pkanavos | private IPithosSettings Settings { get; set; } |
24 | d21f3c77 | pkanavos | |
25 | d21f3c77 | pkanavos | public IStatusNotification StatusNotification { get; set; } |
26 | 88e2300a | George Pantazis | public IStatusNotification ProgressBarNotification { get; set; } |
27 | d21f3c77 | pkanavos | |
28 | d21f3c77 | pkanavos | /* |
29 | d21f3c77 | pkanavos | private CancellationTokenSource _cts=new CancellationTokenSource(); |
30 | d21f3c77 | pkanavos | |
31 | d21f3c77 | pkanavos | public void SignalStop() |
32 | d21f3c77 | pkanavos | { |
33 | d21f3c77 | pkanavos | _cts.Cancel(); |
34 | d21f3c77 | pkanavos | } |
35 | d21f3c77 | pkanavos | */ |
36 | d21f3c77 | pkanavos | |
37 | d21f3c77 | pkanavos | |
38 | d21f3c77 | pkanavos | //Download a file. |
39 | b1303755 | pkanavos | public async Task<TreeHash> DownloadCloudFile(AccountInfo accountInfo, ObjectInfo cloudFile, string filePath,CancellationToken cancellationToken) |
40 | d21f3c77 | pkanavos | { |
41 | d21f3c77 | pkanavos | if (accountInfo == null) |
42 | d21f3c77 | pkanavos | throw new ArgumentNullException("accountInfo"); |
43 | d21f3c77 | pkanavos | if (cloudFile == null) |
44 | d21f3c77 | pkanavos | throw new ArgumentNullException("cloudFile"); |
45 | d21f3c77 | pkanavos | if (String.IsNullOrWhiteSpace(cloudFile.Account)) |
46 | d21f3c77 | pkanavos | throw new ArgumentNullException("cloudFile"); |
47 | d7288179 | pkanavos | if (cloudFile.Container==null) |
48 | d7288179 | pkanavos | throw new ArgumentNullException("cloudFile"); |
49 | d7288179 | pkanavos | if (cloudFile.Container.IsAbsoluteUri) |
50 | d21f3c77 | pkanavos | throw new ArgumentNullException("cloudFile"); |
51 | d21f3c77 | pkanavos | if (String.IsNullOrWhiteSpace(filePath)) |
52 | d21f3c77 | pkanavos | throw new ArgumentNullException("filePath"); |
53 | d21f3c77 | pkanavos | if (!Path.IsPathRooted(filePath)) |
54 | d21f3c77 | pkanavos | throw new ArgumentException("The filePath must be rooted", "filePath"); |
55 | d21f3c77 | pkanavos | Contract.EndContractBlock(); |
56 | d21f3c77 | pkanavos | using (ThreadContext.Stacks["Operation"].Push("DownloadCloudFile")) |
57 | d21f3c77 | pkanavos | { |
58 | 1865ad90 | Panagiotis Kanavos | // var cancellationToken=_cts.Token;// .ThrowIfCancellationRequested(); |
59 | 1865ad90 | Panagiotis Kanavos | |
60 | b1303755 | pkanavos | //The file's treehash after download completes. For directories, the treehash is always the empty hash |
61 | b1303755 | pkanavos | var finalHash = TreeHash.Empty; |
62 | b1303755 | pkanavos | |
63 | 42af59dc | pkanavos | if (await WaitOrAbort(accountInfo,cloudFile, cancellationToken).ConfigureAwait(false)) |
64 | b1303755 | pkanavos | return finalHash; |
65 | a60e5887 | pkanavos | |
66 | a60e5887 | pkanavos | var fileName = Path.GetFileName(filePath); |
67 | a60e5887 | pkanavos | |
68 | b1303755 | pkanavos | var info = FileInfoExtensions.FromPath(filePath).WithProperCapitalization(); |
69 | a60e5887 | pkanavos | |
70 | 225694f9 | pkanavos | TreeHash localTreeHash; |
71 | b1303755 | pkanavos | |
72 | cfb09103 | pkanavos | using (StatusNotification.GetNotifier("Hashing for Download {0}", "Hashed for Download {0}", (info.Exists && info is FileInfo),fileName)) |
73 | 225694f9 | pkanavos | { |
74 | b1303755 | pkanavos | var state = StatusKeeper.GetStateByFilePath(filePath); |
75 | dc18b138 | pkanavos | var progress = new Progress<HashProgress>(d => |
76 | dc18b138 | pkanavos | StatusNotification.Notify(new StatusNotification(String.Format("Hashing for Download {0:p} of {1}", d.Percentage, fileName)))); |
77 | d21f3c77 | pkanavos | |
78 | b1303755 | pkanavos | localTreeHash = StatusAgent.CalculateTreeHash(info, accountInfo, state, Settings.HashingParallelism, cancellationToken, progress); |
79 | 225694f9 | pkanavos | } |
80 | b1303755 | pkanavos | |
81 | b1303755 | pkanavos | var localPath = info.FullName; |
82 | d7288179 | pkanavos | var relativeUrl = cloudFile.Name; |
83 | d21f3c77 | pkanavos | |
84 | d21f3c77 | pkanavos | var url = relativeUrl.ToString(); |
85 | d7288179 | pkanavos | if (url.EndsWith(".ignore", StringComparison.InvariantCultureIgnoreCase)) |
86 | b1303755 | pkanavos | return finalHash; |
87 | d21f3c77 | pkanavos | |
88 | 5e646964 | Panagiotis Kanavos | if (!Selectives.IsSelected(accountInfo,cloudFile)) |
89 | b1303755 | pkanavos | return finalHash; |
90 | d21f3c77 | pkanavos | |
91 | d21f3c77 | pkanavos | |
92 | d21f3c77 | pkanavos | //Are we already downloading or uploading the file? |
93 | d21f3c77 | pkanavos | using (var gate = NetworkGate.Acquire(localPath, NetworkOperation.Downloading)) |
94 | d21f3c77 | pkanavos | { |
95 | d21f3c77 | pkanavos | if (gate.Failed) |
96 | b1303755 | pkanavos | return finalHash; |
97 | d21f3c77 | pkanavos | |
98 | d21f3c77 | pkanavos | var client = new CloudFilesClient(accountInfo); |
99 | d21f3c77 | pkanavos | var account = cloudFile.Account; |
100 | d21f3c77 | pkanavos | var container = cloudFile.Container; |
101 | d21f3c77 | pkanavos | |
102 | d21f3c77 | pkanavos | if (cloudFile.IsDirectory) |
103 | d21f3c77 | pkanavos | { |
104 | d21f3c77 | pkanavos | if (!Directory.Exists(localPath)) |
105 | d21f3c77 | pkanavos | try |
106 | d21f3c77 | pkanavos | { |
107 | d21f3c77 | pkanavos | Directory.CreateDirectory(localPath); |
108 | d21f3c77 | pkanavos | if (Log.IsDebugEnabled) |
109 | d21f3c77 | pkanavos | Log.DebugFormat("Created Directory [{0}]", localPath); |
110 | d21f3c77 | pkanavos | } |
111 | d21f3c77 | pkanavos | catch (IOException) |
112 | d21f3c77 | pkanavos | { |
113 | d21f3c77 | pkanavos | var localInfo = new FileInfo(localPath); |
114 | d21f3c77 | pkanavos | if (localInfo.Exists && localInfo.Length == 0) |
115 | d21f3c77 | pkanavos | { |
116 | d21f3c77 | pkanavos | Log.WarnFormat("Malformed directory object detected for [{0}]", localPath); |
117 | d21f3c77 | pkanavos | localInfo.Delete(); |
118 | d21f3c77 | pkanavos | Directory.CreateDirectory(localPath); |
119 | d21f3c77 | pkanavos | if (Log.IsDebugEnabled) |
120 | d21f3c77 | pkanavos | Log.DebugFormat("Created Directory [{0}]", localPath); |
121 | d21f3c77 | pkanavos | } |
122 | d21f3c77 | pkanavos | } |
123 | d21f3c77 | pkanavos | } |
124 | d21f3c77 | pkanavos | else |
125 | d21f3c77 | pkanavos | { |
126 | b1303755 | pkanavos | var isChanged = IsObjectChanged(cloudFile, localPath,localTreeHash); |
127 | d21f3c77 | pkanavos | if (isChanged) |
128 | d21f3c77 | pkanavos | { |
129 | d21f3c77 | pkanavos | //Retrieve the hashmap from the server |
130 | d7288179 | pkanavos | var serverHash = await client.GetHashMap(account, container, relativeUrl).ConfigureAwait(false); |
131 | d21f3c77 | pkanavos | //If it's a small file |
132 | 96e5791c | pkanavos | /* if (serverHash.Hashes.Count == 1) |
133 | d21f3c77 | pkanavos | //Download it in one go |
134 | d21f3c77 | pkanavos | await |
135 | d21f3c77 | pkanavos | DownloadEntireFileAsync(accountInfo, client, cloudFile, relativeUrl, localPath,cancellationToken); |
136 | d21f3c77 | pkanavos | //Otherwise download it block by block |
137 | 96e5791c | pkanavos | else*/ |
138 | 96e5791c | pkanavos | await DownloadWithBlocks(accountInfo, client, cloudFile, relativeUrl, localPath,localTreeHash, |
139 | d21f3c77 | pkanavos | serverHash,cancellationToken); |
140 | d21f3c77 | pkanavos | |
141 | d21f3c77 | pkanavos | if (!cloudFile.IsWritable(accountInfo.UserName)) |
142 | d21f3c77 | pkanavos | { |
143 | d21f3c77 | pkanavos | var attributes = File.GetAttributes(localPath); |
144 | d21f3c77 | pkanavos | File.SetAttributes(localPath, attributes | FileAttributes.ReadOnly); |
145 | d21f3c77 | pkanavos | } |
146 | b1303755 | pkanavos | |
147 | b1303755 | pkanavos | //Once download completes, the final hash will be equal to the server hash |
148 | b1303755 | pkanavos | finalHash = serverHash; |
149 | b1303755 | pkanavos | |
150 | d21f3c77 | pkanavos | } |
151 | d21f3c77 | pkanavos | } |
152 | d21f3c77 | pkanavos | |
153 | d21f3c77 | pkanavos | //Now we can store the object's metadata without worrying about ghost status entries |
154 | b1303755 | pkanavos | StatusKeeper.StoreInfo(localPath, cloudFile,finalHash); |
155 | d21f3c77 | pkanavos | |
156 | d21f3c77 | pkanavos | } |
157 | b1303755 | pkanavos | return finalHash; |
158 | d21f3c77 | pkanavos | } |
159 | d21f3c77 | pkanavos | |
160 | d21f3c77 | pkanavos | } |
161 | d21f3c77 | pkanavos | |
162 | d21f3c77 | pkanavos | //Download a file asynchronously using blocks |
163 | 732276d3 | pkanavos | public async Task DownloadWithBlocks(AccountInfo accountInfo, CloudFilesClient client, ObjectInfo cloudFile, Uri relativeUrl, string filePath, TreeHash localTreeHash,TreeHash serverHash, CancellationToken cancellationToken) |
164 | d21f3c77 | pkanavos | { |
165 | d21f3c77 | pkanavos | if (client == null) |
166 | d21f3c77 | pkanavos | throw new ArgumentNullException("client"); |
167 | d21f3c77 | pkanavos | if (cloudFile == null) |
168 | d21f3c77 | pkanavos | throw new ArgumentNullException("cloudFile"); |
169 | d21f3c77 | pkanavos | if (relativeUrl == null) |
170 | d21f3c77 | pkanavos | throw new ArgumentNullException("relativeUrl"); |
171 | d21f3c77 | pkanavos | if (String.IsNullOrWhiteSpace(filePath)) |
172 | d21f3c77 | pkanavos | throw new ArgumentNullException("filePath"); |
173 | d21f3c77 | pkanavos | if (!Path.IsPathRooted(filePath)) |
174 | d21f3c77 | pkanavos | throw new ArgumentException("The filePath must be rooted", "filePath"); |
175 | d21f3c77 | pkanavos | if (serverHash == null) |
176 | d21f3c77 | pkanavos | throw new ArgumentNullException("serverHash"); |
177 | d21f3c77 | pkanavos | if (cloudFile.IsDirectory) |
178 | d21f3c77 | pkanavos | throw new ArgumentException("cloudFile is a directory, not a file", "cloudFile"); |
179 | 1865ad90 | Panagiotis Kanavos | Contract.EndContractBlock(); |
180 | 1865ad90 | Panagiotis Kanavos | |
181 | 42af59dc | pkanavos | if (await WaitOrAbort(accountInfo, cloudFile, cancellationToken).ConfigureAwait(false)) |
182 | d21f3c77 | pkanavos | return; |
183 | d21f3c77 | pkanavos | |
184 | d21f3c77 | pkanavos | var fileAgent = GetFileAgent(accountInfo); |
185 | d21f3c77 | pkanavos | var localPath = FileInfoExtensions.GetProperFilePathCapitalization(filePath); |
186 | d21f3c77 | pkanavos | |
187 | c8045641 | pkanavos | //Avoid downloading empty files |
188 | c8045641 | pkanavos | if (serverHash.Bytes == 0) |
189 | c8045641 | pkanavos | { |
190 | c8045641 | pkanavos | using (var stream = File.Open(localPath, FileMode.OpenOrCreate, FileAccess.Write)) |
191 | c8045641 | pkanavos | { |
192 | c8045641 | pkanavos | stream.SetLength(0); |
193 | c8045641 | pkanavos | } |
194 | c8045641 | pkanavos | StatusNotification.NotifyChangedFile(localPath); |
195 | c8045641 | pkanavos | |
196 | c8045641 | pkanavos | Log.InfoFormat("[BLOCK GET] COMPLETE {0} for empty file", localPath); |
197 | c8045641 | pkanavos | return; |
198 | c8045641 | pkanavos | } |
199 | c8045641 | pkanavos | |
200 | d21f3c77 | pkanavos | //Calculate the relative file path for the new file |
201 | d21f3c77 | pkanavos | var relativePath = relativeUrl.RelativeUriToFilePath(); |
202 | d21f3c77 | pkanavos | var blockUpdater = new BlockUpdater(fileAgent.CachePath, localPath, relativePath, serverHash); |
203 | d21f3c77 | pkanavos | |
204 | d21f3c77 | pkanavos | StatusNotification.SetPithosStatus(PithosStatus.LocalSyncing, String.Format("Calculating hashmap for {0} before download", Path.GetFileName(localPath))); |
205 | d21f3c77 | pkanavos | //Calculate the file's treehash |
206 | d21f3c77 | pkanavos | |
207 | a60e5887 | pkanavos | var fileName = Path.GetFileName(localPath); |
208 | dc18b138 | pkanavos | var progress = new Progress<HashProgress>(d => |
209 | dc18b138 | pkanavos | StatusNotification.Notify(new StatusNotification(String.Format("Hashing for Download {0:p} of {1}", d.Percentage, fileName)))); |
210 | b1303755 | pkanavos | |
211 | 8d38a269 | pkanavos | var treeHash = localTreeHash ?? |
212 | 8d38a269 | pkanavos | await Signature.CalculateTreeHashAsync(localPath, (int)serverHash.BlockSize, serverHash.BlockHash, Settings.HashingParallelism,cancellationToken,progress).ConfigureAwait(false); |
213 | d21f3c77 | pkanavos | |
214 | d21f3c77 | pkanavos | //And compare it with the server's hash |
215 | d21f3c77 | pkanavos | var upHashes = serverHash.GetHashesAsStrings(); |
216 | d21f3c77 | pkanavos | var localHashes = treeHash.HashDictionary; |
217 | 2c0ad917 | pkanavos | ReportDownloadProgress(Path.GetFileName(localPath), 0,0, upHashes.Length, cloudFile.Bytes); |
218 | 2c0ad917 | pkanavos | |
219 | 2c0ad917 | pkanavos | |
220 | 69fdb645 | Panagiotis Kanavos | long i = 0; |
221 | 2c0ad917 | pkanavos | client.DownloadProgressChanged += (sender, args) => |
222 | 2c0ad917 | pkanavos | ReportDownloadProgress(Path.GetFileName(localPath), i, args.ProgressPercentage, upHashes.Length, cloudFile.Bytes); |
223 | 2c0ad917 | pkanavos | |
224 | 2c0ad917 | pkanavos | |
225 | 2c0ad917 | pkanavos | for (i = 0; i < upHashes.Length; i++) |
226 | 1865ad90 | Panagiotis Kanavos | { |
227 | 42af59dc | pkanavos | if (await WaitOrAbort(accountInfo, cloudFile, cancellationToken).ConfigureAwait(false)) |
228 | d21f3c77 | pkanavos | return; |
229 | d21f3c77 | pkanavos | |
230 | d21f3c77 | pkanavos | //For every non-matching hash |
231 | d21f3c77 | pkanavos | var upHash = upHashes[i]; |
232 | d21f3c77 | pkanavos | if (!localHashes.ContainsKey(upHash)) |
233 | d21f3c77 | pkanavos | { |
234 | d21f3c77 | pkanavos | StatusNotification.Notify(new CloudNotification { Data = cloudFile }); |
235 | 2c0ad917 | pkanavos | ReportDownloadProgress(Path.GetFileName(localPath), i, 0,upHashes.Length, cloudFile.Bytes); |
236 | d21f3c77 | pkanavos | |
237 | d21f3c77 | pkanavos | if (blockUpdater.UseOrphan(i, upHash)) |
238 | d21f3c77 | pkanavos | { |
239 | d21f3c77 | pkanavos | Log.InfoFormat("[BLOCK GET] ORPHAN FOUND for {0} of {1} for {2}", i, upHashes.Length, localPath); |
240 | d21f3c77 | pkanavos | continue; |
241 | d21f3c77 | pkanavos | } |
242 | d21f3c77 | pkanavos | Log.InfoFormat("[BLOCK GET] START {0} of {1} for {2}", i, upHashes.Length, localPath); |
243 | 69fdb645 | Panagiotis Kanavos | long start = i * serverHash.BlockSize; |
244 | d21f3c77 | pkanavos | //To download the last block just pass a null for the end of the range |
245 | d21f3c77 | pkanavos | long? end = null; |
246 | d21f3c77 | pkanavos | if (i < upHashes.Length - 1) |
247 | d21f3c77 | pkanavos | end = ((i + 1) * serverHash.BlockSize); |
248 | d21f3c77 | pkanavos | |
249 | b1303755 | pkanavos | |
250 | d21f3c77 | pkanavos | //Download the missing block |
251 | 46a6bfd9 | George Pantazis | byte[] block = await client.GetBlock(cloudFile.Account, cloudFile.Container, relativeUrl, start, end ?? cloudFile.Bytes -1, cancellationToken).ConfigureAwait(false); |
252 | d21f3c77 | pkanavos | |
253 | 96e5791c | pkanavos | //and store it asynchronously |
254 | 96e5791c | pkanavos | #pragma warning disable 4014 |
255 | d21f3c77 | pkanavos | blockUpdater.StoreBlock(i, block); |
256 | 96e5791c | pkanavos | #pragma warning restore 4014 |
257 | d21f3c77 | pkanavos | |
258 | d21f3c77 | pkanavos | Log.InfoFormat("[BLOCK GET] FINISH {0} of {1} for {2}", i, upHashes.Length, localPath); |
259 | d21f3c77 | pkanavos | } |
260 | 2c0ad917 | pkanavos | ReportDownloadProgress(Path.GetFileName(localPath), i, 100,upHashes.Length, cloudFile.Bytes); |
261 | d21f3c77 | pkanavos | } |
262 | d21f3c77 | pkanavos | |
263 | d21f3c77 | pkanavos | //Want to avoid notifications if no changes were made |
264 | d21f3c77 | pkanavos | var hasChanges = blockUpdater.HasBlocks; |
265 | d21f3c77 | pkanavos | blockUpdater.Commit(); |
266 | d21f3c77 | pkanavos | |
267 | d21f3c77 | pkanavos | if (hasChanges) |
268 | d21f3c77 | pkanavos | //Notify listeners that a local file has changed |
269 | d21f3c77 | pkanavos | StatusNotification.NotifyChangedFile(localPath); |
270 | d21f3c77 | pkanavos | |
271 | d21f3c77 | pkanavos | Log.InfoFormat("[BLOCK GET] COMPLETE {0}", localPath); |
272 | b1303755 | pkanavos | |
273 | d21f3c77 | pkanavos | } |
274 | d21f3c77 | pkanavos | |
275 | d21f3c77 | pkanavos | //Download a small file with a single GET operation |
276 | d21f3c77 | pkanavos | private async Task DownloadEntireFileAsync(AccountInfo accountInfo, CloudFilesClient client, ObjectInfo cloudFile, Uri relativeUrl, string filePath, CancellationToken cancellationToken) |
277 | d21f3c77 | pkanavos | { |
278 | d21f3c77 | pkanavos | if (client == null) |
279 | d21f3c77 | pkanavos | throw new ArgumentNullException("client"); |
280 | d21f3c77 | pkanavos | if (cloudFile == null) |
281 | d21f3c77 | pkanavos | throw new ArgumentNullException("cloudFile"); |
282 | d21f3c77 | pkanavos | if (relativeUrl == null) |
283 | d21f3c77 | pkanavos | throw new ArgumentNullException("relativeUrl"); |
284 | d21f3c77 | pkanavos | if (String.IsNullOrWhiteSpace(filePath)) |
285 | d21f3c77 | pkanavos | throw new ArgumentNullException("filePath"); |
286 | d21f3c77 | pkanavos | if (!Path.IsPathRooted(filePath)) |
287 | d21f3c77 | pkanavos | throw new ArgumentException("The localPath must be rooted", "filePath"); |
288 | d21f3c77 | pkanavos | if (cloudFile.IsDirectory) |
289 | d21f3c77 | pkanavos | throw new ArgumentException("cloudFile is a directory, not a file", "cloudFile"); |
290 | 1865ad90 | Panagiotis Kanavos | Contract.EndContractBlock(); |
291 | 1865ad90 | Panagiotis Kanavos | |
292 | 42af59dc | pkanavos | if (await WaitOrAbort(accountInfo, cloudFile, cancellationToken).ConfigureAwait(false)) |
293 | d21f3c77 | pkanavos | return; |
294 | d21f3c77 | pkanavos | |
295 | d21f3c77 | pkanavos | var localPath = FileInfoExtensions.GetProperFilePathCapitalization(filePath); |
296 | d21f3c77 | pkanavos | StatusNotification.SetPithosStatus(PithosStatus.LocalSyncing, String.Format("Downloading {0}", Path.GetFileName(localPath))); |
297 | d21f3c77 | pkanavos | StatusNotification.Notify(new CloudNotification { Data = cloudFile }); |
298 | 2c0ad917 | pkanavos | ReportDownloadProgress(Path.GetFileName(localPath), 1, 0,1, cloudFile.Bytes); |
299 | d21f3c77 | pkanavos | |
300 | d21f3c77 | pkanavos | var fileAgent = GetFileAgent(accountInfo); |
301 | d21f3c77 | pkanavos | //Calculate the relative file path for the new file |
302 | d21f3c77 | pkanavos | var relativePath = relativeUrl.RelativeUriToFilePath(); |
303 | d21f3c77 | pkanavos | //The file will be stored in a temporary location while downloading with an extension .download |
304 | d21f3c77 | pkanavos | var tempPath = Path.Combine(fileAgent.CachePath, relativePath + ".download"); |
305 | d21f3c77 | pkanavos | //Make sure the target folder exists. DownloadFileTask will not create the folder |
306 | d21f3c77 | pkanavos | var tempFolder = Path.GetDirectoryName(tempPath); |
307 | d21f3c77 | pkanavos | if (!Directory.Exists(tempFolder)) |
308 | d21f3c77 | pkanavos | Directory.CreateDirectory(tempFolder); |
309 | d21f3c77 | pkanavos | |
310 | d21f3c77 | pkanavos | //Download the object to the temporary location |
311 | d7288179 | pkanavos | await client.GetObject(cloudFile.Account, cloudFile.Container, relativeUrl, tempPath, cancellationToken).ConfigureAwait(false); |
312 | d21f3c77 | pkanavos | |
313 | d21f3c77 | pkanavos | //Create the local folder if it doesn't exist (necessary for shared objects) |
314 | d21f3c77 | pkanavos | var localFolder = Path.GetDirectoryName(localPath); |
315 | d21f3c77 | pkanavos | if (!Directory.Exists(localFolder)) |
316 | d21f3c77 | pkanavos | try |
317 | d21f3c77 | pkanavos | { |
318 | d21f3c77 | pkanavos | Directory.CreateDirectory(localFolder); |
319 | d21f3c77 | pkanavos | } |
320 | d21f3c77 | pkanavos | catch (IOException) |
321 | d21f3c77 | pkanavos | { |
322 | d21f3c77 | pkanavos | //A file may already exist that has the same name as the new folder. |
323 | d21f3c77 | pkanavos | //This may be an artifact of the way Pithos handles directories |
324 | d21f3c77 | pkanavos | var fileInfo = new FileInfo(localFolder); |
325 | d21f3c77 | pkanavos | if (fileInfo.Exists && fileInfo.Length == 0) |
326 | d21f3c77 | pkanavos | { |
327 | d21f3c77 | pkanavos | Log.WarnFormat("Malformed directory object detected for [{0}]", localFolder); |
328 | d21f3c77 | pkanavos | fileInfo.Delete(); |
329 | d21f3c77 | pkanavos | Directory.CreateDirectory(localFolder); |
330 | d21f3c77 | pkanavos | } |
331 | d21f3c77 | pkanavos | else |
332 | d21f3c77 | pkanavos | throw; |
333 | d21f3c77 | pkanavos | } |
334 | d21f3c77 | pkanavos | //And move it to its actual location once downloading is finished |
335 | d21f3c77 | pkanavos | if (File.Exists(localPath)) |
336 | d21f3c77 | pkanavos | File.Replace(tempPath, localPath, null, true); |
337 | d21f3c77 | pkanavos | else |
338 | d21f3c77 | pkanavos | File.Move(tempPath, localPath); |
339 | d21f3c77 | pkanavos | //Notify listeners that a local file has changed |
340 | d21f3c77 | pkanavos | StatusNotification.NotifyChangedFile(localPath); |
341 | d21f3c77 | pkanavos | |
342 | d21f3c77 | pkanavos | |
343 | d21f3c77 | pkanavos | } |
344 | d21f3c77 | pkanavos | |
345 | d21f3c77 | pkanavos | |
346 | 4cf8e4a5 | George Pantazis | private void ReportDownloadProgress(string fileName, long block, int blockPercentage,int totalBlocks, long fileSize) |
347 | d21f3c77 | pkanavos | { |
348 | d21f3c77 | pkanavos | StatusNotification.Notify(totalBlocks == 0 |
349 | 2c0ad917 | pkanavos | ? new ProgressNotification(fileName, "Downloading", 1, blockPercentage,1, fileSize) |
350 | 2c0ad917 | pkanavos | : new ProgressNotification(fileName, "Downloading", block, blockPercentage, totalBlocks, fileSize)); |
351 | d21f3c77 | pkanavos | } |
352 | d21f3c77 | pkanavos | |
353 | b1303755 | pkanavos | private bool IsObjectChanged(ObjectInfo cloudFile, string localPath,TreeHash localTreeHash) |
354 | d21f3c77 | pkanavos | { |
355 | d21f3c77 | pkanavos | //If the target is a directory, there are no changes to download |
356 | d21f3c77 | pkanavos | if (Directory.Exists(localPath)) |
357 | d21f3c77 | pkanavos | return false; |
358 | d21f3c77 | pkanavos | //If the file doesn't exist, we have a chagne |
359 | d21f3c77 | pkanavos | if (!File.Exists(localPath)) |
360 | d21f3c77 | pkanavos | return true; |
361 | d21f3c77 | pkanavos | //If there is no stored state, we have a change |
362 | d21f3c77 | pkanavos | var localState = StatusKeeper.GetStateByFilePath(localPath); |
363 | d21f3c77 | pkanavos | if (localState == null) |
364 | d21f3c77 | pkanavos | return true; |
365 | d21f3c77 | pkanavos | |
366 | b1303755 | pkanavos | var localHash= localTreeHash.TopHash.ToHashString(); |
367 | d21f3c77 | pkanavos | //If the file is different from the stored state, we have a change |
368 | b1303755 | pkanavos | if (localState.Checksum != localHash) |
369 | d21f3c77 | pkanavos | return true; |
370 | d21f3c77 | pkanavos | //If the top hashes differ, we have a change |
371 | 1cc1e8c5 | pkanavos | return (localState.Checksum != cloudFile.X_Object_Hash); |
372 | d21f3c77 | pkanavos | } |
373 | d21f3c77 | pkanavos | |
374 | d21f3c77 | pkanavos | private static FileAgent GetFileAgent(AccountInfo accountInfo) |
375 | d21f3c77 | pkanavos | { |
376 | d21f3c77 | pkanavos | return AgentLocator<FileAgent>.Get(accountInfo.AccountPath); |
377 | 1865ad90 | Panagiotis Kanavos | } |
378 | 1865ad90 | Panagiotis Kanavos | |
379 | 5e646964 | Panagiotis Kanavos | private async Task<bool> WaitOrAbort(AccountInfo account,ObjectInfo cloudFile, CancellationToken token) |
380 | 1865ad90 | Panagiotis Kanavos | { |
381 | 1865ad90 | Panagiotis Kanavos | token.ThrowIfCancellationRequested(); |
382 | 42af59dc | pkanavos | await UnpauseEvent.WaitAsync().ConfigureAwait(false); |
383 | 5e646964 | Panagiotis Kanavos | var shouldAbort = !Selectives.IsSelected(account,cloudFile); |
384 | 1865ad90 | Panagiotis Kanavos | if (shouldAbort) |
385 | 1865ad90 | Panagiotis Kanavos | Log.InfoFormat("Aborting [{0}]", cloudFile.Uri); |
386 | 1865ad90 | Panagiotis Kanavos | return shouldAbort; |
387 | d21f3c77 | pkanavos | } |
388 | d21f3c77 | pkanavos | |
389 | d21f3c77 | pkanavos | [Import] |
390 | d21f3c77 | pkanavos | public Selectives Selectives { get; set; } |
391 | d21f3c77 | pkanavos | |
392 | d21f3c77 | pkanavos | public AsyncManualResetEvent UnpauseEvent { get; set; } |
393 | d21f3c77 | pkanavos | } |
394 | d21f3c77 | pkanavos | } |