php上传apk后自动提取apk包信息的使用(示例下载)


Posted in PHP onApril 26, 2013

进入公司第一个项目就是做market市场。所以后台要上传APK软件之类。为了方便,上传APK后由系统自动提取APK文件的相关信息,比如:apk包名、产品名称、版本信息、APK Code、程序大小、ICON等。起初处理方式

通过命令:java -jar AXMLPrinter2.jar AndroidManifest.xml > cmdAfter.xml
得到cmdAfter.xml文件,然后分析cmdAfter.xml文件获取相关信息。

但是遗憾的是,从这文件中可以得到apk包名,但无法得到ico图标文件名及其它相关信息。如下图所示

php上传apk后自动提取apk包信息的使用(示例下载)

上图中,比如label、icon等都是标志值,无法直接得到需要的结果。曾经分析该值与APK文件内部文件的关系,但不同的APK构造不同,实现过于麻烦。事实上,网上一些安桌市场等网站,当你上传APK时,除了提取出APK包名外,还包括ICON图标、大小等信息。因此,即然别人可以实现,我想肯定有办法来解决这个事情。于是经过研究,得到预期结果。在此将方法做个记录,欢迎交流。

核心提取APK信息代码
  /***
   * 分析已上传的APK文件,提取所需要的数据
   */
  function upAPK(){
    global $_config_product_apktool_count;//使用apktool.jar解压的次数,原因下面有说明。
    if($this->msg!='')return;//如果有错误,返回
    $dir=$this->upload_path;//上传路径
    $stringsXML_exists=false;
    if(file_exists($dir.'package/res/values/strings.xml'))unlink($dir.'package/res/values/strings.xml');
    for($i=0;$i<$_config_product_apktool_count && !$stringsXML_exists;$i++){
      //针对UC的APK包或其类似的APK包,解压一次并不能完全得到strings.xml文件或相关文件。目前只有采用这个办法了。
      //在系统cmd下直接使用java -jar ...执行解压,有时可以得到strings.xml文件,有时也得不到,不知道是不是jar包的问题。
      exec('java -jar ../apktool.jar d -f '.$this->tmpFile.' '.$dir.'package');//注释:解压完毕再往下执行
      $stringsXML_exists=file_exists($dir.'package/res/values/strings.xml');
    }
    //检查AndroidManifest.xml文件是否存在,如果不存在,则不是合法的APK文件
    if(!file_exists($dir.'package/AndroidManifest.xml')){$this->msg='不是合法的APK文件,请重新上传!';return;}
    $AndroidManifestXML=file_get_contents($dir.'package/AndroidManifest.xml');//读取AndroidManifest.xml 
    if(preg_match('/package=\"([^\"]*)\"/i',$AndroidManifestXML,$package))$returnVal['package']=$package[1];//如果有包名,返回到数组
    //增加versionCode
    if(preg_match('/versionCode=\"([^\"]*)\"/i',$AndroidManifestXML,$versionCode))$returnVal['versionCode']=$versionCode[1];//如果有版本代码,返回到数组
    //检测到包名后判断数据库中是否已经存在。
    if($this->id==0){//添加新产品时检测,修改产品不检测
      if($returnVal['package']!=''){
        $sql='select id from product where package='.SqlEncode($package[1]);
        $result=mysql_query($sql);
        if(mysql_num_rows($result)>0){
          $this->msg='该APK已经存在,请更换!';
          return;
        }
      }else{
        $this->msg='系统无法检测该APK信息,请联系管理员!';
        return;
      }
    }
    if($stringsXML_exists)$stringXML=file_get_contents($dir.'package/res/values/strings.xml');//如果有strings.xml则读取strings.xml文件
    if(preg_match('/versionName=\"([^\"]*)\"/i',$AndroidManifestXML,$ver))$returnVal['ver']=$ver[1];//如果有版本号,返回到数组
    //版本号的情况目前发现有两种:1、版本号在AndroidManifest.xml中直接列出;通过以上正则即可提取
    //2、版本号同label一样,放到strings.xml文件中
    //2011-11-23 add
    if($stringXML!='' && strstr($ver[1],'@')){
      if(preg_match('/^@string\/(.*)/i',$ver[1],$findVer)){
        if(preg_match('/<string name=\"'.$findVer[1].'\">([^<]*)<\/string>/',$stringXML,$a))$returnVal['ver']=$a[1];
      }
    }
    ////////////////////////////////////////////
    if(preg_match('/<application[\s\S]*? android:icon="@drawable\/([^"]*)"/i',$AndroidManifestXML,$icon))$returnVal['thumbimg']=$icon[1];//如果有图标,返回到数组
    if($stringsXML_exists && preg_match('/<application[\s\S]*? android:label="@string\/([^"]*)"/i',$AndroidManifestXML,$label)){
      if(preg_match('/<string name=\"'.$label[1].'\">([^<]*)<\/string>/',$stringXML,$name)){
        $returnVal['name']=$name[1];//如果有产品名称,返回到数组
        /**
        百度:strings.xml
        特殊情况1:<string name="app_name">"  掌上百度  "</string>
        */
        $returnVal['name']=preg_replace('/\s|"/','',$returnVal['name']);
      }
    }
    //$this->msg=$returnVal['package'].'--'.$returnVal['ver'].'--'.$returnVal['thumbimg'].'--'.$returnVal['name'];
    if($this->oldAPK!=''){//重新上传则删除原apk文件和icon.png图片
      unlink($dir.$this->oldAPK);
      unlink($dir.$this->oldAPK.'.png');
    }
    //遍历package/res目录下的目录[drawable|drawable-hdpi|drawable-nodpi|drawable-ldpi|drawable-mdpi]
    //系统取icon尺寸最大的图标
    $tmpArr[0]=0;$tmpArr[1]=0;$tmpArr[2]='drawable';
    $dirs=opendir($dir.'package/res');
    while(($file=readdir($dirs))){
      preg_match('/(drawable(-.*?dpi)?)/i',$file,$drawable_folder);
      $iconPath=$dir.'package/res/'.$drawable_folder[1].'/'.$returnVal['thumbimg'].'.png';
      if(file_exists($iconPath)){
        $iconInfo=getimagesize($iconPath);
        if($iconInfo[0]>$tmpArr[0] && $iconInfo[1]>$tmpArr[1]){
          $tmpArr[0]=$iconInfo[0];$tmpArr[1]=$iconInfo[1];$tmpArr[2]=$drawable_folder[1];
        }
      }
    }
    //$this->msg=$iconInfo[0].'---'.$iconInfo[1];
    closedir($dirs);
    if(rename($dir.'package/res/'.$tmpArr[2].'/'.$returnVal['thumbimg'].'.png',$dir.$this->iframe_key.'.apk.png')){//找到目录并成功移动
      $returnVal['thumbimg']=$this->iframe_key.'.apk.png';
    }
    if(!move_uploaded_file($this->tmpFile,$dir.$this->iframe_key.'.apk')){$this->msg='上传失败!';return;}//转移apk文件
    $returnVal['filename']=$this->iframe_key.'.apk';
    $returnVal['size']=$this->size;
    $this->result=$returnVal;
  }   

