文档章节

Java 解析 IPA 文件,读取 Info.plist 信息

learn_more
 learn_more
发布于 2015/03/05 11:38
字数 912
阅读 4929
收藏 6

    在做移动MDM功能的时候,就遇到了这样一个问题,当用户上传IPA文件时,我如何知道这个IPA文件的相关信息呢?IPA文件有一个很重要的文件Info.plist 就类似于Android程序的Manifest.xml,只要能够从IPA文件中提取出来Info.plist,然后在进行解析提起相关信息,那就马到成功了。那么,按照上面步骤开始了。

1、Java解压IPA文件,只获取 Info.plist

/**
	 * 解压IPA文件,只获取IPA文件的Info.plist文件存储指定位置
	 * @param file
	 * zip文件
	 * @param unzipDirectory
	 * 解压到的目录
	 * @throws Exception
	 */
	private static File getZipInfo(File file, String unzipDirectory)
			throws Exception {
		// 定义输入输出流对象
		InputStream input = null;
		OutputStream output = null;
		File result = null;
		File unzipFile = null;
		ZipFile zipFile = null;
		try {
			// 创建zip文件对象
			zipFile = new ZipFile(file);
			// 创建本zip文件解压目录
			String name = file.getName().substring(0,file.getName().lastIndexOf("."));
			unzipFile = new File(unzipDirectory + "/" + name);
			if (unzipFile.exists()){
				unzipFile.delete();
			}
			unzipFile.mkdir();
			// 得到zip文件条目枚举对象
			Enumeration<ZipEntry> zipEnum = zipFile.getEntries();
			// 定义对象
			ZipEntry entry = null;
			String entryName = null;
			String names[] = null;
			int length;
			// 循环读取条目
			while (zipEnum.hasMoreElements()) {
				// 得到当前条目
				entry = zipEnum.nextElement();
				entryName = new String(entry.getName());
				// 用/分隔条目名称
				names = entryName.split("\\/");
				length = names.length;
				for (int v = 0; v < length; v++) {
					if(entryName.endsWith(".app/Info.plist")){ // 为Info.plist文件,则输出到文件
						input = zipFile.getInputStream(entry);
						result = new File(unzipFile.getAbsolutePath()+ "/Info.plist");
						output = new FileOutputStream(result);
						byte[] buffer = new byte[1024 * 8];
						int readLen = 0;
						while ((readLen = input.read(buffer, 0, 1024 * 8)) != -1){
							output.write(buffer, 0, readLen);
						}
						break;
					}
				}
			}
		} catch (Exception ex) {
			ex.printStackTrace();
		} finally {
			if (input != null)
				input.close();
			if (output != null) {
				output.flush();
				output.close();
			}
			// 必须关流,否则文件无法删除
			if(zipFile != null){
				zipFile.close();
			}
		}
		// 如果有必要删除多余的文件
		if(file.exists()){
			file.delete();
		}
		return result;
	}
/**
	 * IPA文件的拷贝,把一个IPA文件复制为Zip文件,同时返回Info.plist文件
	 * 参数 oldfile 为 IPA文件
	 */
	private static File getIpaInfo(File oldfile) throws IOException {
		try{
			int byteread = 0;
			String filename = oldfile.getAbsolutePath().replaceAll(".ipa", ".zip");
			File newfile = new File(filename);
			if (oldfile.exists()){
				// 创建一个Zip文件
				InputStream inStream = new FileInputStream(oldfile);
				FileOutputStream fs = new FileOutputStream(newfile);     
				byte[] buffer = new byte[1444];            
				while ((byteread = inStream.read(buffer)) != -1){
					fs.write(buffer,0,byteread);                   
				}
				if(inStream != null){
					inStream.close();
				}
				if(fs != null){
					fs.close();	
				}
				// 解析Zip文件
				return unzip(newfile, newfile.getParent());
			}           
		}catch(Exception e){       
			e.printStackTrace();    
		}
		return null;
	}

2、Java读取Info.plist文件,获取需要的信息

/**
	 * 通过IPA文件获取Info信息
	 * 这个方法可以重构,原因是指获取了部分重要信息,如果想要获取全部,那么应该返回一个Map<String,Object>
	 * 对于plist文件中的数组信息应该序列化存储在Map中,那么只需要第三发jar提供的NSArray可以做到!
	 */
	public static Map<String,String> getIpaInfoMap(File ipa) throws Exception{
		
		Map<String,String> map = new HashMap<String,String>();
		File file = getIpaInfo(ipa);
		// 第三方jar包提供
		NSDictionary rootDict = (NSDictionary) PropertyListParser.parse(file);
		// 应用包名
		NSString parameters = (NSString) rootDict.objectForKey("CFBundleIdentifier");
		map.put("CFBundleIdentifier", parameters.toString());
		// 应用名称
		parameters = (NSString) rootDict.objectForKey("CFBundleName");
		map.put("CFBundleName", parameters.toString());
		// 应用版本
		parameters = (NSString) rootDict.objectForKey("CFBundleVersion");
		map.put("CFBundleVersion", parameters.toString());
		// 应用展示的名称
		parameters = (NSString) rootDict.objectForKey("CFBundleDisplayName");
		map.put("CFBundleDisplayName", parameters.toString());
		// 应用所需IOS最低版本
		parameters = (NSString) rootDict.objectForKey("MinimumOSVersion");
		map.put("MinimumOSVersion", parameters.toString());
		
		// 如果有必要,应该删除解压的结果文件
		file.delete();
		file.getParentFile().delete();
		
		return map;
	}

