不用appstore照样发布应用,无限应用下载实战


首先声明一下小弟对ios也不是很熟,不喜勿喷啊大笑


首先说一下需求吧,现在有很多的应用已经是存储在数据库中,而ios的设备只需打开指定的网址点击相应应用链接即可完成应用的下载。


好,现在开始走流程具体实现的过程。


一、连接到sybase数据库(应用的数据是存储在这个数据库中的),连接需要的jdbc自己上网上找(jconn3.jar)。

package com.justsy.db;

import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DBManager {
	// 返回数据库连接对象
	public Connection createConn() {
		Connection conn = null;
		try {
			Class.forName("com.sybase.jdbc3.jdbc.SybDriver");
			String url = "jdbc:sybase:Tds:192.168.2.143:2638/AfariaDb_Asa11";
			conn = DriverManager.getConnection(url, "akwolf", "123456");
			return conn;

		} catch (Exception fe) {
			System.err.println("createConn(): " + fe.getMessage());
			return null;
		}

	}
}


二、一个设备要得到可用的应用列表是传递当前设备id到数据库中取得的,

<?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>AppList</title>
        <script type="text/javascript" src="js/jquery-1.6.4.min.js">
        </script>
        <script type="text/javascript">
            var param = window.location.search;
            var deviceId, packageId;
            var param = param.substring(1);
            var params = param.split("&");
            $.each(params, function(index){
                var m = this.split("=");
                if (index == 0) {
                    deviceId = m[1];
                }
                else 
                    if (index == 1) {
                        packageId = m[1];
                    }
            });
            $(function(){
                // ajax request
                $.post("Applet", {
                    "action": "get_app_list",
                    "DeviceID": deviceId,
                    "PackageTypeID": packageId
                }, function(data){
					var apps = $("#apps") ;
					var br = $("<br/>") ;
                    var rows = $(data).find("row") ;
					$.each(rows,function(){
						var row = $(this) ;
						var div = $("<div></div>") ;
						var img = $("<img src='Applet?action=downImg:PackageID="+row.attr("PackageID")+":ContentTypeID=4'/>")
						var a = $("<a href='#'></a>") ;
						a.text(row.attr("PackageName")) ;
						var pId = row.attr("PackageID") ;
						pId = pId.substring(1,pId.length-1) ;
						a.attr("href","itms-services://?action=download-manifest&url=http://192.168.2.101:8080/JustsyApp/RroductFile?PackageID:"+pId) ;
						img.appendTo(div) ;
						a.appendTo(div) ;
						div.appendTo(apps) ;
					}) ;
                }, "xml");
            });
            
            function createXml(str){
                if (document.all) {
                    var xmlDom = new ActiveXObject("Microsoft.XMLDOM");
                    xmlDom.loadXML(str);
                    return xmlDom;
                }
                else 
                    return new DOMParser().parseFromString(str, "text/xml");
            }
        </script>
    </head>
    <body>
    	<div id="apps">
			<a href="itms-services://?action=download-manifest&url=http://192.168.2.101:8080/JustsyApp/template.plist">Install App</a>
    	</div>
    </body>
</html>


template.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
   <key>items</key>
   <array>
       <dict>
           <key>assets</key>
           <array>
               <dict>
                   <key>kind</key>
                   <string>software-package</string>
                   <key>url</key>
                   <string>http://192.168.2.27:8080/justsy/CSuiteBuyer_Int1.ipa</string>
               </dict>
               <dict>
                   <key>kind</key>
                   <string>display-image</string>
                   <key>needs-shine</key>
                   <true/>
                   <key>url</key>
                   <string>http://192.168.2.27:8080/justsy/ifw114.png</string>
               </dict>
	       <dict>
                   <key>kind</key>
                   <string>full-size-image</string>
                   <key>needs-shine</key>
                   <true/>
                   <key>url</key>
                   <string>http://192.168.2.27:8080/justsy/ifw114.png</string>
               </dict>
           </array><key>metadata</key>
           <dict>
               <key>bundle-identifier</key>
               <string>com.sap.DS4M.C-SuiteBuyer.internal</string>
               <key>bundle-version</key>
               <string>8.0.3.99</string>
               <key>kind</key>
               <string>software</string>
               <key>subtitle</key>
               <string>Everlight</string>
               <key>title</key>
               <string>Everlight</string>
           </dict>
       </dict>
   </array>
