情景描述:
因產品需要一個能夠在客戶電腦桌面生成網站鏈接的快捷方式的功能,遂有了此功能,動態生成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)。