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