3、程序测试

public static void main(String[] args) throws Exception {
		
		File file = new File("d:/UniAccess.ipa");
		Map<String,String> map = getIpaInfoMap(file);
		for(String key : map.keySet()){
			System.out.println(key+" : "+map.get(key));
		}
		
	}

4、测试结果

CFBundleIdentifier : com.qihoo.installSafety
CFBundleDisplayName : 360手机卫士
CFBundleName : 360MobileSafe
CFBundleVersion : 4.2.0.2
MinimumOSVersion : 5.0

5、相关jar包,以及IPA文件,可以到如下指定地址下载

1)http://download.csdn.net/detail/wp562846864/8474481

2)http://m1.app111.org/2014/09/19/20140919142959.ipa


© 著作权归作者所有

共有 人打赏支持
learn_more
粉丝 92
博文 240
码字总数 210196
作品 0
深圳
程序员
私信 提问
加载中

评论(6)

8446666
8446666
开始也是自己拼串,但是还是用了一个google的开源包:java 解析.plist : [url=https://www.sojson.com/blog/286.html]https://www.sojson.com/blog/286.html[/url]
8446666
8446666
用这个开源的吧:https://www.sojson.com/blog/286.html
铭尘沐风
铭尘沐风
zipFile.getEntries()怎么没有这个方法,
只有getEntry(name)这个方法
learn_more
learn_more

引用来自“骇客神条”的评论

代码不全 unzip(newfile, newfile.getParent());
方法呢?

引用来自“人在海途”的评论

的确是缺少那个方法,你解决了吗
// 解析Zip文件 这个方法就一个ZIP文件解析,这个不需要给出吧...........
人在海途
人在海途

引用来自“骇客神条”的评论

代码不全 unzip(newfile, newfile.getParent());
方法呢?
的确是缺少那个方法,你解决了吗
骇客神条
骇客神条
代码不全 unzip(newfile, newfile.getParent());
方法呢?
ios程序本地化---nib版本

一、简介 * 使用本地化功能,可以轻松地将应用程序翻译成多种语言,甚至可以翻译成同一语言的多种方言 * 如果要添加本地化功能,需要为每种支持的语言创建一个子目录,称为”本地化文件夹”,...

孔俊
2013/06/19
0
0
cjz010/PCScreenQReader

屏幕QR读取器 项目介绍 屏幕QR读取器. 安装教程 下载release目录下所有文件; 运行对应系统的脚本; 截屏后自动解析屏幕上的qr码。 如图 源码可以参考的价值: 1. java最小化到托盘的实现 关闭...

cjz010
07/26
0
0
jvm生命周期之类的加载、连接、初始化

加载 在java中,我们经常会接触到一个词——类加载,它和这里的加载并不是一回事,通常我们说类加 载指的是类的生命周期中加载、连接、初始化三个阶段。在加载阶段,java虚拟机会做什么工作呢...

javatomcat
2014/03/15
0
1
JAVA NIO编程入门(一)

JAVA NIO编程入门(一) 一、前言 笔者之前接触的NIO编程比较少,所以对这一块的基础也比较弱,NIO作为java编程中一个重要的模块,不能很好的掌握它,感觉自己在java方面就掌握的不够,所以,...

木木匠
09/01
0
0
android访问天气服务01--构建XmlPullParser

Pull模式解析原理: 与SAX解析器模拟相似,也提供了类似的事件驱动.使用parser.next()可以进入下一个元素并触发相应事件。事件将作为数值代码被发送,因此可以使用一个switch对感兴趣的事件进...

攀爬的小瓜牛
2014/09/01
0
0

没有更多内容

加载失败,请刷新页面

加载更多

重磅!亚洲诚信实力斩获:“2018 DigiCert/Symantec 年度最佳创新合作伙伴”大奖

2018年11月13日-16日,全球顶级数字证书厂商,DigiCert/Symantec亚太区圆桌会议(Asia Partner Roundtable 2018)在日本大阪隆重召开。 亚洲诚信作为DigiCert/Symantec亚太区白金战略合作伙伴和...

亚洲诚信
22分钟前
2
0
始于阿里,回归社区:阿里8个项目进入CNCF云原生全景图

摘要: 一群技术理想主义者,与太平洋另一边的技术高手们正面PK,在这场躲不开的战役中,一起认真一把。 破土而出的生命力,源自理想主义者心底对技术的信念。 云原生技术正席卷全球,云原生...

阿里云官方博客
30分钟前
3
0
修改this指向(bind、call 和 apply)

一、bind bind 的其中一个用法就是:绑定函数,使其无论怎么样调用都用相同的 this 示例: var obj = { getThis: function() { console.log(this); }};obj.getThis()...

文文1
今天
1
0
WSL安装JDK8

下载地址 JDK_URL https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html UNLIMITED_STRENGTH_URL https://www.oracle.com/technetwork/java/javase/down......

terwergreen
今天
4
0
sparkStreaming基本概念

概述 Spark Streaming 是 Spark Core API 的扩展, 它支持弹性的, 高吞吐的, 容错的实时数据流的处理. 数据可以通过多种数据源获取, 例如 Kafka, Flume, Kinesis 以及 TCP sockets, 也可以通过...

freeli
今天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部