java实现生成windows可执行的批处理文件(.bat)
情景描述:
因产品需要一个能够在客户电脑桌面生成网站链接的快捷方式的功能,遂有了此功能,动态生成windows批处理文件,用户执行后可生成桌面快捷方式,打开即可登录到网站实行功能操作,此功能下载要求是一个压缩包(压缩包包括,客户的logo,使用说明等文件),压缩功能前文已有可自行查看。
其实动态生成文件功能并不复杂,但是动态生成的windows可执行的批处理文件就有一点坑(当然也是我没有考虑到)。我们都知道java一般是运行于服务器即linux系统上的,而我们开发一般所用的编码规范都是UTF-8,所以此时在linux上生成的文件就无法再windows上执行(当然可以打开只是批处理文件无法运行),windows无法识别,所以就有了此次记录。直接上代码。
顺带给大家科普一下linux和windows及mac的区别:
系统 | 换行符 |
windows | CRLF \r\n |
mac | CR \r |
unix | LF \n |
下载方法:
/** * 下载压缩包 * * @param id 商户id * @param request * @param response * @return void * @author chen.bing * @Date 2019/11/4 17:35 */ @RequestMapping(value = "downloadzip") public void downloadzip(String id, HttpServletRequest request, HttpServletResponse response) { if (StringUtils.isBlank(id)) { LOG.error("商户id为空"); return; } String rootZipUrl = Global.getProperty("VOUCHER_IMAGE_DIR"); File rootFile = new File(rootZipUrl); if (!rootFile.exists()) { LOG.error("当前文件夹不存在", rootZipUrl); return; } String zipUrl = rootZipUrl; SysMerch sysMerch = sysMerchService.selectById(id); if (sysMerch != null) { String scheme = request.getScheme(); String contextPath = request.getContextPath(); String serverName = request.getServerName(); int port = request.getServerPort(); String basePath = scheme + "://" + serverName + ":" + port + contextPath; String orgCode = sysMerch.getOrgCode(); String merchNo = sysMerch.getMerchNo(); String merchName = sysMerch.getMerchName(); String merchNameDir = rootZipUrl + File.separator + merchName; String oldZipUrl = rootZipUrl + File.separator + "downloadZip"; boolean b = FileUtils.fileNameRenameTo(oldZipUrl,merchNameDir); zipUrl = oldZipUrl; if (b) { zipUrl = merchNameDir; } String batUrl = zipUrl + File.separator + "安装包" + File.separator + "setup.txt"; File file = new File(batUrl); if (!file.exists()) { File parentFile = file.getParentFile(); if (!parentFile.exists()) { parentFile.mkdirs(); } // 不存在创建文件和其父目录 FileUtils.createFile(batUrl); } setUpMerchLogo(orgCode, merchNo, merchName, request); String merchLogoBat = "set wind = ws.createshortcut(strDesktop & \"\\%s.lnk\")"; String url = "wind.Arguments=\"%s\""; String loginUrl = basePath + "/login?org_code_ck_key=" + orgCode + "&merch_no_ck_key=" + merchNo; String logoBat = String.format(merchLogoBat, merchName); String merchUrl = String.format(url, loginUrl); List<String> list = new LinkedList<>(); list.add("@echo off"); list.add("more %0 +5>%temp%yst.vbs"); list.add("%temp%yst.vbs"); list.add("del %temp%yst.vbs"); list.add("exit"); list.add("set ws = createobject(\"wscript.shell\")"); list.add("strDesktop = ws.SpecialFolders(\"Desktop\")"); list.add(logoBat); list.add("wind.IconLocation = ws.CurrentDirectory & \"\\favicon.ico\""); list.add("wind.targetpath = \"%ProgramFiles%\\Internet Explorer\\IEXPLORE.EXE\""); list.add("wind.workingdirectory = \"%ProgramFiles%\\Internet Explorer\""); list.add(merchUrl); list.add("wind.save"); OutputStream outputStream = null; try { // 加锁,防止在压缩时有图片写入 synchronized (ZIP_LOCK) { outputStream = new FileOutputStream(file); // 此处循环操作是因为要将.bat文件转换为windows格式 for (int i = 0; i < list.size(); i++) { byte[] bytes = list.get(i).getBytes(Charset.forName("GB2312")); outputStream.write(bytes); if (i < list.size()) { outputStream.write("\r\n".getBytes(Charset.forName("GB2312"))); } } outputStream.close(); String batPath = zipUrl + File.separator + "安装包" + File.separator + "setup.bat"; File batFile = new File(batPath); batFile.delete(); FileUtils.fileNameRenameTo(batUrl,batPath); FileUtils.downloadZip(response, rootZipUrl, merchName); FileUtils.fileNameRenameTo(merchNameDir, oldZipUrl); file.delete(); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
看了这段代码可能会有人觉得性能不够好,循环写入文件内容太繁琐耗时,首先我此处的批处理命令只有十几行,内容不多,并不算耗时,其次,如果用字符串+“\r\n”的方式写入完全不起作用,下载下来之后依然是unix的格式,所以需要在写入一行内容之后再直接对文件写入 \r\n ,让其达到windows的换行效果,另外此处代码使用的编码是GB2312是一种windows可用的编码,这样设置之后下载下来的.bat文件就可以在windows上直接打开执行(只支持windows不支持mac)。