ファイルをWebからダウンロードする (HTTPを用いた非同期ダウンロード) - Android

AndroidアプリでWebからファイルをダウンロードするコードを紹介します。

プロジェクトの作成

Android アプリケーションプロジェクトを作成します。
[New Android Application]ダイアログボックスが表示されますので、以下を設定します。
  • Application Name: "HttpFileDownload"
  • Project Name: "HttpFileDownload"
  • Package Name: "com.iPentec.httpfiledownload"
  • Minimum Required SDK: "API 8: Android 2.2 (Froyo)"
  • Target SDK: "API 17: Android 4.2 (Jelly Bean)"
  • Compile With: "API 17: Android 4.2 (Jelly Bean)"
  • Theme: "Holo Light with Dark Action Bar"

パーミッションの設定

今回はインターネットからファイルをダウンロードしSDカードにデータを書き込みます。以下のパーミッションが必要です。
  • android.permission.INTERNET
  • android.permission.WRITE_EXTERNAL_STORAGE
この2つのuses-permissionを追加します。追加の手順はこちらの記事を参照して下さい。

UIの作成

以下のUIを作成します。 MainActivityのフォームにボタンを1つ配置します。


ボタンの"On Click"プロパティに"button1_click"を設定します。

コード

以下のコードを記述します。

MainActivity.java

package com.iPentec.httpfiledownload;

import java.io.File;

import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.view.Menu;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends Activity {
  private final String DOWNLOAD_FILE_URL = "(ダウンロードするファイルのURL)";
    
  private ProgressDialog progressDialog;
  private ProgressHandler progressHandler; 
  private AsyncFileDownload asyncfiledownload;
  
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    
    progressHandler = new ProgressHandler();
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
  }
  
  public void button1_click(View view){
    initFileLoader(); 
    showDialog(0); 
    progressHandler.progressDialog = progressDialog;
    progressHandler.asyncfiledownload = asyncfiledownload;
    
    if (progressDialog != null && asyncfiledownload != null){
      progressDialog.setProgress(0); 
      progressHandler.sendEmptyMessage(0); 
    }else{
      Toast ts = Toast.makeText(this, "NULLエラー", Toast.LENGTH_LONG);
      ts.show();
    }
  }
  
  private void initFileLoader()
  {
    File sdCard = Environment.getExternalStorageDirectory();
    File directory = new File(sdCard.getAbsolutePath() + "/SampleFolder");
    if(directory.exists() == false){
      if (directory.mkdir() == true){
      }else{
        Toast ts = Toast.makeText(this, "ディレクトリ作成に失敗", Toast.LENGTH_LONG);
        ts.show();  
      }
    }
    File outputFile = new File(directory, "test.jpg");
    asyncfiledownload = new AsyncFileDownload(this,DOWNLOAD_FILE_URL, outputFile);
    asyncfiledownload.execute();

    /*
    //内部メモリの領域を用いる場合
    File dataDir = this.getFilesDir();
    File directory = new File(dataDir.getAbsolutePath()+ "/SampleFolder");
    if(directory.exists() == false){
      if (directory.mkdir() == true){
      }else{
        Toast ts = Toast.makeText(this, "ディレクトリ作成に失敗", Toast.LENGTH_LONG);
        ts.show();  
      }
    }   
    File outputFile = new File(directory, "test.jpg");
    asyncfiledownload = new AsyncFileDownload(this,DOWNLOAD_FILE_URL, outputFile);
    asyncfiledownload.execute();
    */
  }
  
  @Override
  protected void onPause(){
      super.onPause();
      cancelLoad();
  }

  @Override
  protected void onStop(){
      super.onStop();
      cancelLoad();
  }

  private void cancelLoad()
  {
    if(asyncfiledownload != null){
      asyncfiledownload.cancel(true);
    }
  }
  
  @Override
  protected Dialog onCreateDialog(int id){
    switch(id){
      case 0:
        progressDialog = new ProgressDialog(this);
        progressDialog.setIcon(R.drawable.ic_launcher);
        progressDialog.setTitle("Downloading files..");
        progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        progressDialog.setButton(DialogInterface.BUTTON_POSITIVE, "Hide", 
            new DialogInterface.OnClickListener() {
              @Override
              public void onClick(DialogInterface dialog, int which) {
              }
              });
        
        progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", 
            new DialogInterface.OnClickListener() {
              @Override
              public void onClick(DialogInterface dialog, int which) {
                cancelLoad();
              }
            });
      }
      return progressDialog;
  }
}

AsyncFileDownload.java

package com.iPentec.httpfiledownload;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import android.util.Log;

import android.app.Activity;
import android.os.AsyncTask;