提取信息流程

1、首先,通过apktool.jar命令提取apk文件中package/res/values/string.xml文件。不知为什么原因,释放apk文件时,有时并不一定得到string.xml文件。所以,后台增加:$_config_product_apktool_count参数,来控制释放的最大次数。

2、读取释放根目录下的AndroidManifest.xml文件。从该文件中可以获取到APK包名、版本信息。

3、检测,如果是新上传的APK,则其包名在数据库中是否存在。就是禁止上传相同包名的APK。修改时不检测。

4、通过正则获取所需要的信息。

这里为什么要提取string.xml文件?

因为并不是所有信息,都在AndroidManifest.xml中。有的信息在AndroidManifest.xml中只是做为一个“引用”,真实记录是在string.xml中的。比如

AndroidManifest.xml中关于Label和icon的值。

php上传apk后自动提取apk包信息的使用(示例下载)

上图中:label="@string/app_name" 表明在string.xml中string的name属性为app_name的值,即为该APK的“软件名称”,这里是“Market市场”,如下图所示:

php上传apk后自动提取apk包信息的使用(示例下载)

@drawable/quickflick_icon,表示quickflick_icon为ICON的文件名。

由于特殊需要,我需要找到最大的ICON图标,见下面代码:

     //遍历package/res目录下的目录[drawable|drawable-hdpi|drawable-nodpi|drawable-ldpi|drawable-mdpi]
    //系统取icon尺寸最大的图标
    $tmpArr[0]=0;$tmpArr[1]=0;$tmpArr[2]='drawable';
    $dirs=opendir($dir.'package/res');
    while(($file=readdir($dirs))){
      preg_match('/(drawable(-.*?dpi)?)/i',$file,$drawable_folder);
      $iconPath=$dir.'package/res/'.$drawable_folder[1].'/'.$returnVal['thumbimg'].'.png';
      if(file_exists($iconPath)){
        $iconInfo=getimagesize($iconPath);
        if($iconInfo[0]>$tmpArr[0] && $iconInfo[1]>$tmpArr[1]){
          $tmpArr[0]=$iconInfo[0];$tmpArr[1]=$iconInfo[1];$tmpArr[2]=$drawable_folder[1];
        }
      }
    }
    //$this->msg=$iconInfo[0].'---'.$iconInfo[1];
    closedir($dirs); 
 

经过分析,一般APK中存放ICON图标在以下几个目录:drawable|drawable-hdpi|drawable-nodpi|drawable-ldpi|drawable-mdpi,通过遍历比较的方式获取最大ICON图标,并移到临时目录。

将所有需要提取的信息,存到一个数组中,并通过javascript写到表单中。如下图所示:

php上传apk后自动提取apk包信息的使用(示例下载)

提取APK信息总结

上面的代码,目前为止,在提取上传的APK中,能能正常提取信息,未发现错误。在上面代码的注释中也看到,关于“掌上百度”这款APK,提取不了信息,是由于他的特殊处理方式,即:<string name="app_name">"  掌上百度  "</string>,他在名称中加上了双引号,这算是一个特例了吧。更多的特例我目前还未发现,所以,有可能会有特例出现,这需要分析APK的数据,并在程序中做特殊处理。

