OLD | NEW |
1 /* | 1 /* |
2 Copyright 2013 Google Inc | 2 Copyright 2013 Google Inc |
3 | 3 |
4 Licensed under the Apache License, Version 2.0 (the "License"); | 4 Licensed under the Apache License, Version 2.0 (the "License"); |
5 you may not use this file except in compliance with the License. | 5 you may not use this file except in compliance with the License. |
6 You may obtain a copy of the License at | 6 You may obtain a copy of the License at |
7 | 7 |
8 http://www.apache.org/licenses/LICENSE-2.0 | 8 http://www.apache.org/licenses/LICENSE-2.0 |
9 | 9 |
10 Unless required by applicable law or agreed to in writing, software | 10 Unless required by applicable law or agreed to in writing, software |
11 distributed under the License is distributed on an "AS IS" BASIS, | 11 distributed under the License is distributed on an "AS IS" BASIS, |
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 See the License for the specific language governing permissions and | 13 See the License for the specific language governing permissions and |
14 limitations under the License. | 14 limitations under the License. |
15 */ | 15 */ |
16 | 16 |
17 using System; | 17 using System; |
| 18 using System.Linq; |
18 using System.IO; | 19 using System.IO; |
19 using System.Net.Http.Headers; | 20 using System.Net.Http.Headers; |
20 using System.Threading; | 21 using System.Threading; |
21 using System.Threading.Tasks; | 22 using System.Threading.Tasks; |
22 | 23 |
23 using Google.Apis.Requests; | 24 using Google.Apis.Requests; |
24 using Google.Apis.Services; | 25 using Google.Apis.Services; |
25 using Google.Apis.Util; | 26 using Google.Apis.Util; |
26 | 27 |
27 namespace Google.Apis.Download | 28 namespace Google.Apis.Download |
(...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
182 CancellationToken cancellationToken) | 183 CancellationToken cancellationToken) |
183 { | 184 { |
184 // validate the parameters | 185 // validate the parameters |
185 url.ThrowIfNull("url"); | 186 url.ThrowIfNull("url"); |
186 stream.ThrowIfNull("stream"); | 187 stream.ThrowIfNull("stream"); |
187 if (!stream.CanWrite) | 188 if (!stream.CanWrite) |
188 { | 189 { |
189 throw new ArgumentException("stream doesn't support write operat
ions"); | 190 throw new ArgumentException("stream doesn't support write operat
ions"); |
190 } | 191 } |
191 | 192 |
192 var builder = new RequestBuilder() { BaseUri = new Uri(url) }; | 193 RequestBuilder builder = null; |
| 194 |
| 195 var uri = new Uri(url); |
| 196 if (string.IsNullOrEmpty(uri.Query)) |
| 197 { |
| 198 builder = new RequestBuilder() { BaseUri = new Uri(url) }; |
| 199 } |
| 200 else |
| 201 { |
| 202 builder = new RequestBuilder() { BaseUri = new Uri(url.Substring
(0, url.IndexOf("?"))) }; |
| 203 // remove '?' at the beginning |
| 204 var query = uri.Query.Substring(1); |
| 205 var pairs = from parameter in query.Split('&') |
| 206 select parameter.Split('='); |
| 207 // add each query parameter. each pair contains the key [0] and
then its value [1] |
| 208 foreach (var p in pairs) |
| 209 { |
| 210 builder.AddParameter(RequestParameterType.Query, p[0], p[1])
; |
| 211 } |
| 212 } |
193 builder.AddParameter(RequestParameterType.Query, "alt", "media"); | 213 builder.AddParameter(RequestParameterType.Query, "alt", "media"); |
194 | 214 |
195 long currentRequestFirstBytePos = 0; | 215 long currentRequestFirstBytePos = 0; |
196 | 216 |
197 try | 217 try |
198 { | 218 { |
199 // this "infinite" loop stops when the "to" byte position in the
"Content-Range" header is the last | 219 // this "infinite" loop stops when the "to" byte position in the
"Content-Range" header is the last |
200 // byte of the media ("length"-1 in the "Content-Range" header). | 220 // byte of the media ("length"-1 in the "Content-Range" header). |
201 // e.g. "Content-Range: 200-299/300" - "to"(299) = "length"(300)
- 1. | 221 // e.g. "Content-Range: 200-299/300" - "to"(299) = "length"(300)
- 1. |
202 while (true) | 222 while (true) |
203 { | 223 { |
204 cancellationToken.ThrowIfCancellationRequested(); | 224 cancellationToken.ThrowIfCancellationRequested(); |
205 | 225 |
206 var currentRequestLastBytePos = currentRequestFirstBytePos
ChunkSize - 1; | 226 var currentRequestLastBytePos = currentRequestFirstBytePos
ChunkSize - 1; |
207 | 227 |
208 // create the request and set the Range header | 228 // create the request and set the Range header |
209 var request = builder.CreateRequest(); | 229 var request = builder.CreateRequest(); |
210 request.Headers.Range = new RangeHeaderValue(currentRequestF
irstBytePos, | 230 request.Headers.Range = new RangeHeaderValue(currentRequestF
irstBytePos, |
211 currentRequestLastBytePos); | 231 currentRequestLastBytePos); |
212 | 232 |
213 using (var response = await service.HttpClient.SendAsync(req
uest, cancellationToken). | 233 using (var response = await service.HttpClient.SendAsync(req
uest, cancellationToken). |
214 ConfigureAwait(false)) | 234 ConfigureAwait(false)) |
215 { | 235 { |
216 // read the content and copy to the parameter's stream | 236 // read the content and copy to the parameter's stream |
217 var responseStream = await response.Content.ReadAsStream
Async().ConfigureAwait(false); | 237 var responseStream = await response.Content.ReadAsStream
Async().ConfigureAwait(false); |
218 responseStream.CopyTo(stream); | 238 responseStream.CopyTo(stream); |
219 | 239 |
220 // read the headers and check if all the media content w
as already downloaded | 240 // read the headers and check if all the media content w
as already downloaded |
221 var contentRange = response.Content.Headers.ContentRange
; | 241 var contentRange = response.Content.Headers.ContentRange
; |
222 currentRequestFirstBytePos = contentRange.To.Value 1; | 242 long mediaContentLength; |
223 long mediaContentLength = contentRange.Length.Value; | 243 |
| 244 if (contentRange == null) |
| 245 { |
| 246 // content range is null when the server doesn't adh
ere the media download protocol, in· |
| 247 // that case we got all the media in one chunk |
| 248 currentRequestFirstBytePos = mediaContentLength = |
| 249 response.Content.Headers.ContentLength.Value; |
| 250 } |
| 251 else |
| 252 { |
| 253 currentRequestFirstBytePos = contentRange.To.Value
1; |
| 254 mediaContentLength = contentRange.Length.Value; |
| 255 } |
224 | 256 |
225 if (currentRequestFirstBytePos == mediaContentLength) | 257 if (currentRequestFirstBytePos == mediaContentLength) |
226 { | 258 { |
227 var progress = new DownloadProgress(DownloadStatus.C
ompleted, mediaContentLength); | 259 var progress = new DownloadProgress(DownloadStatus.C
ompleted, mediaContentLength); |
228 UpdateProgress(progress); | 260 UpdateProgress(progress); |
229 return progress; | 261 return progress; |
230 } | 262 } |
231 } | 263 } |
232 | 264 |
233 UpdateProgress(new DownloadProgress(DownloadStatus.Downloadi
ng, currentRequestFirstBytePos)); | 265 UpdateProgress(new DownloadProgress(DownloadStatus.Downloadi
ng, currentRequestFirstBytePos)); |
234 } | 266 } |
235 } | 267 } |
236 catch (Exception ex) | 268 catch (Exception ex) |
237 { | 269 { |
238 var progress = new DownloadProgress(ex, currentRequestFirstByteP
os); | 270 var progress = new DownloadProgress(ex, currentRequestFirstByteP
os); |
239 UpdateProgress(progress); | 271 UpdateProgress(progress); |
240 return progress; | 272 return progress; |
241 } | 273 } |
242 } | 274 } |
243 } | 275 } |
244 } | 276 } |
OLD | NEW |