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 相关文章推荐
11个PHP 分页脚本推荐
Aug 15 PHP
php检测文件编码的方法示例
Apr 25 PHP
PHP大批量插入数据库的3种方法和速度对比
Jul 08 PHP
PDO防注入原理分析以及注意事项
Feb 25 PHP
yii权限控制的方法(三种方法)
Dec 28 PHP
PHP怎样用正则抓取页面中的网址
Aug 09 PHP
PHP Oauth授权和本地加密实现方法
Aug 12 PHP
PHP面向对象五大原则之单一职责原则(SRP)详解
Apr 04 PHP
因str_replace导致的注入问题总结
Aug 08 PHP
php实现快速对二维数组某一列进行组装的方法小结
Dec 04 PHP
php+mysql+ajax 局部刷新点赞/取消点赞功能(每个账号只点赞一次)
Jul 24 PHP
宝塔面板出现“open_basedir restriction in effect. ”的解决方法
Mar 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 include,include_once,require,require_once
2008/09/05 PHP
php通用防注入程序 推荐
2011/02/26 PHP
php匹配字符中链接地址的方法
2014/12/22 PHP
PHP实现权限管理功能示例
2017/09/22 PHP
php和asp语法上的区别总结
2019/05/12 PHP
yii2.0框架场景的简单使用示例
2020/01/25 PHP
AutoSave/自动存储功能实现
2007/03/24 Javascript
关于js拖拽上传 [一个拖拽上传修改头像的流程]
2011/07/13 Javascript
js获取当前路径的简单示例代码
2014/01/08 Javascript
利用js读取动态网站从服务器端返回的数据
2014/02/10 Javascript
JavaScript中的toDateString()方法使用详解
2015/06/12 Javascript
jQuery实现table中的tr上下移动并保持序号不变的实例代码
2016/07/11 Javascript
JavaScript实现随机点名器实例详解
2019/05/07 Javascript
node中使用log4js4.x版本记录日志的方法
2019/08/20 Javascript
基于VUE的v-charts的曲线显示功能
2019/10/01 Javascript
Python常用正则表达式符号浅析
2014/08/13 Python
深入理解NumPy简明教程---数组1
2016/12/17 Python
python数据结构之列表和元组的详解
2017/09/23 Python
用Python登录好友QQ空间点赞的示例代码
2017/11/04 Python
python_opencv用线段画封闭矩形的实例
2018/12/05 Python
python实现剪切功能
2019/01/23 Python
基于Python的PIL库学习详解
2019/05/10 Python
python使用beautifulsoup4爬取酷狗音乐代码实例
2019/12/04 Python
Python中使用filter过滤列表的一个小技巧分享
2020/05/02 Python
Python绘制组合图的示例
2020/09/18 Python
htmlentities() 和 htmlspecialchars()有什么区别
2015/07/01 面试题
校园安全标语
2014/06/07 职场文书
中国梦演讲稿范文
2014/08/28 职场文书
2014年设备管理工作总结
2014/11/26 职场文书
狼牙山五壮士观后感
2015/06/09 职场文书
小学语文继续教育研修日志
2015/11/13 职场文书
2016年保险公众宣传日活动总结
2016/04/05 职场文书
导游词之南京栖霞山
2019/10/18 职场文书
职场:企业印章管理制度(模板)
2019/10/18 职场文书
Python+Matplotlib图像上指定坐标的位置添加文本标签与注释
2022/04/11 Python
Java 多线程并发FutureTask
2022/06/28 Java/Android