在实现这个APK提取功能中,关键是要找到APK包的组织规律,只有找到规律,程序实现就是在自然不过的事。

释放APK文件注意内容

exec('java -jar ../apktool.jar d -f '.$this->tmpFile.' '.$dir.'package');

能顺利执行上面的语句,要符合下面条件:

1、安装java包,对java目录,users用户组的权限有:读取和运行、列出文件夹目录、读取

2、cmd.exe文件,users用户组的权限有:读取和运行、读取

3、PHP允许调用exec

4、上传目录要确保有写入文件的权限

如果有更好的提取方式,欢迎交流,相互学习。

PHP提取APK信息DEMO演示下载

下载地址:http://xiazai.3water.com/201304/yuanma/php_apk_3waternet.rar

PHP 相关文章推荐
PHP动态图像的创建
Oct 09 PHP
PHP静态新闻列表自动生成代码
Jun 14 PHP
php遍历目录viewDir函数
Dec 15 PHP
php设计模式之观察者模式的应用详解
May 21 PHP
PHP中trim()函数简单使用指南
Apr 16 PHP
php使用Jpgraph绘制简单X-Y坐标图的方法
Jun 10 PHP
PHP简单字符串过滤方法示例
Sep 04 PHP
php数组指针操作详解
Feb 14 PHP
PHP实现Session入库/存入redis的方法
May 04 PHP
PHP观察者模式原理与简单实现方法示例
Aug 25 PHP
Laravel 加载第三方类库的方法
Apr 20 PHP
laravel 5.3 单用户登录简单实现方法
Oct 14 PHP
关于二级目录拖拽排序的实现(源码示例下载)
Apr 26 #PHP
使用php发送有附件的电子邮件-(PHPMailer使用的实例分析)
Apr 26 #PHP
PHP中基于ts与nts版本- vc6和vc9编译版本的区别详解
Apr 26 #PHP
Eclipse中php插件安装及Xdebug配置的使用详解
Apr 25 #PHP
使用php+apc实现上传进度条且在IE7下不显示的问题解决方法
Apr 25 #PHP
PHP中操作ini配置文件的方法
Apr 25 #PHP
基于python发送邮件的乱码问题的解决办法
Apr 25 #PHP
You might like
php 分库分表hash算法
2009/11/12 PHP
php表单转换textarea换行符的方法
2010/09/10 PHP
php中session_unset与session_destroy的区别分析
2011/06/16 PHP
php数组查找函数in_array()、array_search()、array_key_exists()使用实例
2014/04/29 PHP
destoon文章模块调用企业会员资料的方法
2014/08/22 PHP
phalcon model在插入或更新时会自动验证非空字段的解决办法
2016/12/29 PHP
使用Git实现Laravel项目的自动化部署
2019/11/24 PHP
TP5框架实现自定义分页样式的方法示例
2020/04/05 PHP
firefo xml 读写实现js代码
2009/06/11 Javascript
javascript小数四舍五入多种方法实现
2012/12/23 Javascript
javascript自启动函数的问题探讨
2013/10/05 Javascript
jquery动态添加option示例
2013/12/30 Javascript
简单的邮箱登陆的提示效果类似于yahoo邮箱
2014/02/26 Javascript
jQuery时间戳和日期相互转换操作示例
2018/12/07 jQuery
Vue中点击active并第一个默认选中功能的实现
2020/02/24 Javascript
js实现ajax的用户简单登入功能
2020/06/18 Javascript
[03:12]完美世界DOTA2联赛PWL DAY9集锦
2020/11/10 DOTA
使用python调用浏览器并打开一个网址的例子
2014/06/05 Python
Python多线程结合队列下载百度音乐的方法
2015/07/27 Python
Python Django使用forms来实现评论功能
2016/08/17 Python
Python读取图片属性信息的实现方法
2016/09/11 Python
深入浅析Python获取对象信息的函数type()、isinstance()、dir()
2018/09/17 Python
Python列表常见操作详解(获取,增加,删除,修改,排序等)
2019/02/18 Python
python保存字典和读取字典的实例代码
2019/07/07 Python
Python CSS选择器爬取京东网商品信息过程解析
2020/06/01 Python
Python虚拟环境的创建和包下载过程分析
2020/06/19 Python
Python matplotlib模块及柱状图用法解析
2020/08/10 Python
Python发送邮件实现基础解析
2020/08/14 Python
详解html2canvas截图不能截取圆角图片的解决方案
2018/01/30 HTML / CSS
苹果美国官方商城:Apple美国
2016/08/24 全球购物
Mavi牛仔裤美国官网:土耳其著名牛仔品牌
2016/09/24 全球购物
工作表扬信的范文
2014/01/10 职场文书
项目考察欢迎辞
2014/01/17 职场文书
北京大学自荐信范文
2014/01/28 职场文书
仓库管理员岗位职责
2015/02/03 职场文书
同事欢送会致辞
2015/07/31 职场文书