java語言如何生成plist下載ipa文件
java語言生成plist下載ipa文件
在通過html頁面下載ipa文件安裝,需要通過plist文件下載,并且還要遵循 itms-services協(xié)議。
也就說我們需要生產(chǎn)plist文件,然后通過html頁面鏈接指向plist文件。
下面是通過java語言生成plist文件:
public static String createPlist(String url,String version,String title) throws IOException{
log.info("==========開始創(chuàng)建plist文件");
//這個地址應(yīng)該是創(chuàng)建的服務(wù)器地址,在這里用生成到本地磁盤地址
final String path = GetPropertiesValue.getValues("ios_plists_path");
File file = new File(path);
if (!file.exists()) {
file.setWritable(true);//賦予文件權(quán)限
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"
//這個是開發(fā)者賬號用戶名,也可以為空,為空安裝時看不到圖標(biāo),完成之后可以看到
+ "<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:這個參數(shù)是為了找到你自己上傳的ipa文件;
- bundle-identifier:這個參數(shù)是開發(fā)者賬號用戶名,可以為空或任意,區(qū)別在于安裝的過程中有無圖標(biāo)和進(jìn)度
下面是生成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服務(wù)器 。
這樣只要我們只要訪問這個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 {
? ? ? ? ? ? // 創(chuàng)建zip文件對象
? ? ? ? ? ? zipFile = new ZipFile(file);
? ? ? ? ? ? // 創(chuàng)建本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;
? ? ? ? ? ? // 循環(huán)讀取條目
? ? ? ? ? ? while (zipEnum.hasMoreElements()) {
? ? ? ? ? ? ? ? // 得到當(dāng)前條目
? ? ? ? ? ? ? ? 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();
? ? ? ? ? ? }
? ? ? ? ? ? // 必須關(guān)流,否則文件無法刪除
? ? ? ? ? ? if(zipFile != null){
? ? ? ? ? ? ? ? zipFile.close();
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? // 如果有必要刪除多余的文件
? ? ? ? if(file.exists()){
? ? ? ? ? ? file.delete();
? ? ? ? }
? ? ? ? return result;
? ? }
/**
? ? ?* IPA文件的拷貝,把一個IPA文件復(fù)制為Zip文件,同時返回Info.plist文件
? ? ?* 參數(shù) 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()){
? ? ? ? ? ? ? ? // 創(chuàng)建一個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信息
? ? ?* 這個方法可以重構(gòu),原因是指獲取了部分重要信息,如果想要獲取全部,那么應(yīng)該返回一個Map<String,Object>
? ? ?* 對于plist文件中的數(shù)組信息應(yīng)該序列化存儲在Map中,那么只需要第三發(fā)jar提供的NSArray可以做到!
? ? ?*/
? ? public static Map<String,String> getIpaInfoMap(File ipa) throws Exception{
? ? ? ? Map<String,String> map = new HashMap<String,String>();
? ? ? ? File file = getIpaInfo(ipa);
? ? ? ? // 第三方j(luò)ar包提供
? ? ? ? NSDictionary rootDict = (NSDictionary) PropertyListParser.parse(file);
? ? ? ? // 應(yīng)用包名
? ? ? ? NSString parameters = (NSString)rootDict.objectForKey("CFBundleIdentifier");
? ? ? ? map.put("CFBundleIdentifier", parameters.toString());
? ? ? ? // 應(yīng)用名稱
? ? ? ? parameters = (NSString) rootDict.objectForKey("CFBundleName");
? ? ? ? map.put("CFBundleName", parameters.toString());
? ? ? ? // 應(yīng)用版本
? ? ? ? parameters = (NSString) rootDict.objectForKey("CFBundleShortVersionString");
? ? ? ? map.put("CFBundleShortVersionString", parameters.toString());
? ? ? ? // 應(yīng)用展示的名稱
? ? ? ? parameters = (NSString) rootDict.objectForKey("CFBundleDisplayName");
? ? ? ? map.put("CFBundleDisplayName", parameters!=null?parameters.toString():null);
? ? ? ? // 應(yīng)用所需IOS最低版本
? ? ? ? parameters = (NSString) rootDict.objectForKey("MinimumOSVersion");
? ? ? ? map.put("MinimumOSVersion", parameters.toString());
? ? ? ? //文件大小
? ? ? ? map.put("FileSize",String.valueOf(file.length()));
? ? ? ? // 如果有必要,應(yīng)該刪除解壓的結(jié)果文件
? ? ? ? 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("==========開始創(chuàng)建plist文件");
? ? ? ? //將.ipa文件變?yōu)?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("==========創(chuàng)建plist文件異常:" + e.getMessage());
? ? ? ? }finally {
? ? ? ? ? ? if(writer != null){
? ? ? ? ? ? ? ? writer.close();
? ? ? ? ? ? }
? ? ? ? ? ? if(output != null){
? ? ? ? ? ? ? ? output.close();
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? logger.info("==========成功創(chuàng)建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
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
若依 MyBatis改為MyBatis-Plus的實現(xiàn)步驟
本文主要介紹了若依 MyBatis改為MyBatis-Plus的實現(xiàn)步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-08-08
MyBatisPlus中@TableField注解的基本使用
這篇文章主要介紹了MyBatisPlus中@TableField注解的基本使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07
Spring Boot 集成 Sharding-JDBC + Mybatis-Plus 實現(xiàn)分庫分表功能
這篇文章主要介紹了Spring Boot 集成 Sharding-JDBC + Mybatis-Plus 實現(xiàn)分庫分表功能,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08
Spring @RestController注解組合實現(xiàn)方法解析
這篇文章主要介紹了Spring @RestController注解組合實現(xiàn)方法解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-06-06
SpringBoot結(jié)合Maven項目依賴版本沖突問題解決
本文主要介紹了SpringBoot結(jié)合Maven項目依賴版本沖突問題解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06

