在 TS 中解析 ipa 文件

在 TS 中解析 ipa 文件

ipaXcode打包出来的APP的安装包,通过解析ipa中的文件,我们可以获得APPDisplayNameVersionBundleIdentifier等信息,
同时也可以获取到APP证书的相关信息,包括APP安装环境证书的有效期APP开通的功能可安装设备的UDID公共秘钥指纹等。

解析 ipa 可用的工具

在NPM 官网上搜索 app、ipa、package 或 parser 等信息,
可直接使用的插件有:app-info-parser,但是其不支持 TS,经过询问,作者并不明确何时可以适配 TS。
js-app-parser是支持 TS 的解析插件,但是解析 ipa 时会出错,解析 apk 时是正确的(解析非标准打包的 apk 会出错,解决见下一篇)。
综合分析以上插件源码,找到 ipa 解析失败问题并解决,创建一个支持 TSts-package-parser
js-app-parser解析出错的地方是解析info.plist的时候出错了。

分析 ipa

解析 ipa,实际需要解析的文件是 info.plistAppIcon
ipa 实际上就是压缩包。只要将其解压缩即可得到全部内容,再从中找到需要的文件,就可以得到 APP 的信息。

mobileprovision文件中包含 APP 的证书等信息,此文件可由后端进行解析。

解析 ipa
解压缩 ipa

主流的解压缩工具为 jszip,并支持TS,使用 jszip 解压缩 ipa 文件,得到ipa里的所有文件。

解析过程中需要用到的方法

  // 创建jszip对象this._jsZip = new JSZip();/*** 解压文件* @param blob 文件内容* @returns 文件数据:JSZip*/unZipFile(blob: Blob | ArrayBuffer) {return new Promise((resolve, reject) => {this._jsZip.loadAsync(blob).then((zipObjc: JSZip) => {return resolve(zipObjc);}).catch((e) => {return reject('解析File失败');});});}/*** 生成文件* @param path 要压缩的文件路径* @param type 文件类型:OutputType* @returns 生成的文件*/zipFilePathToNeedType<T extends OutputType>(path: string, type: T) {return new Promise((resolve, reject) => {this._jsZip.file(path).async(type).then((result) => {return resolve(result);}).catch(() => {return reject('生成文件失败');});});}

通过以上解压缩方法即可得到解压后的 ipa 对象。

  // 解压ipathis.unZipFile(file).then((zipObjc: JSZip) => {const names = Object.getOwnPropertyNames(zipObjc.files);});// 通过zipObjc得到ipa的全部文件名称和路径const names = Object.getOwnPropertyNames(zipObjc.files);// 通过遍历names得到info.plist文件const plistRegex = /^Payload\/(?:.*)\.app\/Info.plist$/;let plistPaht = '';for (let i = 0; i < names.length; i++) {if (plistRegex.test(names[i])) {plistPath = names[i];break;}}
解析 info.plist 文件

获取到 info.plist 文件路径后,读取到文件内容,类型为:arraybuffer

