java接口下载zip,不生产中间文件,返回前端文件流
- 程序设计:
- 代码实现:
程序设计:
前端向后端请求zip文件,zip文件中有多个文件压缩而成,后端操作文件流,而不生成中间文件。最后把zip返回给前端。
代码实现:
@ApiOperation(value = "下载Zip", notes = "")@PostMapping("/getDownLoadZip")public void getDownLoadZip(@RequestBody GClientManagementVo vo, HttpServletRequest request,HttpServletResponse response) throws Exception {SysUserEntityVo uc = (SysUserEntityVo) request.getAttribute("UC");GClientManagementService.getDownLoadZip(vo, uc,response);}
中间有业务代码,可酌情删减。代码中注意关闭流,避免影响内存。
@Overridepublic void getDownLoadZip(GClientManagementVo vo, SysUserEntityVo uc, HttpServletResponse response) throws Exception {String filename = vo.getProjectName();String encodeFileName = URLEncoder.encode(filename);ServletOutputStream out = response.getOutputStream();// 创建一个ByteArrayOutputStream来存放最终的ZIP流ByteArrayOutputStream zipOutputStream = new ByteArrayOutputStream();ZipOutputStream zipOut = new ZipOutputStream(zipOutputStream);try {//写入文件createFilePs(vo, zipOut);//结束写入zipOut.finish();// 最终ZIP流的内容byte[] zipBytes = zipOutputStream.toByteArray();//设置允许跨域的keyresponse.setHeader("Access-Control-Expose-Headers", "Content-Disposition");//文件名有“,”等特殊字符发送到前端会报错,用""括起来解决response.addHeader("Content-Disposition", "attachment;filename=\"" + encodeFileName + "\"");//设置文件大小response.addHeader("Content-Length", "" + zipBytes.length);//设置文件名,避免问题,这个也用""括起来response.setHeader("filename,", "filename=\"" + encodeFileName + "\"");//设置文件类型response.setContentType("application/octet-stream");out.write(zipBytes);out.flush();} catch (Exception e) {throw e;} finally {try {out.close();} catch (Exception e) {throw e;}try {zipOutputStream.close();} catch (Exception e) {throw e;}try {zipOut.close();} catch (Exception e) {throw e;}try {out.close();} catch (Exception e) {throw e;}}}private void createFilePs(GClientManagementVo vo, ZipOutputStream zipOut) throws Exception {// 创建第一个文件install 或者uninstallByteArrayOutputStream file1 = new ByteArrayOutputStream();//install or uninstallString str1 = "";String fileName1 = "";QueryWrapper<GClientAdmin> queryWrapper = new QueryWrapper();if (vo.getFunction().equalsIgnoreCase(ParamsEnum.Install.getValue())) {queryWrapper.eq("sign", ParamsEnum.Install.getValue());} else {queryWrapper.eq("sign", ParamsEnum.Uninstall.getValue());}queryWrapper.last("limit 1");GClientAdmin gClientAdmin = gClientAdminDao.selectOne(queryWrapper);fileName1 = gClientAdmin.getScriptName();str1 = replaceScriptcode(gClientAdmin.getScriptCode(), vo);try {// 创建ByteArrayOutputStream来模拟文件流writeToFile(file1, str1);// 将每个文件流添加到ZIP流中addToZipFile(fileName1, file1.toByteArray(), zipOut);} catch (IOException ex) {ex.printStackTrace();throw ex;}//多个已经上传文件List<GClientManagementScriptVo> scriptList = vo.getScriptList();if (CollectionUtils.isNotEmpty(scriptList)) {scriptList.forEach(e -> {List<ParamsObject> paramslist = e.getParamslist();if (CollectionUtils.isNotEmpty(paramslist)) {paramslist.forEach(m -> {if (ParamsEnum.File.getValue().equalsIgnoreCase(m.getDataType())) {String str = m.getFileContent();String fileName = m.getFileName();//创建ByteArrayOutputStream来模拟文件流ByteArrayOutputStream file = new ByteArrayOutputStream();try {// 创建ByteArrayOutputStream来模拟文件流writeToFile(file, str);// 将每个文件流添加到ZIP流中addToZipFile(fileName, file.toByteArray(), zipOut);} catch (IOException ex) {ex.printStackTrace();}}});}});}}private String replaceScriptcode(String scriptcode, GClientManagementVo vo) throws Exception {//查找主参数的字符串String startParams = ParamsEnum.Params_Replace_Start.getValue();String endParams = ParamsEnum.Params_Replace_End.getValue();String resultParams = findSubstringBetween(scriptcode, startParams, endParams);if (resultParams != null) {//开始替换主参数String result = resultParams//projectName.replace(ParamsEnum.ProjectName.getValue(), "\"" + vo.getProjectName() + "\"")//applicationName.replace(ParamsEnum.MSIApplicationName.getValue(), "\"" + vo.getApplicationName() + "\"")//cmdRcopt.replace(ParamsEnum.CmdRCOpt.getValue(), vo.getCmdRcopt())//maxRunTime.replace(ParamsEnum.MaxRunTime.getValue(), vo.getMaxRunTime().toString())//startStopService.replace(ParamsEnum.StartStopService.getValue(), "\"" + vo.getStartStopService() + "\"")//debugEnable.replace(ParamsEnum.DebuggingEnabled.getValue(), "$" + vo.getDebugEnable())//defaultMsicLine.replace(ParamsEnum.DefaultMSICmdLine.getValue(), "\"" + vo.getDefaultMsicLine() + "\"")//uDriveMap.replace(ParamsEnum.UDriveMap.getValue(), "$" + vo.getuDriveMap())//function.replace(ParamsEnum.sFunction.getValue(), "\"" + vo.getFunction().toUpperCase(Locale.ROOT) + "\"")//ifsScript.replace(ParamsEnum.bIsIFSScript.getValue(), "$" + vo.getIfsScript());//替换主脚本字符串// 使用 replaceAll 方法和正则表达式来替换所有匹配的子字符串// 注意:这里使用了正则表达式,所以需要对特殊字符进行转义int startIndex = scriptcode.indexOf(startParams);if (startIndex != -1) {int endIndex = scriptcode.indexOf(endParams, startIndex);if (endIndex != -1) {scriptcode = scriptcode.substring(0, startIndex) + result + scriptcode.substring(endIndex + endParams.length());} else {throw new Exception("End tag not found.");}} else {throw new Exception("Start tag not found.");}//查找子参数的字符串String startCommand = ParamsEnum.Command_Replace_Start.getValue();String endCommand = ParamsEnum.Command_Replace_End.getValue();AtomicReference<String> codeStr = new AtomicReference<>("");if (CollectionUtils.isNotEmpty(vo.getScriptList())) {vo.getScriptList().forEach(e -> {AtomicReference<String> finalCode = new AtomicReference<>(e.getScriptCode());if (CollectionUtils.isNotEmpty(e.getParamslist())) {e.getParamslist().forEach(m -> {String value = "";String name = "";if (m.getDataType().equalsIgnoreCase(ParamsEnum.Boolean.getValue())) {value = "$" + m.getValue();name = "${" + m.getName() + "}";} else if (m.getDataType().equalsIgnoreCase(ParamsEnum.String.getValue())) {//CustomParameters特殊情况,全string参数if(m.getName().equalsIgnoreCase(ParamsEnum.CustomParameters.getValue())){if(StringUtils.startsWith(m.getValue().toString(),"\"")&&StringUtils.endsWith(m.getValue().toString(),"\"")){value = m.getValue().toString();}else {value = "\"" + m.getValue() + "\"";}}else {value = "\"" + m.getValue() + "\"";}name = "${" + m.getName() + "}";} else if (m.getDataType().equalsIgnoreCase(ParamsEnum.Int.getValue())) {value = m.getValue().toString();name = "${" + m.getName() + "}";} else if (m.getDataType().equalsIgnoreCase(ParamsEnum.File.getValue())) {value = "\"\\" + m.getFileName() + "\"";name = "${" + m.getName() + "}";}finalCode.set(finalCode.get().replace(name, value));});}codeStr.set(codeStr.get() + "\n" + finalCode.get());});}//子参数替换int startIndex2 = scriptcode.indexOf(startCommand);if (startIndex2 != -1) {int endIndex2 = scriptcode.indexOf(endCommand, startIndex2);if (endIndex2 != -1) {scriptcode = scriptcode.substring(0, startIndex2) + codeStr.get() + scriptcode.substring(endIndex2 + endCommand.length());} else {throw new Exception("End tag not found.");}} else {throw new Exception("Start tag not found.");}} else {throw new Exception("error");}return scriptcode;}public static String findSubstringBetween(String source, String start, String end) {// 找到开始字符串的位置int startIndex = source.indexOf(start);// 开始字符串不存在if (startIndex == -1) {return null;}// 在开始字符串之后找到结束字符串的位置int endIndex = source.indexOf(end, startIndex + start.length());// 结束字符串不存在if (endIndex == -1) {return null;}// 提取并返回子字符串return source.substring(startIndex + start.length(), endIndex);}private void writeToFile(ByteArrayOutputStream out, String content) throws IOException {try (OutputStreamWriter writer = new OutputStreamWriter(out, "UTF-8")) {writer.write(content);writer.flush();} finally {try {out.close();} catch (Exception e) {throw e;}}}private void addToZipFile(String fileName, byte[] fileContent, ZipOutputStream zipOut) throws IOException {ZipEntry zipEntry = new ZipEntry(fileName);zipOut.putNextEntry(zipEntry);zipOut.write(fileContent);zipOut.closeEntry();}