- PR -

[2005][C#]WebRequestで取得したStreamをそのままファイルに保存したい

1
投稿者投稿内容
momo
常連さん
会議室デビュー日: 2006/11/06
投稿数: 35
投稿日時: 2007-05-30 11:47
WebRequestでWebサーバからファイルをダウンロードしています。
WebClientはTimeoutが設定できないのでWebRequestのほうを使用しています。

現在WebResponseの取得はできていますが、
それをファイルに保存するところでとまっています。

WebResponse→Stream→バイト配列→FileStreamで保存

という流れを考えていたのですが、
Stream→バイト配列のところでうまくいきません。
StreamのReadByteメソッドをループでは時間がかかるとのことなので
Readメソッドでバイト配列に変換したいのですが、
StreamのLengthプロパティでシークできない旨のエラーがでます。

WebClientのDownloadDataメソッドではうまくいっているURLなのですが・・。
(テストとしてhttp://www.goo.ne.jp/index.htmlをダウンロードしていますが、
実際はexeやDLLをダウンロードします。)

Streamをバイト配列に変換するにはどうしたらよいでしょうか。
また、バイト配列に変換せずStreamから直接ファイルに保存とかはできるのでしょうか。

Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2007-05-30 12:08
全体の長さが取得できないストリームから読み出す場合、適当な大きさのバッファ(byte 配列)を用意し、0 以上を返す間 Read メソッドを繰り返し呼び出すのが一般的です。最適なバッファサイズはストリーム元など環境によって変わります。
読み出したバッファは、ファイルに保存するならそのまま FileStream に書き出せばいいでしょう。Read メソッドの返値が書き込むべきサイズも意味します。書き出したら次の Read へ。

引用:

バイト配列に変換せずStreamから直接ファイルに保存とかはできるのでしょうか。


標準クラスライブラリには存在していません。
なちゃ
ぬし
会議室デビュー日: 2003/06/11
投稿数: 872
投稿日時: 2007-05-30 12:48
引用:

Streamをバイト配列に変換するにはどうしたらよいでしょうか。
また、バイト配列に変換せずStreamから直接ファイルに保存とかはできるのでしょうか。


そういうStreamを一度作ってしまうと便利かな。
momo
常連さん
会議室デビュー日: 2006/11/06
投稿数: 35
投稿日時: 2007-05-30 14:08
Hongliangさん

引用:

全体の長さが取得できないストリームから読み出す場合、適当な大きさのバッファ(byte 配列)を用意し、0 以上を返す間 Read メソッドを繰り返し呼び出すのが一般的です。



アドバイスありがとうございます。
なるほど。1バイトずつではなくnバイトずつ読み込み速度アップということですね?
アドバイスを参考にして以下のようなコードを組んで見みした。

コード:
  WebRequest webReq = WebRequest.Create(strSendURLAll);
  webReq.Timeout = 30 * 1000;
  webReq.Method = HTTP_GET;

  WebResponse webRes = webReq.GetResponse();  
  Stream sr = webRes.GetResponseStream();
  int intBuffSize = 1000;
  int intFileSize = 0;
  FileStream srmSaveFile = new FileStream(pstrSaveFileDirName + pstrGetFileName,
                                          FileMode.Create);


  //nバイトずつ読み込む
  int intRet = 0;
  do
  {
      byte[] bytBuff = new byte[intBuffSize];
      //1000バイトずつ読み込む
      intRet = sr.Read(bytBuff, 0, intBuffSize);
      if (intRet == 0) break;
      //ファイルへ書き込み
      srmSaveFile.Write(bytBuff, 0, intRet);
      intFileSize += intRet;
  }
  while (intRet - intBuffSize >= 0);

  sr.Close();
  srmSaveFile.Close();



これでうまくファイルに保存されるようになりました。

が、本来Readできるはずのものが途中でぶちきれしまいます。

Streamの全てをReadしきれていないようです。
切れる位置は、現在2パターンあります。
バッファするサイズがよくないのでしょうか・・・?

(※HttpWebRequest、HttpWebResponseを利用したGET、
かつStreamReaderクラスを使用した場合は71KBほどになります。)

スレの趣旨からは外れますが、お分かりの方がいらっしゃれば
教えていただけないでしょうか。

なちゃさん
引用:

そういうStreamを一度作ってしまうと便利かな。



そうですね。今回は一箇所でしか使わないので
直に書いてしまう予定ですが。
今後そんなクラスを作っておくと便利ですよね。
検討してみます。
Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2007-05-30 14:32
Stream.Read の解説にはこう書かれています。
引用:

読み込み可能なデータがない場合は、少なくとも 1 バイトのデータが読み込み可能になるまでブロックします。Read が 0 を返すのは、ストリーム内にそれ以上データがない場合とそれ以上読み込み可能な対象 (閉じたソケットやファイル終端など) を予期していない場合だけです。メソッドの実装は、ストリームの末尾に到達していない場合でも、要求された数に満たないバイトを返すようにすることができます。



つまり、Stream.Read の返値が第三引数未満の値になったとしても、イコールストリームの末尾と考えることはできません。ストリームの末尾と判断できるのは 0 が返されたときだけです。

ちなみに私はよくこんな感じに書きます。

コード:


int read;
while ((read = stream.Read(buffer, 0, bufferSize)) > 0) {
// 処理
}




[追記]
あ、それからループ内で毎回バッファを new するのはあんまり好ましくないかと。
使いまわして問題ないものですし。

[ メッセージ編集済み 編集者: Hongliang 編集日時 2007-05-30 14:35 ]
momo
常連さん
会議室デビュー日: 2006/11/06
投稿数: 35
投稿日時: 2007-05-30 16:06
引用:

つまり、Stream.Read の返値が第三引数未満の値になったとしても、イコールストリームの末尾と考えることはできません。ストリームの末尾と判断できるのは 0 が返されたときだけです。



うーん、なるほど。MSDNの文言を正しく理解できていなかったようです。
Readの戻り値が0の時だけbreakすることで解決しました。

引用:

あ、それからループ内で毎回バッファを new するのはあんまり好ましくないかと。


そうですね。ここも修正しました。

ありがとうございました。
1

スキルアップ/キャリアアップ(JOB@IT)