diff --git a/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java b/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java index f0476d001f..f8c9895250 100644 --- a/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java +++ b/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java @@ -320,13 +320,6 @@ public class ApiAutomationController { return apiAutomationService.export(request); } - @PostMapping(value = "/export/jmx") - @RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_READ_EXPORT_SCENARIO) - @MsAuditLog(module = "api_automation", type = OperLogConstants.EXPORT, sourceId = "#request.id", title = "#request.name", project = "#request.projectId") - public List exportJmx(@RequestBody ApiScenarioBatchRequest request) { - return apiAutomationService.exportJmx(request); - } - @GetMapping(value = "/stop/{reportId}") public void stop(@PathVariable String reportId) { new LocalRunner().stop(reportId); @@ -342,5 +335,15 @@ public class ApiAutomationController { return apiAutomationService.setDomain(request.getDefinition()); } + @PostMapping(value = "/export/jmx") + @RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_READ_EXPORT_SCENARIO) + @MsAuditLog(module = "api_automation", type = OperLogConstants.EXPORT, sourceId = "#request.id", title = "#request.name", project = "#request.projectId") + public ResponseEntity downloadBodyFiles(@RequestBody ApiScenarioBatchRequest request) { + byte[] bytes = apiAutomationService.exportZip(request); + return ResponseEntity.ok() + .contentType(MediaType.parseMediaType("application/octet-stream")) + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + "场景JMX文件集.zip") + .body(bytes); + } } diff --git a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java index 6aaf5ef6c4..7f13547fb6 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java @@ -68,6 +68,7 @@ import javax.annotation.Resource; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; +import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -2018,6 +2019,30 @@ public class ApiAutomationService { return resList; } + public byte[] exportZip(ApiScenarioBatchRequest request) { + List apiScenarioWithBLOBs = getExportResult(request); + // 生成jmx + Map files = new LinkedHashMap<>(); + apiScenarioWithBLOBs.forEach(item -> { + if (StringUtils.isNotEmpty(item.getScenarioDefinition())) { + String jmx = generateJmx(item); + if (StringUtils.isNotEmpty(jmx)) { + ApiScenrioExportJmx scenrioExportJmx = new ApiScenrioExportJmx(item.getName(), apiTestService.updateJmxString(jmx, null, true).getXml()); + String fileName = item.getName() + ".jmx"; + String jmxStr = scenrioExportJmx.getJmx(); + files.put(fileName, jmxStr.getBytes(StandardCharsets.UTF_8)); + } + } + }); + if (CollectionUtils.isNotEmpty(apiScenarioWithBLOBs)) { + List names = apiScenarioWithBLOBs.stream().map(ApiScenarioWithBLOBs::getName).collect(Collectors.toList()); + request.setName(String.join(",", names)); + List ids = apiScenarioWithBLOBs.stream().map(ApiScenarioWithBLOBs::getId).collect(Collectors.toList()); + request.setId(JSON.toJSONString(ids)); + } + return FileUtils.listBytesToZip(files); + } + public void batchUpdateEnv(ApiScenarioBatchRequest request) { Map envMap = request.getEnvMap(); Map> mapping = request.getMapping(); diff --git a/backend/src/main/java/io/metersphere/commons/utils/FileUtils.java b/backend/src/main/java/io/metersphere/commons/utils/FileUtils.java index c0d1e52b6a..765e431e3d 100644 --- a/backend/src/main/java/io/metersphere/commons/utils/FileUtils.java +++ b/backend/src/main/java/io/metersphere/commons/utils/FileUtils.java @@ -17,15 +17,35 @@ import org.springframework.core.io.FileSystemResource; import org.springframework.web.multipart.MultipartFile; import java.io.*; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.UUID; +import java.util.*; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; public class FileUtils { public static final String BODY_FILE_DIR = "/opt/metersphere/data/body"; public static final String MD_IMAGE_DIR = "/opt/metersphere/data/image/markdown"; + public static byte[] listBytesToZip(Map mapReport) { + try { + if (!mapReport.isEmpty()) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ZipOutputStream zos = new ZipOutputStream(baos); + for (Map.Entry report : mapReport.entrySet()) { + ZipEntry entry = new ZipEntry(report.getKey()); + entry.setSize(report.getValue().length); + zos.putNextEntry(entry); + zos.write(report.getValue()); + } + zos.closeEntry(); + zos.close(); + return baos.toByteArray(); + } + } catch (Exception e) { + return new byte[10]; + } + return new byte[10]; + } + private static void create(List bodyUploadIds, List bodyFiles, String path) { String filePath = BODY_FILE_DIR; if (StringUtils.isNotEmpty(path)) { diff --git a/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue b/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue index 08780219fb..92b550de7d 100644 --- a/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue +++ b/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue @@ -252,6 +252,7 @@ import MsTableColumn from "@/business/components/common/components/table/MsTable import HeaderLabelOperate from "@/business/components/common/head/HeaderLabelOperate"; import {editApiScenarioCaseOrder} from "@/business/components/api/automation/api-automation"; import {TYPE_TO_C} from "@/business/components/api/automation/scenario/Setting"; +import axios from "axios"; export default { name: "MsApiScenarioList", @@ -1022,6 +1023,17 @@ export default { } }); }, + fileDownload(url, param) { + axios.post(url, param, {responseType: 'blob'}) + .then(response => { + let link = document.createElement("a"); + link.href = window.URL.createObjectURL(new Blob([response.data], {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8"})); + link.download = "场景JMX文件集.zip"; + this.result.loading = false; + link.click(); + }); + }, + exportJmx() { let param = {}; this.buildBatchParam(param); @@ -1030,15 +1042,7 @@ export default { return; } this.result.loading = true; - this.result = this.$post("/api/automation/export/jmx", param, response => { - this.result.loading = false; - let obj = response.data; - if (obj && obj.length > 0) { - obj.forEach(item => { - downloadFile(item.name + ".jmx", item.jmx); - }); - } - }); + this.fileDownload("/api/automation/export/jmx", param); }, getConditions() { return this.condition;