</dict>
</plist>



好对上面那段代码我要解释一下:

1、取得设备id使用ajax请求回可用的应用列表,并拼装成超链接列表。

2、在拼装超链接列表时是有讲究的,<a href="itms-services://?action=download-manifest&url=http://192.168.2.101:8080/JustsyApp/template.plist">Install App</a>

红色部分是一个应用的描述文件,而描述文件中指明了应用下载的地址,和显示图片的地址及一些描述信息

注意点:在做到这一步时遇到一个的问题,在url超链接中无法接参数加上"=","{}"或"&"之类的字符点击就没反应,最后的解决方案是使用":"进行参数的分割。不知道这其中有什么门道,知道的大虾可以指点哈。


三、好了现在的任务重点是根据点击的超链接动态的生成plist的描述xml文件,这里使用jdom(jar包自己上网上找)进行xml的相关操作,恩,,把根据应用id生成plist描述文件的代码贴出来

package com.justsy.productXML.jdom;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.xml.sax.SAXException;

import com.justsy.db.DBManager;

public class RroductFile extends HttpServlet {
	private static final long serialVersionUID = 1L;


	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		doPost(request, response);
	}

	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		try {
			String param = "" ;
			
			// 格式[PackageID:2a67a513-bbd5-431d-8630-f03b39b89ea1]
			Enumeration<String> en = request.getParameterNames() ;
			if(en.hasMoreElements()){
				param = en.nextElement() ;
			}
			// 取得AppList.html中传递的PackageID,并将PackageId拼装到plist中并输出
			String PackageID = param.substring(param.lastIndexOf(":")+1) ;
			String responseText = this.cereatePlist(this.getServletContext()
					.getRealPath("/"), PackageID);

			OutputStream os = response.getOutputStream();
			byte stringMsg[] = responseText.getBytes();
			os.write(stringMsg);
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (JDOMException e) {
			e.printStackTrace();
		}
	}
	
	private String cereatePlist(String path, String PackageID)
			throws IOException, SQLException, JDOMException {
		SAXBuilder sb = new SAXBuilder();
		// 加载plist模板
		Document doc = sb.build(new File(path + "manifest.plist"));
		
		Element root = doc.getRootElement(); // 获取根元素
		Element root2 = root.getChild("dict");
		Element root3 = root2.getChild("array");
		Element root4 = root3.getChild("dict");
		Element root5 = root4.getChild("array");
		
		Element metadataDict = root4.getChild("dict") ;

		List root6 = root5.getChildren("dict");

		Element msg = (Element) root6.get(0);
		List stringList = msg.getChildren("string");
		Element msgIpa = (Element) stringList.get(1);
		
		//System.out.println(PackageID);
		// 指向app下载的路径
		msgIpa.setText("http://192.168.2.101:8080/JustsyApp/Applet?action=downPlist:ContentTypeID=1:PackageID="
				+ PackageID);

		Element msg2 = (Element) root6.get(1);
		List stringList2 = msg2.getChildren("string");
		Element msgImg = (Element) stringList2.get(1);
		// 指向小图片显示地址
		msgImg.setText("http://192.168.2.101:8080/JustsyApp/Applet?action=downImg:ContentTypeID=4:PackageID="+PackageID);
		Element msg3 = (Element) root6.get(2);
		List stringList3 = msg3.getChildren("string");
		Element msgSmallImg = (Element) stringList3.get(1);
		// 指向大图片显示地址
		msgSmallImg.setText("http://192.168.2.101:8080/JustsyApp/Applet?action=downImg:ContentTypeID=5:PackageID="+PackageID);
		
		
		InputStream stream = getAppInfoStream("{"+PackageID+"}") ;
		saxContentInfo(metadataDict,stream) ;
		
		Format format = Format.getPrettyFormat();
		XMLOutputter xmlout = new XMLOutputter(format);
		ByteArrayOutputStream bo = new ByteArrayOutputStream();
		xmlout.output(doc, bo);
		String xmlStr = bo.toString();
		System.out.println("xml created!!!");
		bo.flush() ;
		bo.close() ;
		System.out.println(xmlStr);
		return xmlStr;

	}
	
	// getContentInfo
	// 对软件信息进行解析
	
	private void saxContentInfo(Element metaData,InputStream stream) throws JDOMException, IOException{
		
		List strings = metaData.getChildren("string") ;
		Element identifier = (Element) strings.get(0) ;
		Element version = (Element) strings.get(1) ;
		Element subtitle = (Element) strings.get(3) ;
		Element title = (Element) strings.get(4) ;
		
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance() ;
		
		try {
			DocumentBuilder builder = factory.newDocumentBuilder() ;
			org.w3c.dom.Document doc = builder.parse(stream) ;
			
			String val1 = doc.getElementsByTagName("CFBundleIdentifier").item(0).getFirstChild().getNodeValue() ;
			String val2 = doc.getElementsByTagName("CFBundleVersion").item(0).getFirstChild().getNodeValue() ;
			String val3 = doc.getElementsByTagName("CFBundleName").item(0).getFirstChild().getNodeValue() ;
			String val4 = doc.getElementsByTagName("CFBundleDisplayName").item(0).getFirstChild().getNodeValue() ;
			//System.out.println(val);
			identifier.setText(val1) ;
			version.setText(val2) ;
			subtitle.setText(val3) ;
			title.setText(val4) ;
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		}
	}
	
	
	private InputStream getAppInfoStream(String packageId) throws SQLException, IOException {
		//String str = "" ;
		DBManager db = new DBManager();
		Connection conn = db.createConn();
		
		String sql = "select * from A_PRFL_PACKAGE_CONTENT where PackageID= ? and ContentTypeID=1";
		PreparedStatement pstmt = conn.prepareStatement(sql) ;
		pstmt.setString(1, packageId) ;
		ResultSet rs = pstmt.executeQuery();
		rs.next() ;
		InputStream stream = rs.getBinaryStream("ContentVersion") ;
		
		
		//rs.close() ;
		//pstmt.close() ;
		
		return stream ;
	}

}


