php上传apk后自动提取apk包信息的使用(示例下载)
通过命令:java -jar axmlprinter2.jar androidmanifest.xml > cmdafter.xml
得到cmdafter.xml文件,然后分析cmdafter.xml文件获取相关信息。
但是遗憾的是,从这文件中可以得到apk包名,但无法得到ico图标文件名及其它相关信息。如下图所示
上图中,比如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的值。
上图中:label="@string/app_name" 表明在string.xml中string的name属性为app_name的值,即为该apk的“软件名称”,这里是“market市场”,如下图所示:
@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写到表单中。如下图所示:
提取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演示下载
下载地址: