中堅プログラマーの備忘録

忘れっぽくなってきたので備忘録として・・・

c#.netでファイルをダウンロードするにはWebClientクラスを使うのがいいのか?

開発環境;visual studio 2015
開発言語:c#.net

1.概要

WEB上にあるファイルをフォームアプリケーションを使ってダウンロードしようと思ったのですが
方法がたくさんあって迷ってしまった。
とりあえずやりたいことは、
①バイナリ型式でダウンロードする
②非同期処理(デッドロックしないようにする)
③ダウンロード完了時にイベントを発生させる
④ダウンロードのタイムアウト時間を設ける
が今回の条件でした。

では、【WebClient】クラスを使ったと想定して1つづつ検証していきます。

2.検証内容

①バイナリ型式でダウンロードする。

 なぜ、こんなことが条件になっているのかというと
 ダウンロードしたデータの中身を解析する必要がありました。
 これについては【DownloadDataAsync】メソッドを使うことで実現できました。
 

②非同期処理(デッドロックしないようにする)

 同期処理にすると、ダウンロードが完了するまで
 フォームアプリケーションが操作不可能になってしまうので
 これを避けるために非同期処理が必要でした。
 これについては【DownloadDataAsync】メソッドを使うことで実現できました。
 

③ダウンロード完了時にイベントを発生させる

 複数のファイルを順番にダウンロードするのが目的でした。
 1つのファイルがダウンロード完了したら、次のファイルといった感じで
 リストの順番でしっかりダウンロードを行う。
 これについては【DownloadDataCompleted】イベントハンドラを作成することで実現できました。
 

④ダウンロードのタイムアウト時間を設ける

 これについては標準ではタイムアウトの設定が出来ないようです。
 悩んだ結果、独自で【Timer】クラスを実行し、
 【Timer_Tick】イベント内で【CancelAsync】を実行することで対応しました。
 
上記の結果から【WebClient】クラスで問題ないと判断し実装することに決めました。
以下ソースです。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;

namespace downloadFile
{
    public partial class Form1 : Form
    {
        //WebClientフィールド
        System.Net.WebClient downloadClient = null;
        //ダウンロードしたファイルの保存先
        string fileName = "C:\\work\\1.jpg";
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            
            //ダウンロード基のURL
            Uri u = new Uri("http://www.cor-art.com/best/tenkei/down/pSA001.jpg");
            //WebClientの作成
            if (downloadClient == null)
            {
                downloadClient = new System.Net.WebClient();
                //イベントハンドラの作成
                downloadClient.DownloadDataCompleted += new System.Net.DownloadDataCompletedEventHandler(downloadClient_DownloadDataCompleted);
            }
            // ダウンロードを開始する
            timer1.Start();
            downloadClient.DownloadDataAsync(u);
        }

        private void downloadClient_DownloadDataCompleted(object sender, System.Net.DownloadDataCompletedEventArgs e)
        {
            timer1.Stop();
            if (e.Cancelled)
            {
                Console.WriteLine("処理がタイムアウトしました。");
            }
            else if (e.Error != null)
            {
                Console.WriteLine("エラー:{0}", e.Error.Message);
            }
            else
            {
                Console.WriteLine("ダウンロードが完了しました。");
                byte[] sourceData = e.Result;
                // sourceDataの解析処理sample(ちゃんとjpgファイルがダウンロードされているか)
                if (sourceData[sourceData.Length - 2] == (Byte)0xff && sourceData[sourceData.Length - 1] == (Byte)0xd9)
                {
                    // FileStreamを作成
                    System.IO.FileStream fs = new System.IO.FileStream(fileName, System.IO.FileMode.Create, System.IO.FileAccess.Write);
                    // 書き込み
                    fs.Write(sourceData, 0, sourceData.Length);
                    // 閉じる
                    fs.Close();
                }else
                {
                    Console.WriteLine("jpgファイルではありません");
                }
            }
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            timer1.Stop();
            downloadClient.CancelAsync();
        }
    }
}

ソースの内容は
サンプルのjpgファイルをバイナリ型式でダウンロードして
本当にjpgファイルなのか解析し、問題なければ
【C:\work\1.jpg】
に保存しています。
【timer】で10秒のタイムアウトを設けています。