上面的代码我也解释一下,取得传递的参数从数据库中取得详细信息组装xml文件。剩下的都是很普通的代码自己可以根据需求改一下就可以用了。


四、好走到这一步,数据库读取数据没问题的话就可以进行应用的下载安装了,但是在实际的测试中遇到下载速度相当的缓慢不知道什么原因。


五、在实际服务器端读取数据库中应用时,出现读取数据库(数据库使用的是sybase数据库)二进制文件32k的限制,后来在网上找到问题的解决方案。具体就是statement中设置set textsize 50000000,



/**
	 * 下载app
	 * 
	 * @param response
	 * @throws IOException
	 * @throws SQLException
	 */
	private void downloadApp(HttpServletResponse response,
			HttpServletRequest request) throws IOException, SQLException {
		DBManager db = new DBManager();
		Connection conn = db.createConn();

		String param = request.getParameter("action");
		// System.out.println(param);
		String packageId = this.getParameter(param, "PackageID");
		packageId = "{" + packageId + "}";

		String sql = "select * from A_PRFL_PACKAGE_CONTENT where PackageID='"
				+ packageId + "' and ContentTypeID=1";
		System.out.println(sql);
		Statement stmt = conn.createStatement();
		stmt.executeUpdate("set textsize 50000000");
		ResultSet rs = stmt.executeQuery(sql);
		if (!rs.next()) {
			return;
		}
		String appName = rs.getString("ContentFileName");
		InputStream inputStream = rs.getBinaryStream("ContentData");
		// conn.close();

		sendDownload(response, inputStream, appName);
	}



六、注意点plist中的ipa和图片地址不要使用https协议,可能导致应用无法下载。


Ok,,现在在ipad上测试点击应用的连接就可以进行应用的下载了大笑

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值