public class AsyncFileDownload extends AsyncTask<String, Void, Boolean>{
  private final String TAG = "AsyncFileDownload";
  private final int TIMEOUT_READ = 5000; 
  private final int TIMEOUT_CONNECT = 30000; 
  
  public Activity owner;
  private final int BUFFER_SIZE = 1024;
  
  private String urlString;
  private File outputFile;
  private FileOutputStream fileOutputStream;
  private InputStream inputStream;
  private BufferedInputStream bufferedInputStream;

  private int totalByte = 0; 
  private int currentByte = 0;

  private byte[] buffer = new byte[BUFFER_SIZE];

  private URL url;
  private URLConnection urlConnection;
  
  public AsyncFileDownload(Activity activity, String url, File oFile) {
    owner = activity;
    urlString = url; 
    outputFile = oFile; 
 }
  
  @Override
  protected Boolean doInBackground(String... url) {
    try{
      connect();
    }catch(IOException e){
      Log.d(TAG, "ConnectError:" + e.toString());
      cancel(true);
    }
    
    if(isCancelled()){
      return false;
    }
    if (bufferedInputStream !=  null){
      try{
        int len;
        while((len = bufferedInputStream.read(buffer)) != -1){
            fileOutputStream.write(buffer, 0, len);
            currentByte += len;
            //publishProgress();
            if(isCancelled()){
                break;
            }
        }
      }catch(IOException e){
        Log.d(TAG, e.toString());
        return false;
      }
    }else{
      Log.d(TAG, "bufferedInputStream == null");
    }
    
    try{
      close();
    }catch(IOException e){
      Log.d(TAG, "CloseError:" + e.toString());
    }
    return true; 
  }
  
  @Override 
  protected void onPreExecute(){ 
  }
  
  @Override 
  protected void onPostExecute(Boolean result){ 
  }
    
  @Override
  protected void onProgressUpdate(Void... progress){
  }
    
  private void connect() throws IOException 
  { 
    url = new URL(urlString);
    urlConnection = url.openConnection();
    urlConnection.setReadTimeout(TIMEOUT_READ);
    urlConnection.setConnectTimeout(TIMEOUT_CONNECT);
    inputStream = urlConnection.getInputStream();
    bufferedInputStream = new BufferedInputStream(inputStream, BUFFER_SIZE);
    fileOutputStream = new FileOutputStream(outputFile);

    totalByte = urlConnection.getContentLength();
    currentByte = 0;
  }
  
  private void close() throws IOException
  {
    fileOutputStream.flush();
    fileOutputStream.close();
    bufferedInputStream.close();
  }
  
  public int getLoadedBytePercent()
  {
    if(totalByte <= 0){ 
      return 0; 
    } 
    return (int)Math.floor(100 * currentByte/totalByte); 
  }

}

ProgressHandler.java

package com.iPentec.httpfiledownload;

import android.app.ProgressDialog;
import android.os.Handler;
import android.os.Message;
import android.os.AsyncTask;

public class ProgressHandler extends Handler{
  public ProgressDialog progressDialog;
  public AsyncFileDownload asyncfiledownload;

  @Override
  public void handleMessage(Message msg){
    super.handleMessage(msg);
    if(asyncfiledownload.isCancelled()){
      progressDialog.dismiss();
    }
    else if(asyncfiledownload.getStatus() == AsyncTask.Status.FINISHED){
      progressDialog.dismiss();
    }else{
      progressDialog.setProgress(asyncfiledownload.getLoadedBytePercent());
      this.sendEmptyMessageDelayed(0, 100);
    }
  }
}

実行結果

プロジェクトを実行します。下図の画面が表示されます。


ボタンをタップするとファイルのダウンロードが始まります。プログレスダイアログが表示され進行状況も表示されます。

注意

USBケーブルが本体と接続したままの場合、SDカードが共有状態になっておりアクセス可能にならない場合があります。この場合ファイルがSDカードに書き込めないことがあります。この場合、USBケーブルを外して実行すると正しく動作します。

ファイルの確認方法

ファイルが正しくダウンロードできたかを確認します。

EclipseのDDMSを開きます。上部のタブから[File Explorer]タブを選択します。


ダウンロードされたファイルを選択し、右上の"Pull a file from the device"ボタンを押します。


ファイルを保存ダイアログボックスが開きますので、ファイルの保存先を指定します。


ファイルが保存されました。


ファイルを開いて正しくダウンロードできたかを確認します。

著者
iPentecのプログラマー、最近はAIの積極的な活用にも取り組み中。
とっても恥ずかしがり。
掲載日: 2013-03-29
iPentec all rights reserverd.