【トラブル】【Java】HttpURLConnection を使ったファイルダウンロードで空のファイル(0Byte)ができる

現象

 * 以下の「サンプル(NG)」でファイルのダウンロードを試みたが、
   空のファイル(0Byte)ができただけで例外も発生しなかった。
 * 元ファイルもしっかり存在することを確認

サンプル(NG)

import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public abstract class Main {
   // ★ここは自分で書き直してください★
   private static final String InputData = "http://k.yimg.jp/images/top/sp2/cmn/logo-ns-131205.png";
   private static final String OutputData = "C:\\Temp\\yahoobloglogo.png";
   public static void main(String[] args) {
      try {
         URL url = new URL(InputData);

         HttpURLConnection connection = (HttpURLConnection) url.openConnection();
         // false の場合、ユーザーとの対話処理は許可されていません。
         connection.setAllowUserInteraction(false);
         // true の場合、プロトコルは自動的にリダイレクトに従います
         connection.setInstanceFollowRedirects(true);
         // URL 要求のメソッドを"GET"に設定
         connection.setRequestMethod("GET");
         connection.connect();

         // HTTP 応答メッセージから状態コードを取得します
         int httpStatusCode = connection.getResponseCode();
         if (httpStatusCode != HttpURLConnection.HTTP_OK) {
            throw new Exception();
         }

         try (DataInputStream inputStream = new DataInputStream(
               connection.getInputStream());
               DataOutputStream outputStream = new DataOutputStream(
                     new BufferedOutputStream(new FileOutputStream(OutputData)))) {

            writeStream(inputStream, outputStream);
         } catch (Exception ex) {
            ex.printStackTrace();
         }

         System.out.println("Completed!!");
      } catch (Exception ex) {
         ex.printStackTrace();
      }
   }

   private static void writeStream(InputStream inputStream,
         OutputStream outputStream) throws IOException {
      int availableByteNumber;
      // ★ここも問題があった★
      while ((availableByteNumber = inputStream.available()) > 0) {
         byte[] buffers = new byte[availableByteNumber];
         int readByteNumber = inputStream.read(buffers);
         if (readByteNumber < 0) {
            break;
         }
         outputStream.write(buffers, 0, readByteNumber);
      }
   }
}

原因

 * InputStream.available()で、0Byteを返していたため。
http://stackoverflow.com/questions/23161119/urlconnection-returning-empty-inputstream
によると、
「InputStream.available() がブロックせずに、全てデータが入手できるまで待ちます。
そして、InputStream.available()を呼び出した時には完全に受信できていないかもしれません」

Java API/ クラス InputStream

https://docs.oracle.com/javase/jp/6/api/java/io/InputStream.html#available()

解決策

 * InputStream.available() を使用せずに実装する。
~~~
int availableByteNumber;
byte[] buffers = new byte[100000];
while((availableByteNumber= dataInputStream.read(buffers)) > 0){
    // Do something here with buffer
}
~~~

サンプル(OK)

http://blogs.yahoo.co.jp/dk521123/34362429.html
に載っているが
   // 略

   private static void writeStream(InputStream inputStream, String outputPath)
         throws Exception {
      int availableByteNumber;
      byte[] buffers = new byte[100000];
      try (DataInputStream dataInputStream = new DataInputStream(inputStream);
            DataOutputStream outputStream = new DataOutputStream(
                  new BufferedOutputStream(new FileOutputStream(outputPath)))) {
         while ((availableByteNumber = dataInputStream.read(buffers)) > 0) {
            outputStream.write(buffers, 0, availableByteNumber);
         }
      } catch (Exception ex) {
         throw ex;
      }
   }


関連記事

URLからファイルをダウンロードするには...

http://blogs.yahoo.co.jp/dk521123/34362429.html