java语言如何生成plist下载ipa文件

 更新时间:2023年08月23日 15:14:38   作者:永不停歇的火车  
这篇文章主要介绍了java语言如何生成plist下载ipa文件问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

java语言生成plist下载ipa文件

在通过html页面下载ipa文件安装,需要通过plist文件下载,并且还要遵循 itms-services协议。 

也就说我们需要生产plist文件,然后通过html页面链接指向plist文件。

下面是通过java语言生成plist文件:

public static String createPlist(String url,String version,String title) throws IOException{
        log.info("==========开始创建plist文件");
        //这个地址应该是创建的服务器地址,在这里用生成到本地磁盘地址
        final String path = GetPropertiesValue.getValues("ios_plists_path");
        File file = new File(path);
        if (!file.exists()) {
            file.setWritable(true);//赋予文件权限
            file.mkdirs();
        }
        String plistFile = GetPropertiesValue.getValues("plists_name");//文件名称
        final String PLIST_PATH = path + plistFile;
        file = new File(PLIST_PATH);
        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        String sub_title = GetPropertiesValue.getValues("plists_sub_title");
        String plist = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
                 + "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
                 + "<plist version=\"1.0\">\n" + "<dict>\n"
                 + "<key>items</key>\n" 
                 + "<array>\n" 
                 + "<dict>\n"
                 + "<key>assets</key>\n" 
                 + "<array>\n" 
                 + "<dict>\n"
                 + "<key>kind</key>\n"
                 + "<string>software-package</string>\n"
                 + "<key>url</key>\n"
                 //你之前所上传的ipa文件路径
                 + "<string>"+url+"</string>\n" 
                 + "</dict>\n" 
                 + "</array>\n"
                 + "<key>metadata</key>\n"
                 + "<dict>\n"
                 + "<key>bundle-identifier</key>\n"
                 //这个是开发者账号用户名,也可以为空,为空安装时看不到图标,完成之后可以看到
                 + "<string></string>\n"
                 + "<key>bundle-version</key>\n"
                 + "<string>"+version+"</string>\n"
                 + "<key>kind</key>\n"
                 + "<string>software</string>\n"
                 + "<key>subtitle</key>\n"
                 + "<string>"+sub_title+"</string>\n"
                 + "<key>title</key>\n"

上面重要的地方有两点

  • url:这个参数是为了找到你自己上传的ipa文件;
  • bundle-identifier:这个参数是开发者账号用户名,可以为空或任意,区别在于安装的过程中有无图标和进度

下面是生成html文件,通过html的方式下载这个ipa文件。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>下载</title>
<script type="text/javascript">
var url = 'https://127.0.0.1:8080//upload/plists/20160606143426371_63551_1.plist';
window.location.href = "itms-services://?action=download-manifest&url=" + url;
</script>
</head>
<body></body>
</html>

注意:访问这个plist文件的时候必须是基于HTTPS的,所以这就需要有一台https服务器 。

这样只要我们只要访问这个html地址,就可以自动下载ipa文件了。 

解析ipa生成plist文件

1.引入工具类jar

        <dependency>
            <groupId>ant</groupId>
            <artifactId>ant</artifactId>
            <version>1.6.5</version>
        </dependency>
        <dependency>
            <groupId>com.googlecode.plist</groupId>
            <artifactId>dd-plist</artifactId>
            <version>1.8</version>
        </dependency>

2.解析代码

package com.ouyeel.ssx.admin.utils;
import com.dd.plist.NSDictionary;
import com.dd.plist.NSString;
import com.dd.plist.PropertyListParser;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
public class AnalysisIpa {
    private static Logger logger = LoggerFactory.getLogger(AnalysisIpa.class);
    /**
     * 解压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 getZipInfo(newfile,newfile.getParent());
            }           
        }catch(Exception e){       
            e.printStackTrace();    
        }
        return null;
    }
    /**
     * 通过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("CFBundleShortVersionString");
        map.put("CFBundleShortVersionString", parameters.toString());
        // 应用展示的名称
        parameters = (NSString) rootDict.objectForKey("CFBundleDisplayName");
        map.put("CFBundleDisplayName", parameters!=null?parameters.toString():null);
        // 应用所需IOS最低版本
        parameters = (NSString) rootDict.objectForKey("MinimumOSVersion");
        map.put("MinimumOSVersion", parameters.toString());
        //文件大小
        map.put("FileSize",String.valueOf(file.length()));
        // 如果有必要,应该删除解压的结果文件
        file.delete();
        file.getParentFile().delete();
        return map;
    }
    /**
     * 生成plist文件,返回
     * @param map:ipa解析信息
     * @param downloadIpaPath:下载ipa文件路径(https://xxx/xxx/x.ipa)
     * @param ipaFilePath:ipa文件存放路径(/home/tomcat/xxx/x.ipa)
     * @return plist下载地址
     */
    public static String createPlistXml(Map<String,String> map,String downloadIpaPath,String ipaFilePath){
        logger.info("==========开始创建plist文件");
        //将.ipa文件变为.plist文件后缀
        ipaFilePath = ipaFilePath.replace(".ipa",".plist");
        File file = new File(ipaFilePath);
        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        String icon = "http://xxx.oss-cn-beijing.aliyuncs.com/chemcn-ec-web/resources/chemcn-app/testApp/5d0b57f2b1696b03c1ca22ab/icon/com.ouyeel.nbapp_1.0.0_100_i.png";
        String plist = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
                +"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
                +"<plist version=\"1.0\">\n"
                +"<dict>\n"
                +"<key>items</key>\n"
                +"<array>\n"
                +"<dict>\n"
                +"<key>assets</key>\n"
                +"<array>\n"
                +"<dict>\n"
                +"<key>kind</key>\n"
                +"<string>software-package</string>\n"
                +"<key>url</key>\n"
                +"<string>"+downloadIpaPath+"</string>\n"
                +"<key>md5-size</key>\n"
                +"<integer>"+map.get("FileSize")+"</integer>\n"
                +"</dict>\n"
                +"<dict>\n"
                +"<key>kind</key>\n"
                +"<string>display-image</string>\n"
                +"<key>needs-shine</key>\n"
                +"<true/>\n"
                +"<key>url</key>\n"
                +"<string>"+icon+"</string>\n"
                +"</dict>\n"
                +"</array>\n"
                +"<key>metadata</key>\n"
                +"<dict>\n"
                +"<key>bundle-identifier</key>\n"
                +"<string>"+map.get("CFBundleIdentifier")+"</string>\n"
                +"<key>bundle-version</key>\n"
                +"<string>"+map.get("CFBundleShortVersionString")+"</string>\n"
                +"<key>kind</key>\n"
                +"<string>software</string>\n"
                +"<key>title</key>\n"
                +"<string>"+map.get("CFBundleName")+"</string>\n"
                +"</dict>\n"
                +"</dict>\n"
                +"</array>\n"
                +"</dict>\n"
                +"</plist>";
        FileOutputStream output = null;
        OutputStreamWriter writer = null;
        try {
            output = new FileOutputStream(file);
            writer = new OutputStreamWriter(output, "UTF-8");
            writer.write(plist);
        } catch (Exception e) {
            logger.error("==========创建plist文件异常:" + e.getMessage());
        }finally {
            if(writer != null){
                writer.close();
            }
            if(output != null){
                output.close();
            }
        }
        logger.info("==========成功创建plist文件");
        return downloadIpaPath.replaceAll(".ipa",".plist");
    }
    public static void main(String[] args) throws Exception {
        File file = new File("/Users/xuchuanjiang/Documents/temp/xxx-xxx-ios.ipa");
        Map<String,String> map = getIpaInfoMap(file);
        for(String key : map.keySet()){
            System.out.println(key+" : "+map.get(key));
        }
        createPlistXml(map,"https://xxx.xxx.com/ssx/static/xxx-xxx-ios.ipa","/Users/xuchuanjiang/Documents/temp/xxx-app-ios.ipa");
    }
}

3.提供下载的plist链接需要是https

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 详解Java8中CompletableFuture类的使用

    详解Java8中CompletableFuture类的使用

    Java 8中引入了CompletableFuture类,它是一种方便的异步编程工具,可以处理各种异步操作,本文将详细介绍CompletableFuture的使用方式,希望对大家有所帮助
    2023-04-04
  • 为什么JDK8中HashMap依然会死循环

    为什么JDK8中HashMap依然会死循环

    这篇文章主要介绍了为什么JDK8中HashMap依然会死循环,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • 解决tomcat发布工程后,WEB-INF/classes下文件不编译的问题

    解决tomcat发布工程后,WEB-INF/classes下文件不编译的问题

    这篇文章主要介绍了解决tomcat发布工程后,WEB-INF/classes下文件不编译的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • SpringRetry重试框架的具体使用

    SpringRetry重试框架的具体使用

    在项目开发中,经常会遇到需要重试的地方。本文主要介绍了SpringRetry重试框架的具体使用,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-07-07
  • idea项目debug模式无法启动的解决

    idea项目debug模式无法启动的解决

    这篇文章主要介绍了idea项目debug模式无法启动的解决,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-02-02
  • Spring中的@Conditional注解使用和原理详解

    Spring中的@Conditional注解使用和原理详解

    这篇文章主要介绍了Spring中的@Conditional注解使用和原理详解,@Conditional在Spring4.0中被引入,用于开发"If-Then-Else"类型的bean注册条件检查,在@Conditional之前,也有一个注解@Porfile起到类似的作用,需要的朋友可以参考下
    2024-01-01
  • BeanFactory与ApplicationContext的区别示例解析

    BeanFactory与ApplicationContext的区别示例解析

    这篇文章主要为大家介绍了BeanFactory与ApplicationContext的区别示例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11
  • Hibernate核心类和接口的详细介绍

    Hibernate核心类和接口的详细介绍

    今天小编就为大家分享一篇关于Hibernate核心类和接口的详细介绍,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-03-03
  • 详解Spring Aop实例之xml配置

    详解Spring Aop实例之xml配置

    本篇文章主要介绍了详解Spring Aop实例之xml配置,使用xml可以对aop进行集中配置,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-04-04
  • Spring Boot Async异步执行任务过程详解

    Spring Boot Async异步执行任务过程详解

    这篇文章主要介绍了Spring Boot Async异步执行任务过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-08-08

最新评论