命名って難しい

変数、関数、クラスなどなど実装より命名に毎回悩むタイプの人間による技術についてのメモ。

C# - Chromeでダウンロードしているファイルの状況を確認するクラス

Seleniumからファイルのダウンロードリンクをクリックさせてダウンロードしたファイルをどうにかするプログラムの副産物

概要

Chromeはダウンロード中のファイルを以下の名前でダウンロードしていきます。

<元のファイル名>.crdownload

よって、そのcrdownloadファイルをうまいこと監視すればチェックしたいファイルのダウンロード完了を検出できるのです!
とか思ってたんですけど FileSystemWatcher って言う便利なクラスがあったんですね・・・。

もったいないので一応メモ。

ソースコード

    /// <summary>
    /// Chromeのダウンロードの状況クラス
    /// </summary>
    public class ChromeDownloadStatus
    {
        /// <summary>ダウンロードフォルダ</summary>
        public static readonly DirectoryInfo DownloadDirectory;

        /// <summary>ダウンロードフォルダの名前</summary>
        private static readonly string DownloadDirectoryName = "Downloads";
        /// <summary>キャッシュファイルの拡張子</summary>
        private static readonly string DownloadCacheExtension = ".crdownload";

        /// <summary>
        /// ダウンロード状態。
        /// </summary>
        public enum DownloadStatus
        {
            /// <summary>何もしていない</summary>
            None,
            /// <summary>ダウンロード中</summary>
            Downloading,
            /// <summary>ダウンロード完了</summary>
            Downloaded
        }

        /// <summary>
        /// 初期化。
        /// </summary>
        static ChromeDownloadStatus()
        {
            DownloadDirectory = new DirectoryInfo(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), DownloadDirectoryName));
        }

        /// <summary>
        /// 対象のファイルのダウンロード状況を取得する。
        /// </summary>
        /// <param name="fileName">対象のファイル名</param>
        /// <returns></returns>
        public DownloadStatus GetDownloadStatus(string fileName)
        {
            var cacheFile = new FileInfo(Path.Combine(DownloadDirectory.FullName, fileName + DownloadCacheExtension));
            var file = new FileInfo(Path.Combine(DownloadDirectory.FullName, fileName));

            // キャッシュがある場合はダウンロード中
            if (cacheFile.Exists)
            {
                return DownloadStatus.Downloading;
            }
            // キャッシュがなくて、ファイルが存在する場合は完了
            if (!cacheFile.Exists && file.Exists)
            {
                return DownloadStatus.Downloaded;
            }
            return DownloadStatus.None;
        }

        /// <summary>
        /// ダウンロード完了まで待つ。
        /// タイムアウトの場合はNULLを返す。
        /// </summary>
        /// <param name="fileName">ファイル名(ダウンロードフォルダに存在するもの。</param>
        /// <param name="loopWaitSpan">チェックごとのWait</param>
        /// <param name="timeout">タイムアウト時間。</param>
        /// <returns>ダウンロード完了したファイル情報</returns>
        public FileInfo WaitUntilDownloaded(string fileName, TimeSpan loopWaitSpan, TimeSpan timeout)
        {
            var start = DateTime.Now;

            // 指定のファイル名のダウンロード状況を取得する
            Func<DownloadStatus> getStatus = () => GetDownloadStatus(fileName);
            // タイムアウトかどうか取得する。
            Func<bool> isTimeout = () => timeout < (DateTime.Now - start);

            var dlStatus = getStatus();

            // 既にDL終了であればそのままファイルを返す
            if (dlStatus == DownloadStatus.Downloaded)
            {
                return DownloadDirectory.GetFiles(fileName).First();
            }

            // ダウンロード開始直前の場合開始していない扱いになるため開始になるまで待つ
            if (dlStatus == DownloadStatus.None)
            {
                while (getStatus() == DownloadStatus.None)
                {
                    Thread.Sleep(loopWaitSpan);
                    if (isTimeout())
                    {
                        return null;
                    }
                }
            }

            // ダウンロード完了状態になるまで待つ
            while (getStatus() != DownloadStatus.Downloaded)
            {
                Thread.Sleep(loopWaitSpan);
                if (isTimeout())
                {
                    return null;
                }
            }

            // ここまで来たらDL完了のため、ファイルを取得する
            return DownloadDirectory.GetFiles(fileName).FirstOrDefault();
        }

    }