生成passbook

package cn.wanshe.utils; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.Date; import org.apache.tools.zip.ZipOutputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import cn.wanshe.common.GlobalSessionField; import cn.wanshe.help.entity.PassModel; import cn.wanshe.pojo.Event; import cn.wanshe.pojo.Tickets; /** * 生成passbook的工具类 * * @author gaozx */ public class PassUtils { private Tickets ticket; public PassUtils(Tickets ticket) { this.ticket = ticket; } private static Logger log = LoggerFactory.getLogger(PassUtils.class); /** * 创建passbook * * @param out * @author gaozx */ public boolean createPassbook(OutputStream out) { long startTime = System.currentTimeMillis(); PassModel pm = new PassModel(); Event event = ticket.getEvent(); pm.setName(event.getTitle()); pm.setSerialNum(new Date().getTime() + ""); pm.setTicketCode(ticket.getTicket_code()); log.debug("票id" + ticket.getId()); if (ticket.getTicketRepeat() == null && ticket.getTicketType() != null) { // 普通活动 pm.setStartTime(event.getStart_time()); } else if (ticket.getTicketRepeat() != null) { // 周期活动 pm.setStartTime(ticket.getTicketRepeat().getEventRepeat().getStart_time()); } else { // 都不满足,结束 log.error("createPassbook", "票未关联票类型"); return false; } if (event.getPlace() != null) { // 活动有地点 pm.setLatitude(event.getPlace().getLatitude()); pm.setLongitude(event.getPlace().getLongitude()); pm.setLocation(event.getPlace().getName()); } else { pm.setLatitude(39.953414); pm.setLongitude(116.298597); pm.setLocation("北京"); } String passJson = getPassJsonModel(pm); try { // passbook文件夹 String passFile = GlobalSessionField.PASS_BOOK_FILE_PATH + new Date().getTime() + StringUtils.randomNum(4) + "\\"; StringUtils.createFileFolder(passFile + "pass.json"); //pass.json FileWriter jsonFw = new FileWriter(new File(passFile + "pass.json")); jsonFw.write(passJson); jsonFw.flush(); jsonFw.close(); //manifest.json String manJson = getManifestJson(PassUtils.shaFiles(passFile + "pass.json")); FileWriter manFw = new FileWriter(new File(passFile + "manifest.json")); manFw.write(manJson); manFw.flush(); manFw.close(); signatureManJson(GlobalSessionField.PASS_CER_FILE_PATH, passFile, GlobalSessionField.PASS_PASSIN); PassUtils.zipFile(out, passFile, GlobalSessionField.PASS_BOOK_PIC_PATH); log.debug("createPassbook excute time:" + (System.currentTimeMillis() - startTime) + "ms"); return true; } catch (IOException e) { log.error("createPassbook", e); return false; } } /** * 获得字符的Unicode编码 * * @param str 待编码字符 * @return * @author gaozx */ public static String chinaToUnicode(String str) { StringBuffer result = new StringBuffer(); for (int i = 0; i < str.length(); i++) { int chr1 = (char) str.charAt(i); if (chr1 >= 19968 && chr1 <= 171941) {// 汉字范围 \u4e00-\u9fa5 (中文) result.append("\\u" + Integer.toHexString(chr1)); } else { result.append(str.charAt(i)); } } return result.toString(); } /** * 获取文件的sha1值 * * @param filePath 待处理文件的完整路径名 * @throws IOException * @author gaozx */ public static String shaFiles(String filePath) throws NullPointerException, IOException { String sha1 = null; // SHA1(g:\event.pkpass)= 43a9ccc756a787f82b35be368e40001f5055803f BufferedReader msgBr = null; BufferedReader errorMsgBr = null; StringBuffer data = new StringBuffer(); StringBuffer errorData = new StringBuffer(); try { String shell = "openssl sha1 " + filePath; Process process = Runtime.getRuntime().exec(shell); InputStream msg = process.getInputStream(); InputStream errorMsg = process.getErrorStream(); msgBr = new BufferedReader(new InputStreamReader(msg)); errorMsgBr = new BufferedReader(new InputStreamReader(errorMsg)); String buf = null; while ((buf = msgBr.readLine()) != null) { data.append(buf); } while ((buf = errorMsgBr.readLine()) != null) { errorData.append(buf); } // 如果错误信息非空,就抛出异常 if (errorData != null && errorData.length() > 0) { throw new NullPointerException(errorData.toString()); } } finally { if (msgBr != null) { msgBr.close(); } if (errorMsgBr != null) { errorMsgBr.close(); } } if ((errorData == null || errorData.length() == 0) && data != null && data.length() > 0) { if (data.indexOf("=") > 0) { String tempSha = data.toString().split("=")[1]; sha1 = tempSha.trim(); } } return sha1; } /** * 给passbook源文件签名 * * @param cerFilePath 证书根目录,(c:\\cer) * @param manifestFilePath 待签名文件根目录(c:\\pass) * @param passin 私钥密码 * @author gaozx * @throws IOException */ private void signatureManJson(String cerFilePath, String manifestFilePath, String passin) throws NullPointerException { String wwdr = cerFilePath + "WWDR.pem"; String passcertificate = cerFilePath + "passcertificate.pem"; String passkey = cerFilePath + "passkey.pem"; String manifest = manifestFilePath + "manifest.json"; String signature = manifestFilePath + "signature"; String shell = "openssl smime -binary -sign -certfile " + wwdr + " -signer " + passcertificate + " -inkey " + passkey + " -in " + manifest + " -out " + signature + " -outform DER -passin pass:" + passin; log.debug(shell); BufferedReader errorMsgBr = null; try { Process process = Runtime.getRuntime().exec(shell); InputStream errorMsg = process.getErrorStream(); errorMsgBr = new BufferedReader(new InputStreamReader(errorMsg)); StringBuffer errorData = new StringBuffer(); String buf = null; while ((buf = errorMsgBr.readLine()) != null) { errorData.append(buf); } // 如果错误信息非空,就抛出异常 if (errorData != null && errorData.length() > 0 && !errorData.toString().equals("Loading 'screen' into random state - done")) { log.error(errorData.toString()); // 添加异常信息 throw new NullPointerException(); } } catch (Exception e) { e.printStackTrace(); throw new NullPointerException("签名出现错误"); } finally { if (errorMsgBr != null) { try { errorMsgBr.close(); } catch (IOException e) { log.error("流关闭异常", e); } } } } /** * 压缩文件 outputFile 输出一个压缩文件 inputFileNames 输入文件夹 */ public static void zipFile(OutputStream os, String... inputFileNames) throws IOException { File[] files = new File[inputFileNames.length]; for (int i = 0; i < inputFileNames.length; i++) { files[i] = new File(inputFileNames[i]); } try { zip(os, files); } catch (IOException e) { log.error("zipFile压缩文件失败", e); throw new IOException(e); } } private static void zip(OutputStream os, File... inputFiles) throws IOException { ZipOutputStream out = new ZipOutputStream(os); for (File inputFile : inputFiles) { zip(out, inputFile, ""); } System.out.println("zip done"); try { if(out != null) { out.close(); } } catch(IOException e) { log.error("压缩输出流关闭失败", e); throw new IOException(e); } } private static void zip(ZipOutputStream out, File f, String base) throws IOException { if (f.isDirectory()) { File[] fl = f.listFiles(); out.putNextEntry(new org.apache.tools.zip.ZipEntry(base + "/")); base = base.length() == 0 ? "" : base + "/"; for (int i = 0; i < fl.length; i++) { zip(out, fl[i], base + fl[i].getName()); } } else { out.putNextEntry(new org.apache.tools.zip.ZipEntry(base)); FileInputStream in = null; try { in = new FileInputStream(f); int b; log.debug("压缩文件"+base); while ((b = in.read()) != -1) { out.write(b); } } catch (IOException e) { log.error("读取文件错误", e); throw new IOException(e); } finally { in.close(); } } } /** * 获取pass.json模板 */ private String getPassJsonModel(PassModel pm) { StringBuffer model = new StringBuffer(); model.append("{"); model.append("\"formatVersion\": "); if (pm.getFormatVersion() != null) { model.append(pm.getFormatVersion() + ","); } else { model.append("1,"); } model.append("\"serialNumber\": "); if (pm.getSerialNum() != null) { model.append("\"" + pm.getSerialNum() + "\","); } else { model.append("\"" + new Date().getTime() + "\","); } model.append("\"passTypeIdentifier\": \"pass.com.vasee.test\","); model.append("\"teamIdentifier\": \"N95AXC2584\","); model.append("\"organizationName\": \"Wanshe.cn\","); model.append("\"description\": \"Wanshe Event Ticket\","); model.append("\"logoText\": \"\u4e07\u793e\u6d3b\u52a8\u7f51\","); model.append("\"foregroundColor\": \"rgb(255, 255, 255)\","); model.append("\"backgroundColor\": \"rgb(135, 129, 189)\","); model.append("\"labelColor\": \"rgb(45, 54, 129)\","); model.append("\"barcode\": "); if (pm.getTicketCode() != null) { model.append("{"); model.append("\"message\": \"" + pm.getTicketCode() + "\","); model.append("\"format\": \"PKBarcodeFormatQR\","); model.append("\"messageEncoding\": \"iso-8859-1\""); model.append("},"); } else { return null; } SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm"); SimpleDateFormat zSdf = new SimpleDateFormat("Z"); String startTime = null; if (pm.getStartTime() != null) { model.append("\"relevantDate\": "); // 将时区改格式为+08:00 StringBuffer timeZone = new StringBuffer(zSdf.format(pm.getStartTime())); timeZone.insert(3, ":"); startTime = sdf.format(pm.getStartTime()); startTime += timeZone.toString(); model.append("\"" + startTime + "\","); } else { return null; } // 地点名称 String unicodeLocation = PassUtils.chinaToUnicode("北京"); // 经纬度 if (pm.getLocation() != null) { model.append("\"locations\": [{"); model.append("\"longitude\": " + pm.getLongitude() + ","); model.append("\"latitude\": " + pm.getLatitude() + ","); model.append("\"altitude\": " + pm.getAltitude() + ","); unicodeLocation = PassUtils.chinaToUnicode(pm.getLocation()); model.append("\"relevantText\": \"" + unicodeLocation + "\""); model.append("}],"); } else { model.append("\"locations\": [{"); model.append("\"longitude\": " + 116.298597 + ","); model.append("\"latitude\": " + 39.953414 + ","); model.append("\"altitude\": " + 0 + ","); model.append("\"relevantText\": \"" + unicodeLocation + "\""); model.append("}],"); } // 地点 model.append("\"eventTicket\": {\"primaryFields\": [{"); model.append("\"key\": \"event\","); model.append("\"label\": \"\u6d3b\u52a8\","); String eventName = PassUtils.chinaToUnicode(pm.getName()); model.append("\"value\": " + "\"" + eventName + "\""); model.append("}],"); model.append("\"secondaryFields\": [{"); model.append("\"key\": \"loc\","); model.append("\"label\": \"\u5730\u70b9\","); model.append("\"value\": \"" + unicodeLocation + "\"}],"); //时间 票类型 model.append("\"auxiliaryFields\":[{"); model.append("\"key\": \"time&ticket\","); String timeTicket = PassUtils.chinaToUnicode("票/时间"); model.append("\"label\": \""+timeTicket+"\","); String ticketName = ticket.getName(); if(ticket.getSeatModel() != null) { ticketName += " " + ticket.getSeatModel().getSort(); } ticketName += " " + StringUtils.getDateString(pm.getStartTime(), "yyyy-MM-dd HH:mm"); model.append("\"value\": \"" + PassUtils.chinaToUnicode(ticketName) + "\"}]}"); model.append("}"); return model.toString(); } /** * 获取manifest.json模板 * * @param passSha pass.json文件的加密值 * @return */ private String getManifestJson(String passSha) { if (passSha == null) { return null; } StringBuffer manJson = new StringBuffer(); manJson.append("{"); manJson.append("\"pass.json\": \"" + passSha + "\","); manJson.append("\"icon.png\": \"e0f0bcd503f6117bce6a1a3ff8a68e36d26ae47f\","); manJson.append("\"icon@2x.png\": \"784a9bb170809c607ecc73a6ab183a1e65bf82a0\","); manJson.append("\"logo.png\": \"abc97e3b2bc3b0e412ca4a853ba5fd90fe063551\","); manJson.append("\"logo@2x.png\": \"5d1ea85567258fbae37caca20b3232e8069d8dcf\","); manJson.append("\"thumbnail.png\": \"dabfbcc61890ef2aa02943c49859a4ba8465a00e\","); manJson.append("\"thumbnail@2x.png\": \"0d5f7f8fec8cf1e45e5bda4542023d394dcc4e6f\","); manJson.append("\"background.png\": \"dabfbcc61890ef2aa02943c49859a4ba8465a00e\","); manJson.append("\"background@2x.png\": \"0d5f7f8fec8cf1e45e5bda4542023d394dcc4e6f\""); manJson.append("}"); return manJson.toString(); } }

方法仅提供生成的方法,需要自行安装openssl。
效果图如下,因为无IOS设备,遂在android下装了passwallet以查看,兼容问题不可避免。
正面效果:

背面效果:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值