[WorldWind学习]19.WebDownload

  1 using System;

  2 using System.Diagnostics;

  3 using System.Globalization;

  4 using System.Net;

  5 using System.IO;

  6 using System.IO.Compression;

  7 using System.Threading;

  8 using System.Xml;

  9 using Utility;

 10 using WorldWind;

 11 

 12 namespace WorldWind.Net

 13 {

 14     public delegate void DownloadProgressHandler(int bytesRead, int totalBytes);

 15     public delegate void DownloadCompleteHandler(WebDownload wd);

 16 

 17     public enum DownloadType

 18     {

 19         Unspecified,

 20         Wms

 21     }

 22     /// <summary>

 23     /// 网络下载对象,负责下载数据

 24     /// </summary>

 25     public class WebDownload : IDisposable

 26     {

 27         #region Static proxy properties

 28 

 29         static public bool Log404Errors = false;

 30         static public bool useWindowsDefaultProxy = true;

 31         static public string proxyUrl = "";

 32         static public bool useDynamicProxy;

 33         static public string proxyUserName = "";

 34         static public string proxyPassword = "";

 35 

 36         #endregion

 37         public static string UserAgent = String.Format(

 38             CultureInfo.InvariantCulture,

 39             "World Wind v{0} ({1}, {2})",

 40             System.Windows.Forms.Application.ProductVersion,

 41             Environment.OSVersion.ToString(),

 42             CultureInfo.CurrentCulture.Name);

 43 

 44         //下载连接字符串

 45         public string Url;

 46 

 47         /// <summary>

 48         /// Memory downloads fills this stream

 49         /// </summary>

 50         public Stream ContentStream;

 51 

 52         public string SavedFilePath;

 53         public bool IsComplete;

 54 

 55         /// <summary>

 56         /// Called when data is being received.  

 57         /// Note that totalBytes will be zero if the server does not respond with content-length.

 58         /// 开始接收数据时回调

 59         /// 总的字节数为0,如果服务没有返回目录长度

 60         /// </summary>

 61         public DownloadProgressHandler ProgressCallback;

 62 

 63         /// <summary>

 64         /// Called to update debug window.

 65         /// 更新debug窗体,回调函数

 66         /// </summary>

 67         public static DownloadCompleteHandler DebugCallback;

 68 

 69         /// <summary>

 70         /// Called when a download has ended with success or failure

 71         /// 当下载成功或者失败时回调

 72         /// </summary>

 73         public static DownloadCompleteHandler DownloadEnded;

 74 

 75         /// <summary>

 76         /// Called when download is completed.  Call Verify from event handler to throw any exception.

 77         /// 下载完成时回调,调用验证事件是否抛出异常。

 78         /// </summary>

 79         public DownloadCompleteHandler CompleteCallback;

 80 

 81         public DownloadType DownloadType = DownloadType.Unspecified;

 82         public string ContentType;

 83         public int BytesProcessed;

 84         public int ContentLength;

 85 

 86         // variables to allow placefinder to use gzipped requests

 87         //  *default to uncompressed requests to avoid breaking other things

 88         public bool Compressed = false;

 89         public string ContentEncoding;

 90 

 91         /// <summary>

 92         /// The download start time (or MinValue if not yet started)

 93         /// </summary>

 94         public DateTime DownloadStartTime = DateTime.MinValue;

 95 

 96         internal HttpWebRequest request;

 97         internal HttpWebResponse response;

 98 

 99         protected Exception downloadException;

100 

101         protected bool isMemoryDownload;

102         /// <summary>

103         /// used to signal thread abortion; if true, the download thread was aborted

104         /// </summary>

105         private bool stopFlag = false;

106         //下载线程

107         protected Thread dlThread;

108 

109         /// <summary>

110         /// Initializes a new instance of the <see cref="WebDownload"/> class.

111         /// 构造函数,初始化下载对象

112         /// </summary>

113         /// <param name="url">The URL to download from.</param>

114         public WebDownload(string url)

115         {

116             this.Url = url;

117         }

118 

119         /// <summary>

120         /// Initializes a new instance of the <see cref="T:WorldWind.Net.WebDownload"/> class.

121         /// </summary>

122         public WebDownload()

123         {

124         }

125 

126         /// <summary>

127         /// Whether the download is currently being processed (active).

128         /// 当前下载是否仍在进行

129         /// </summary>

130         public bool IsDownloadInProgress

131         {

132             get

133             {

134                 return dlThread != null && dlThread.IsAlive;

135             }

136         }

137 

138         /// <summary>

139         /// Contains the exception that occurred during download, or null if successful.

140         /// </summary>

141         public Exception Exception

142         {

143             get

144             {

145                 return downloadException;

146             }

147         }

148 

149         /// <summary>

150         /// Asynchronous download of HTTP data to file. 

151         /// 异步下载Http数据,通过开启新线程实现。

152         /// </summary>

153         public void BackgroundDownloadFile()

154         {

155             if (CompleteCallback == null)

156                 throw new ArgumentException("No download complete callback specified.");

157             //实例化下载线程

158             dlThread = new Thread(new ThreadStart(Download));

159             dlThread.Name = "WebDownload.dlThread";

160             dlThread.IsBackground = true;

161             dlThread.CurrentUICulture = Thread.CurrentThread.CurrentUICulture;

162             //开启后台下载线程

163             dlThread.Start();

164         }

165 

166         /// <summary>

167         /// Asynchronous download of HTTP data to file.

168         /// 异步下载Http数据,绑定下载完成后的回调函数。

169         /// </summary>

170         public void BackgroundDownloadFile(DownloadCompleteHandler completeCallback)

171         {

172             CompleteCallback += completeCallback;

173             BackgroundDownloadFile();

174         }

175 

176         /// <summary>

177         /// Download image of specified type. (handles server errors for wms type)

178         /// </summary>

179         public void BackgroundDownloadFile(DownloadType dlType)

180         {

181             DownloadType = dlType;

182             BackgroundDownloadFile();

183         }

184 

185         /// <summary>

186         /// Asynchronous download of HTTP data to in-memory buffer.

187         /// 异步下载Http数据到内存缓冲区

188         /// </summary>

189         public void BackgroundDownloadMemory()

190         {

191             if (CompleteCallback == null)

192                 throw new ArgumentException("No download complete callback specified.");

193 

194             isMemoryDownload = true;

195             dlThread = new Thread(new ThreadStart(Download));

196             dlThread.Name = "WebDownload.dlThread(2)";

197             dlThread.IsBackground = true;

198             dlThread.CurrentUICulture = Thread.CurrentThread.CurrentUICulture;

199             dlThread.Start();

200         }

201 

202         /// <summary>

203         /// Asynchronous download of HTTP data to in-memory buffer. 

204         /// </summary>

205         public void BackgroundDownloadMemory(DownloadCompleteHandler completeCallback)

206         {

207             CompleteCallback += completeCallback;

208             BackgroundDownloadMemory();

209         }

210 

211         /// <summary>

212         /// Download image of specified type. (handles server errors for WMS type)

213         /// </summary>

214         /// <param name="dlType">Type of download.</param>

215         public void BackgroundDownloadMemory(DownloadType dlType)

216         {

217             DownloadType = dlType;

218             BackgroundDownloadMemory();

219         }

220 

221         /// <summary>

222         /// Synchronous download of HTTP data to in-memory buffer. 

223         /// </summary>

224         public void DownloadMemory()

225         {

226             isMemoryDownload = true;

227             Download();

228         }

229 

230         /// <summary>

231         /// Download image of specified type. (handles server errors for WMS type)

232         /// </summary>

233         public void DownloadMemory(DownloadType dlType)

234         {

235             DownloadType = dlType;

236             DownloadMemory();

237         }

238 

239         /// <summary>

240         /// HTTP downloads to memory.

241         /// </summary>

242         /// <param name="progressCallback">The progress callback.</param>

243         public void DownloadMemory(DownloadProgressHandler progressCallback)

244         {

245             ProgressCallback += progressCallback;

246             DownloadMemory();

247         }

248 

249         /// <summary>

250         /// Synchronous download of HTTP data to in-memory buffer. 

251         /// </summary>

252         public void DownloadFile(string destinationFile)

253         {

254             SavedFilePath = destinationFile;

255 

256             Download();

257         }

258 

259         /// <summary>

260         /// Download image of specified type to a file. (handles server errors for WMS type)

261         /// </summary>

262         public void DownloadFile(string destinationFile, DownloadType dlType)

263         {

264             DownloadType = dlType;

265             DownloadFile(destinationFile);

266         }

267 

268         /// <summary>

269         /// Saves a http in-memory download to file.

270         /// </summary>

271         /// <param name="destinationFilePath">File to save the downloaded data to.</param>

272         public void SaveMemoryDownloadToFile(string destinationFilePath)

273         {

274             if (ContentStream == null)

275                 throw new InvalidOperationException("No data available.");

276 

277             // Cache the capabilities on file system

278             ContentStream.Seek(0, SeekOrigin.Begin);

279             using (Stream fileStream = File.Create(destinationFilePath))

280             {

281                 if (ContentStream is MemoryStream)

282                 {

283                     // Write the MemoryStream buffer directly (2GB limit)

284                     MemoryStream ms = (MemoryStream)ContentStream;

285                     fileStream.Write(ms.GetBuffer(), 0, (int)ms.Length);

286                 }

287                 else

288                 {

289                     // Block copy

290                     byte[] buffer = new byte[4096];

291                     while (true)

292                     {

293                         int numRead = ContentStream.Read(buffer, 0, buffer.Length);

294                         if (numRead <= 0)

295                             break;

296                         fileStream.Write(buffer, 0, numRead);

297                     }

298                 }

299             }

300             ContentStream.Seek(0, SeekOrigin.Begin);

301         }

302 

303         /// <summary>

304         /// Aborts the current download. 

305         /// 终止当前下载

306         /// </summary>

307         public void Cancel()

308         {

309             CompleteCallback = null;

310             ProgressCallback = null;

311             if (dlThread != null && dlThread != Thread.CurrentThread)

312             {

313                 if (dlThread.IsAlive)

314                 {

315                     Log.Write(Log.Levels.Verbose, "WebDownload.Cancel() : stopping download thread...");

316                     stopFlag = true;

317                     if (!dlThread.Join(500))

318                     {

319                         Log.Write(Log.Levels.Warning, "WebDownload.Cancel() : download thread refuses to die, forcing Abort()");

320                         dlThread.Abort();

321                     }

322                 }

323                 dlThread = null;

324             }

325         }

326 

327         /// <summary>

328         /// Notify event subscribers of download progress.

329         /// </summary>

330         /// <param name="bytesRead">Number of bytes read.</param>

331         /// <param name="totalBytes">Total number of bytes for request or 0 if unknown.</param>

332         private void OnProgressCallback(int bytesRead, int totalBytes)

333         {

334             if (ProgressCallback != null)

335             {

336                 ProgressCallback(bytesRead, totalBytes);

337             }

338         }

339 

340         /// <summary>

341         /// Called with detailed information about the download.

342         /// </summary>

343         /// <param name="wd">The WebDownload.</param>

344         private static void OnDebugCallback(WebDownload wd)

345         {

346             if (DebugCallback != null)

347             {

348                 DebugCallback(wd);

349             }

350         }

351 

352         /// <summary>

353         /// Called when downloading has ended.

354         /// </summary>

355         /// <param name="wd">The download.</param>

356         private static void OnDownloadEnded(WebDownload wd)

357         {

358             if (DownloadEnded != null)

359             {

360                 DownloadEnded(wd);

361             }

362         }

363 

364         /// <summary>

365         /// Synchronous HTTP download

366         /// 线程异步调用的方法Download中,采用请求响应同步下载数据

367         /// </summary>

368         protected void Download()

369         {

370             Log.Write(Log.Levels.Debug, "Starting download thread...");

371 

372             Debug.Assert(Url.StartsWith("http://"));

373             DownloadStartTime = DateTime.Now;

374             try

375             {

376                 try

377                 {

378                     // If a registered progress-callback, inform it of our download progress so far.

379                     OnProgressCallback(0, 1);

380                     OnDebugCallback(this);

381 

382                     // check to see if thread was aborted (multiple such checks within the thread function)

383                     if (stopFlag)

384                     {

385                         IsComplete = true;

386                         return;

387                     }

388 

389 

390                     // create content stream from memory or file

391                     if (isMemoryDownload && ContentStream == null)

392                     {

393                         ContentStream = new MemoryStream();

394                     }

395                     else

396                     {

397                         // Download to file

398                         string targetDirectory = Path.GetDirectoryName(SavedFilePath);

399                         if (targetDirectory.Length > 0)

400                             Directory.CreateDirectory(targetDirectory);

401                         ContentStream = new FileStream(SavedFilePath, FileMode.Create);

402                     }

403 

404                     // Create the request object.

405                     request = (HttpWebRequest)WebRequest.Create(Url);

406                     request.UserAgent = UserAgent;

407 

408 

409                     if (this.Compressed)

410                     {

411                         request.Headers.Add("Accept-Encoding", "gzip,deflate");

412                     }

413                     if (stopFlag)

414                     {

415                         IsComplete = true;

416                         return;

417                     }

418                     request.Proxy = ProxyHelper.DetermineProxyForUrl(

419                         Url,

420                         useWindowsDefaultProxy,

421                         useDynamicProxy,

422                         proxyUrl,

423                         proxyUserName,

424                         proxyPassword);

425 

426                     // TODO: probably better done via BeginGetResponse() / EndGetResponse() because this may block for a while

427                     // causing warnings in thread abortion.

428                     using (response = request.GetResponse() as HttpWebResponse)

429                     {

430                         // only if server responds 200 OK

431                         if (response.StatusCode == HttpStatusCode.OK)

432                         {

433                             ContentType = response.ContentType;

434                             ContentEncoding = response.ContentEncoding;

435 

436                             // Find the data size from the headers.

437                             string strContentLength = response.Headers["Content-Length"];

438                             if (strContentLength != null)

439                             {

440                                 ContentLength = int.Parse(strContentLength, CultureInfo.InvariantCulture);

441                             }

442                             //缓存字节数组,大小1500byte

443                             byte[] readBuffer = new byte[1500];

444                             using (Stream responseStream = response.GetResponseStream())

445                             {

446                                 while (true)

447                                 {

448                                     if (stopFlag)

449                                     {

450                                         IsComplete = true;

451                                         return;

452                                     }

453 

454                                     //  Pass do.readBuffer to BeginRead.

455                                     int bytesRead = responseStream.Read(readBuffer, 0, readBuffer.Length);

456                                     if (bytesRead <= 0)

457                                         break;

458 

459                                     //TODO: uncompress responseStream if necessary so that ContentStream is always uncompressed

460                                     //  - at the moment, ContentStream is compressed if the requesting code sets

461                                     //    download.Compressed == true, so ContentStream must be decompressed 

462                                     //    by whatever code is requesting the gzipped download

463                                     //  - this hack only works for requests made using the methods that download to memory,

464                                     //    requests downloading to file will result in a gzipped file

465                                     //  - requests that do not explicity set download.Compressed = true should be unaffected

466 

467                                     ContentStream.Write(readBuffer, 0, bytesRead);

468 

469                                     BytesProcessed += bytesRead;

470 

471                                     // If a registered progress-callback, inform it of our download progress so far.

472                                     OnProgressCallback(BytesProcessed, ContentLength);

473                                     OnDebugCallback(this);

474                                 }

475                             }

476 

477                         }

478                     }

479 

480                     HandleErrors();

481                 }

482                 catch (ThreadAbortException)

483                 {

484                     // re-throw to avoid it being caught by the catch-all below

485                     Log.Write(Log.Levels.Verbose, "Re-throwing ThreadAbortException.");

486                     throw;

487                 }

488                 catch (System.Configuration.ConfigurationException)

489                 {

490                     // is thrown by WebRequest.Create if App.config is not in the correct format

491                     // TODO: don't know what to do with it

492                     throw;

493                 }

494                 catch (Exception caught)

495                 {

496                     try

497                     {

498                         // Remove broken file download

499                         if (ContentStream != null)

500                         {

501                             ContentStream.Close();

502                             ContentStream = null;

503                         }

504                         if (SavedFilePath != null && SavedFilePath.Length > 0)

505                         {

506                             File.Delete(SavedFilePath);

507                         }

508                     }

509                     catch (Exception)

510                     {

511                     }

512                     SaveException(caught);

513                 }

514 

515                 if (stopFlag)

516                 {

517                     IsComplete = true;

518                     return;

519                 }

520 

521                 if (ContentLength == 0)

522                 {

523                     ContentLength = BytesProcessed;

524                     // If a registered progress-callback, inform it of our completion

525                     OnProgressCallback(BytesProcessed, ContentLength);

526                 }

527 

528                 if (ContentStream is MemoryStream)

529                 {

530                     ContentStream.Seek(0, SeekOrigin.Begin);

531                 }

532                 else if (ContentStream != null)

533                 {

534                     ContentStream.Close();

535                     ContentStream = null;

536                 }

537 

538                 OnDebugCallback(this);

539 

540                 if (CompleteCallback == null)

541                 {

542                     Verify();

543                 }

544                 else

545                 {

546                     CompleteCallback(this);

547                 }

548             }

549             catch (ThreadAbortException)

550             {

551                 Log.Write(Log.Levels.Verbose, "Download aborted.");

552             }

553             finally

554             {

555                 IsComplete = true;

556             }

557 

558             OnDownloadEnded(this);

559         }

560 

561         /// <summary>

562         /// Handle server errors that don't get trapped by the web request itself.

563         /// </summary>

564         private void HandleErrors()

565         {

566             // HACK: Workaround for TerraServer failing to return 404 on not found

567             if (ContentStream.Length == 15)

568             {

569                 // a true 404 error is a System.Net.WebException, so use the same text here

570                 Exception ex = new FileNotFoundException("The remote server returned an error: (404) Not Found.", SavedFilePath);

571                 SaveException(ex);

572             }

573 

574             // TODO: WMS 1.1 content-type != xml

575             // TODO: Move WMS logic to WmsDownload

576             if (DownloadType == DownloadType.Wms && (

577                 ContentType.StartsWith("text/xml") ||

578                 ContentType.StartsWith("application/vnd.ogc.se")))

579             {

580                 // WMS request failure

581                 SetMapServerError();

582             }

583         }

584 

585         /// <summary>

586         /// If exceptions occurred they will be thrown by calling this function.

587         /// </summary>

588         public void Verify()

589         {

590             if (Exception != null)

591                 throw Exception;

592         }

593 

594         /// <summary>

595         /// Log download error to log file

596         /// </summary>

597         /// <param name="exception"></param>

598         private void SaveException(Exception exception)

599         {

600             // Save the exception 

601             downloadException = exception;

602 

603             if (Exception is ThreadAbortException)

604                 // Don't log canceled downloads

605                 return;

606 

607             if (Log404Errors)

608             {

609                 Log.Write(Log.Levels.Error, "HTTP", "Error: " + Url);

610                 Log.Write(Log.Levels.Error + 1, "HTTP", "     : " + exception.Message);

611             }

612         }

613 

614         /// <summary>

615         /// Reads the xml response from the server and throws an error with the message.

616         /// </summary>

617         private void SetMapServerError()

618         {

619             try

620             {

621                 XmlDocument errorDoc = new XmlDocument();

622                 ContentStream.Seek(0, SeekOrigin.Begin);

623                 errorDoc.Load(ContentStream);

624                 string msg = "";

625                 foreach (XmlNode node in errorDoc.GetElementsByTagName("ServiceException"))

626                     msg += node.InnerText.Trim() + Environment.NewLine;

627                 SaveException(new WebException(msg.Trim()));

628             }

629             catch (XmlException)

630             {

631                 SaveException(new WebException("An error occurred while trying to download " + request.RequestUri.ToString() + "."));

632             }

633         }

634 

635         #region IDisposable Members

636 

637         /// <summary>

638         /// Performs application-defined tasks associated with freeing, releasing, or

639         /// resetting unmanaged resources.

640         /// </summary>

641         public void Dispose()

642         {

643             if (dlThread != null && dlThread != Thread.CurrentThread)

644             {

645                 if (dlThread.IsAlive)

646                 {

647                     Log.Write(Log.Levels.Verbose, "WebDownload.Dispose() : stopping download thread...");

648                     stopFlag = true;

649                     if (!dlThread.Join(500))

650                     {

651                         Log.Write(Log.Levels.Warning, "WebDownload.Dispose() : download thread refuses to die, forcing Abort()");

652                         dlThread.Abort();

653                     }

654                 }

655                 dlThread = null;

656             }

657 

658             if (request != null)

659             {

660                 request.Abort();

661                 request = null;

662             }

663 

664             if (ContentStream != null)

665             {

666                 ContentStream.Close();

667                 ContentStream = null;

668             }

669 

670             if (DownloadStartTime != DateTime.MinValue)

671                 OnDebugCallback(this);

672 

673             GC.SuppressFinalize(this);

674         }

675         #endregion

676     }

677 }
View Code

该类基于Http协议,应用层。

Socket有两种:流式Socket(SOCK_STREAM)和数据报式Socket(SOCK_DGRAM)。传输层

  流式是一种面向连接的Socket,针对于面向连接的TCP服务应用;

  数据报式Socket是一种无连接的Socket,对应于无连接的UDP服务应用。

[WorldWind学习]19.WebDownload

[WorldWind学习]19.WebDownload[WorldWind学习]19.WebDownload

你可能感兴趣的:(download)