2013 年 12 月 1 日

apkファイルアップローダーを作る(第2回)

apkファイルアップローダーを作る(第2回)

[jQuery File Upload]

前回からかなり時間が過ぎてしまいましたが、サーバーサイドでapktoolを使ってapkファイルを解凍して、アプリのバージョンやパッケージ名apkファイルの内容を返すところを実装します。jQuery File Uploadも前回は6.4.2だったのが、7.0.1にまで上がっています。今回は、最新の7.0.1を利用します。

Webページからのapkファイルアップロードではなく、直接curlコマンドからも実行できるように実装しています。こうしておけば、WebページのUIは、jQUery File Uploadで実装してもよいし、他のUIライブラリで実装も可能です。

UploadApkHander.php

jQuery File UploadのPHPバージョンのサーバサイドであるUploadHandlerクラスを継承して、UploadApkHandlerクラスを作成します。UploadHandlerクラスは、jQuery File Uploadのルートディレクトリ下のserver/phpディレクトリにあります。

<?php
require_once('UploadHandler.php');

class UploadApkHandler extends UploadHandler {

  // apktoolのパス
  const APKTOOL = '[APK_TOOL_PATH]/apktool d --frame-path [APK_TOOL_PATH]/apktool ';

  // 今回はuser_idはセッションIDを利用する。
  private $user_id = null;

  function __construct() {
    // UploaderHanderを初期化する。
    parent::__construct(array(
        'user_dirs' => true,
        'param_name' => 'file',
        'accept_file_types' => '/apk$/i',
        'upload_dir' => '[APKUPLOADER_FILE_PATH]/apkuploader/files/',
        'apk_dir' => '/apk'
	), true, null);
  }

  /**
   * AndroidManifest.xmlをxpathでパースする。
   */
  protected function get_apk_info($apk_file) {
    $dir = $this->options['upload_dir'] . $this->user_id . $this->options['apk_dir'];
    $manifest = simplexml_load_file($dir . '/AndroidManifest.xml');
    $apk_file->package = (string)$manifest->attributes()->package;
    $apk_file->versionCode = (string)$manifest->attributes('android', true)->versionCode;
    $apk_file->versionName = (string)$manifest->attributes('android', true)->versionName;
    $label = $manifest->application->attributes('android', true)->label;
    list(, $label) = explode('/', $label);
    $strings = simplexml_load_file($dir . '/res/values/strings.xml');
    list(, $name) = each($strings->xpath("/resources/string[@name='" . $label . "']"));
    $apk_file->app_name = (string)$name;
    $apk_file->icon_image = $this->options['upload_url'] . $this->user_id . $this->options['apk_dir'] . '/res/drawable-hdpi/ic_launcher.png';
    return $apk_file;
  }

  /**
   * アップロードしたファイルをapktoolで解凍する。
   * @Override
   */
  protected function handle_file_upload($uploaded_file, $name, $size, $type, $error,
            $index = null, $content_range = null) {
    $file = parent::handle_file_upload($uploaded_file, $name, $size, $type, $error, $index, $content_range);
    $apk_file = new StdClass;
    $apk_file->url = $file->url;
    $apk_file->name = $file->name;
    $apk_file->size = $file->size;
    $dir = $this->options['upload_dir'] . $this->user_id;
    $cmd = self::APKTOOL . $dir . '/' . $apk_file->name . ' ' . $dir . $this->options['apk_dir'];
    // apktoolを実行
    exec($cmd, $output, $status);
    // AndroidManifest.xmlをパース
    $this->get_apk_info($apk_file);
    // アップロードしたファイルを削除
    exec('rm -rf ' . $dir);
    return $apk_file;
  }

  /**
   * user_idとしてセッションIDを設定し、保存する。
   * @Override
   */
  protected function get_user_id() {
      @session_start();
      $this->user_id = session_id();
      return $this->user_id;
  }
}

index.php

さきほどのクラスをコンストラクトして、ファイルを受信します。先ほどのhttp:/www.paads.net/apkuploader/index.phpを呼び出すと、ファイルアップロードが始まります。

<?php

require_once('UploadApkHandler.php');

new UploadApkHandler();

?>

curlコマンドから実行

アップロードしたいapkファイルを準備して、以下のようにコマンドを実行します。

$ curl -F file=@test.apk http://www.paads.net/apkuploader/

アップロードが完了すると、以下のようなJSONデータが返ってきます(見やすいように整形しています)。

"file":[{
  "url" : "http://www.paads.net/apkuploader/files/3dbbb9d7f5b6ac8233a0d8fc7cf57321\/test.apk",
  "name" : "test.apk",
  "size" : 372225,
  "package" : "jp.co.notice.hoge",
  "versionCode" : "2",
  "versionName" : "1.0",
  "app_name" : "Hoge",
  "icon_image" : "http://www.paads.net/apkuploader/files/u3dbbb9d7f5b6ac8233a0d8fc7cf57321/apk/res/drawable-hdpi/ic_launcher.png"
}]}

実際の実行結果サンプルとして、http://www.paads.net/apkuploader/を公開しています。curlコマンドで実行すれば、アップロードされます。

UIがないので、わかりにくかもしれませんが、ウェブページのUIとべったりのサーバサイドサービスを設計・実装してしまうと、使いにくくなりますし、ユニットテストも難しくなります。

アップロードされた後、apkファイルを解凍し、AndroidManifest.xmlをパースして、アプリの情報をJSONで返します。アップロードしたファイルをその後消去していますので、ご安心ください。

後は、ファイルはそのまま保存し、アプリの情報はデータベースに保存すれば、apkファイルのバージョン管理ができます。RESTfulのインタフェースを加えていけば、色々なウェブアプリと連動できるようになります。もう少し、手を加えて、アプリの自動更新のサーバサイドのサービスとして実装することも可能です。

関連記事

  1. apkファイルアップローダーを作る(第1回)
  2. apktoolでアンドロイドアプリの.apkファイルの中身を調べる