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-08MyBatisPlus中@TableField注解的基本使用
這篇文章主要介紹了MyBatisPlus中@TableField注解的基本使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07Spring Boot 集成 Sharding-JDBC + Mybatis-Plus 實現(xiàn)分庫分表功能
這篇文章主要介紹了Spring Boot 集成 Sharding-JDBC + Mybatis-Plus 實現(xiàn)分庫分表功能,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08Spring @RestController注解組合實現(xiàn)方法解析
這篇文章主要介紹了Spring @RestController注解組合實現(xiàn)方法解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-06-06SpringBoot結(jié)合Maven項目依賴版本沖突問題解決
本文主要介紹了SpringBoot結(jié)合Maven項目依賴版本沖突問題解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06