使用 jszip 创建文件,将得到的文件,转换为 buffer 文件。

  import bufferLib from 'buffer';import { parse as PlistParse } from 'plist';import bplist from 'bplist-parser';this.zipFilePathToNeedType(plistPath, 'arraybuffer').then((arrBuffer: ArrayBuffer) => {// 创建buffer对象const buffer = bufferLib.Buffer.from(arrBuffer);// 根据buffer的第一个元素设置bufferTpeconst bufferType = buffer[0] as number | string;// 解析结果对象let result = null;if (bufferType == 60 || bufferType == '<' || bufferType == 239) {result = PlistParse(buffer.toString()) as any;} else if (bufferType == 98 || bufferType == 'b') {result = bplist.parseBuffer(buffer)[0];} else {throw new Error('Unknown plist buffer type.');}}

至此即可解析出 info.plist 的信息。

Info.name = result.CFBundleDisplayName || result.CFBundleName;
Info.versionName = result.CFBundleShortVersionString;
Info.versionCode = result.CFBundleVersion;
Info.ubndleId = result.CFBundleIdentifier;
Info.platform = 'ios';
// 设置icon信息,解析icon需要用到
if (result.CFBundleIcons) {const icons = result.CFBundleIcons.CFBundlePrimaryIcon.CFBundleIconFiles;if (icons) {Info.icon = icons[icons.length - 1];}
}
解析 AppIcon

获取 icon 路径、名称等信息,解析获取到 png 文件。

  // 使用 js-app-parser 解析icon的方法import { parsePNG } from 'js-app-parser/dist/ios/png-parse';// appIcon路径const appIconRegex = /^Payload\/(?:.*)\.app\/AppIcon[0-9]{2}x[0-9]{2}@[2-3]x.png$/;/*** 解析ipa中的icon.png* @param zipObjc 已解析的ipa对象数据* @param bundleUploadInfo 已解析的info.plist数据对象* @returns ApplicationModel对象*/parserFileToPngIcon(zipObjc: JSZip, bundleUploadInfo: BundleUploadModel): Promise<BundleUploadModel> {return new Promise((resolve, reject) => {// 获取ipa中的全部文件及路径const names = Object.getOwnPropertyNames(zipObjc.files);// 解析info.plist时获取的if (bundleUploadInfo.icon) {// icon的file对象let icon = void 0;for (let i = 0; i < names.length; i++) {if (names[i].indexOf(bundleUploadInfo.icon) >= 0) {// 将icon路径生成file类型数据icon = zipObjc.files[names[i]];break;}}if (icon) {bundleUploadInfo.icon = icon.name;// 解析icon数据this.zipFilePathToNeedType(icon.name, 'uint8array').then((data: Uint8Array) => {const iconPng = parsePNG(data);bundleUploadInfo.iconSteam = iconPng;bundleUploadInfo.iconUrl = URL.createObjectURL(new Blob([iconPng]));resolve(bundleUploadInfo);}).catch((err) => {reject(err);});} else {resolve(bundleUploadInfo);}} else {resolve(bundleUploadInfo);}});}
压缩生成文件

ipa 中其他重要的证书信息等存储在 mobileprovision 中。同样的通过 nams 获取到 mobileprovision,
之后将 info.plist、mobileprovision 等文件压缩为同一个文件。

  // 描述文件路径const provisonRegex = /^Payload\/(?:._)\.app\/(?:._).mobileprovision$/;/*** 压缩解析后需要的文件* @param bundleUploadInfo 解析后的对象* @returns BundleUploadModel*/async unzipFilePathToIpaOrApk(bundleUploadInfo: BundleUploadModel): Promise<BundleUploadModel> {const jszip = new JSZip();const paths = [];paths.push(bundleUploadInfo.plistPath);paths.push(bundleUploadInfo.provisionPath);for (let i = 0; i < paths.length; i++) {const path = paths[i];const name = path.substring(path.indexOf('app/') + 4);await this.zipFilePathToNeedType(path, 'blob').then((result: Blob) => {jszip.folder(`Payload/${bundleUploadInfo.name}.app/`).file(name, result);});}return new Promise((resolve, reject) => {jszip.generateAsync({type: 'blob', // 压缩类型compression: 'DEFLATE', // STORE:默认不压缩 DEFLATE:需要压缩compressionOptions: {level: 9, // 压缩等级1~9 1压缩速度最快,9最优压缩方式},mimeType: 'bundleUploadInfo/iphone',}).then((fileZip) => {bundleUploadInfo.ipaZip = fileZip;resolve(bundleUploadInfo);}).catch((err) => {reject(err);});});}
总结

ipa 的解析主要是解析 info.plist、appIcon。

应用之家即采用了此种方式进行解析。
上传 ipa 后并能解析到应用的安装环境证书的有效期APP开通的功能可安装设备的UDID公共秘钥指纹等具体信息。并提供下载统计等丰富功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/206811.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python-算术运算符详解

运算符 算术运算符 关系运算符 逻辑运算符 赋值运算符 算术运算符&#xff1a;加减乘除 %求余 **平方 // 先算乘方&#xff0c;再算乘除&#xff0c;最后是加减。括号可以改变优先级 0不能作为除数&#xff08;不论是整型0还是浮点0&#xff09; 除法截断&#xff1a;舍弃小…

UDP群聊

客户端 import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader…

LeetCode738. Monotone Increasing Digits

文章目录 一、题目二、题解 一、题目 An integer has monotone increasing digits if and only if each pair of adjacent digits x and y satisfy x < y. Given an integer n, return the largest number that is less than or equal to n with monotone increasing digi…

使用VBA快速统计词组(单词组合)词频

实例需求&#xff1a;产品清单如A列所示&#xff0c;现在如下统计词组词频。想必各位小伙伴都指定如何使用字典对象实现去重&#xff0c;进而实现单个单词的词频统计。 但是统计词组词频就没有那么简单了&#xff0c;为了便于演示&#xff0c;此处的词组只限于两个单词的组合。…

自动驾驶右向辅助功能规范

目 录 Contents 目录 1. 介绍 Introduction. 8 1.1 此文档的范围和目的 Scope and Purpose of This Document 8 1.2 参考文档References. 9 1.3 文档的维护 Maintenance of the Document 10 1.4 缩略词Abbreviations. 10 1.5 文档概述Document Overview.. 11 1.6 功能…

GoLong的学习之路,进阶,Viper(yaml等配置文件的管理)

本来有今天是继续接着上一章写微服务的。但是这几天有朋友说&#xff0c;再写Web框架的时候&#xff0c;遇到一个问题&#xff0c;就是很多的中间件&#xff08;redis&#xff0c;微信&#xff0c;mysql&#xff0c;mq&#xff09;的配置信息写的太杂了&#xff0c;很不好管理。…

【解决办法】Pycharm中新添加或者导入项目文件名红色!

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、问题描述二、问题原因三、解决办法 一、问题描述 Pycharm的代码中添加新的文件夹&#xff0c;发现文件夹下的文件名是红色的&#xff0c;如下图&#xff1a; …

jvm-sandbox-repeater 精简版部署之standalone模式

jvm-sandbox-repeater 仅仅提供了录制回放的能力&#xff0c;如果需要完成业务回归、实时监控、压测等平台&#xff0c;后面须要有一个数据中心负责采集数据的加工、存储、搜索&#xff0c;repeater-console提供了简单的demo示例&#xff1b;一个模块管理平台负责管理JVM-Sandb…

设计模式——单例模式(Singleton Pattern)

概述 单例模式确保一个类只有一个实例&#xff0c;而且自行实例化并向整个系统提供整个实例&#xff0c;这个类称为单例类&#xff0c;它提供全局访问的方法。单例模式是一种对象创建型模式。单例模式有三个要点&#xff1a;一是某个类只能有一个实例&#xff1b;二是它必须自行…

矩阵基本操作算法

题目描述&#xff1a; 题目描述 根据主函数和头文件提示&#xff0c;编写子函数void matrixInput(int (*mat)[COL]); void matrixPrint(int *mat[ROW]); void matrixAddT(int *mat); ​ 分别实现矩阵的输入&#xff0c;输出&#xff0c;与自身的转置相加&#xff1a;void matr…

微信小程序开发中的图片缺少后自动居中问题的解决,自动居中样式,自适应图片多少的写法

下面是写的wxml的view样式类似于web开发中的HTML文件 home-grid是我创建的一个父样式&#xff0c;采用子绝父相的定位方式 我在home-grid的父view&#xff08;类似于web中div&#xff09;中包含了三个小的子样式分别是下述代码中的class“yellowstar”&#xff0c;class“maint…

Java---线程讲解(二)

文章目录 1. Runnable接口2. 卖票案例3. 同步代码块解决数据安全问题4. 同步方法解决数据安全问题5. 线程安全的类6. Lock锁 1. Runnable接口 1. 创建线程的另一种方法是声明一个实现Runnable接口的类&#xff0c;之后重写run()方法&#xff0c;然后可以分配类的实例&#xff0…

基于remix+metamask+ganache的智能合约部署调用

在我们部署合约时为了让它更接近真实区块链去中心化体验&#xff0c;我们需要调用小狐狸&#xff08;Metamask&#xff09;来进行真实交易&#xff0c;而metamask里没有内置虚拟测试币&#xff0c;我们需要进行调用Ganache来添加带有虚拟测试币的账号。以上就是三者的关系&…

从 MLOps 到 LMOps 的关键技术嬗变

本文整理自 2023 年 9 月 3 日 QCon 全球软件开发大会 2023 北京站 —— 从 MLOps 到 LMOps 分论坛的同名主题演讲。 本次分享的内容结构如下&#xff1a; 从 MLOps 到 LMOps&#xff1b; MLOps 概述、挑战与解决方案&#xff1b; LMOps 实施挑战与关键技术&#xff08;大模…

[FPGA 学习记录] 快速开发的法宝——IP核

快速开发的法宝——IP核 文章目录 1 IP 核是什么2 为什么要使用 IP 核3 IP 核的存在形式4 IP 核的缺点5 Quartus II 软件下 IP 核的调用6 Altera IP 核的分类 在本小节当中&#xff0c;我们来学习一下 IP 核的相关知识。 IP 核在 FPGA 开发当中应用十分广泛&#xff0c;它被称为…

Java最全面试题专题---1、Java基础知识(2)

笔者有七八年的面试者经验&#xff0c;负责公司技术同学的社招和校招&#xff0c;近些年面试过三四百个技术同学&#xff0c;考虑接近年底这个时段&#xff0c;整理并更新一些以往的面试经验&#xff0c;希望同学们跳槽能有个更好的工作&#xff0c;如有需要的同学可以关注下笔…

Jenkins安装

环境 Ubuntu&#xff0c; 其他平台查看官方文档 步骤 安装jdk sudo apt-get install openjdk-8-jdk 安装Jenkins first add the key to your system wget -q -O - https://pkg.jenkins.io/debian/jenkins.io.key | sudo apt-key add -Then add a Jenkins apt repository …

使用Golang构建高性能网络爬虫

目录 一、Golang的特点 二、构建网络爬虫的步骤 三、关键技术和注意事项 使用协程进行并发处理 使用通道进行协程间的通信 合理控制并发数和处理速度 遵守网站使用协议和法律法规 防止被网站封禁或限制访问 优化网页解析和数据处理 异常处理和错误处理 日志记录和监控…

Flink入门之部署(二)

三种部署模式 standalone集群&#xff0c;会话模式部署&#xff1a;先启动flink集群 web UI提交shell命令提交&#xff1a;bin/flink run -d -m hadoop102:8081 -c com.atguigu.flink.deployment.Flinke1_NordCount./Flink-1.0-SNAPSHOT.jar --hostname hadoop102 --port 8888 …

vmware虚拟机17 安装macos14过程及问题处理亲测

前期准备 1、可引导可虚拟机安装的macOS Sonoma 14 ISO镜像安装文件 我找到得地址&#xff0c;下载自行解决啦 2、VMware虚拟机应用软件 官网下载就好&#xff0c;搜个码搞定 3、解锁工具macOS Unlocker 开始安装&#xff1a; 1、打开VMware软件&#xff0c;新建一个系统…