2014年5月18日日曜日

空き容量が無いドライブへの書き込みについて

C# で、空きディスク容量の少ないドライブに、データを書き込んだ場合にどのようになるかを調べてみました。

ここ最近の HDD や USB メモリなどは非常に大きな容量なので、早々ディスク容量には困らないとは思いますが、古いシステムや数年前の USB メモリを使った場合などには、起こる問題だと思います。

まず、確認する方法について考えてみましたが、ディスクにクォーターなどを設けるなど方法が有るのですが、今回手元に 256M の USB メモリがありましたので(10年前くらいの物ですね)、実際に空き容量を無くした状態で、データを書き込み様子を見る事にしました。

まずは、空き容量を無くす方法ですが、Windows の fsutil.exe コマンドを使用してディスク容量を圧迫します(管理者モードのコマンドプロンプトを使用しないと使えない)。

巨大なサイズのファイルを簡単に作る方法
http://www.atmarkit.co.jp/fwin2k/win2ktips/243largefile/largefile.html

有る程度書き込みが出来る分の容量を残したデータを作りプログラムを作成します。

        static void Main(string[] args) {
            while(true) {
                StreamWriter sw = null;
                try {
                    sw = new StreamWriter(@"E:\testdata.txt", true);
                    // 適当にデータを書き込む(例外が出る)
                    sw.WriteLine("123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
                } catch(Exception) {
                    // System.IO.Exception が出る
                } finally {
                    if(sw != null) {
                        sw.Close();  // ここで例外が出る
                    }
                }
                break;
            }
            Console.WriteLine("End...");    // 例外が出るとここには到達しない
        }

そして、実行をすると、、、System.IO.Exception が発生します。


今回 StreamWriter を使用してデータを書き込むようにしていたのですが、この例外は容量を超えて書き続けた時と、容量を超えた状態で Close() をしたときに発生します。

また、問題なのは Close() の際に発生する部分でこの部分で例外が発生するとアプリごと終了してしまいます。

この問題については、以下のページで同様に話題となっています。

クローズ処理で例外は発生するのか?
http://d.hatena.ne.jp/rabbit2go/20101027/1288185258

VB.NETで空き容量がない場合にFileCloseできない。
http://dobon.net/vb/bbs/log3-23/13815.html

例外が発生する理由としては、Close() の際に Flush を行い、つまりバッファに貯まったデータを全て書き出そうとするための様です。

対策としては、上記の URL にありましたが、必要な容量分のダミーデータを作成し、データを書き込む際に、ダミーデータとの容量を確認しながら書き込み判断を行えと言った感じでしょうか?つまり手動で容量管理をすれば良いってことかな?

ロジックを組んでおけば、さして面倒な内容では無いのですが、何故か今ひとつ腑に落ちない。
とは言え、アプリが落ちると困るので、容量を見ながらの書き込みは必要という事になります。

毎回ファイルポインタの操作をする様な関数の場合は、その都度書き込み先の容量をチェックして書き込むのが良いかと思います。
この場合、書き込むデータのサイズにはよりますが、有る程度多めのバッファで見積れば良いかと思っています。

ディスクの空き容量については .Net Framework 2.0 以降であれば、System.IO.DriveInfo を使えば容易に判定ができるので、データ書き込み前に書き込みデータと空き容量を判定し、ダメな場合は書き込みしないような処理を実装すれば良いかと思います(ここらをどうするかは仕様によりますが)。

ドライブの領域サイズと空き容量を調べるには?[2.0のみ、C#、VB]
http://www.atmarkit.co.jp/fdotnet/dotnettips/468availablefreespace/availablefreespace.html

ドライブの全体の容量や空き容量を取得する
http://dobon.net/vb/dotnet/file/freespace.html

ファイルの操作については比較的簡易に書かれていることや、最初にも書いたとおり最近の大容量かなどで軽視しがちですが、この部分で足下をすくわれる事が有ったので、戒めに書いた次第です。

0 件のコメント:

コメントを投稿