refactor(性能测试): 性能测试下载zip使用流下载
This commit is contained in:
parent
325ae75bf3
commit
2065cd93d3
|
@ -1,5 +1,6 @@
|
||||||
package io.metersphere.engine;
|
package io.metersphere.engine;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -14,7 +15,7 @@ public class EngineContext {
|
||||||
private Integer resourceIndex;
|
private Integer resourceIndex;
|
||||||
private double[] ratios;
|
private double[] ratios;
|
||||||
private Map<String, Object> properties = new HashMap<>();
|
private Map<String, Object> properties = new HashMap<>();
|
||||||
private Map<String, byte[]> testResourceFiles = new HashMap<>();
|
private Map<String, InputStream> testResourceFiles = new HashMap<>();
|
||||||
private Map<String, Boolean> splitFlag = new HashMap<>();
|
private Map<String, Boolean> splitFlag = new HashMap<>();
|
||||||
private boolean checkBackendListener;
|
private boolean checkBackendListener;
|
||||||
|
|
||||||
|
@ -103,11 +104,11 @@ public class EngineContext {
|
||||||
this.ratios = ratios;
|
this.ratios = ratios;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, byte[]> getTestResourceFiles() {
|
public Map<String, InputStream> getTestResourceFiles() {
|
||||||
return testResourceFiles;
|
return testResourceFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTestResourceFiles(Map<String, byte[]> testResourceFiles) {
|
public void setTestResourceFiles(Map<String, InputStream> testResourceFiles) {
|
||||||
this.testResourceFiles = testResourceFiles;
|
this.testResourceFiles = testResourceFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,17 +3,14 @@ package io.metersphere.controller;
|
||||||
|
|
||||||
import io.metersphere.commons.utils.WeakConcurrentHashMap;
|
import io.metersphere.commons.utils.WeakConcurrentHashMap;
|
||||||
import io.metersphere.controller.handler.annotation.NoResultHolder;
|
import io.metersphere.controller.handler.annotation.NoResultHolder;
|
||||||
import io.metersphere.dto.ZipDTO;
|
|
||||||
import io.metersphere.service.JmeterFileService;
|
import io.metersphere.service.JmeterFileService;
|
||||||
import org.springframework.http.HttpHeaders;
|
import jakarta.annotation.Resource;
|
||||||
import org.springframework.http.MediaType;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -45,15 +42,11 @@ public class JmeterFileController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("download")
|
@GetMapping("download")
|
||||||
public ResponseEntity<byte[]> downloadJmeterFiles(@RequestParam("ratio") String ratio,
|
public void downloadJmeterFiles(@RequestParam("ratio") String ratio,
|
||||||
@RequestParam("reportId") String reportId,
|
@RequestParam("reportId") String reportId,
|
||||||
@RequestParam("resourceIndex") int resourceIndex) {
|
@RequestParam("resourceIndex") int resourceIndex, HttpServletResponse response) {
|
||||||
double[] ratios = Arrays.stream(ratio.split(",")).mapToDouble(Double::parseDouble).toArray();
|
double[] ratios = Arrays.stream(ratio.split(",")).mapToDouble(Double::parseDouble).toArray();
|
||||||
ZipDTO zipDTO = jmeterFileService.downloadZip(reportId, ratios, resourceIndex);
|
jmeterFileService.downloadZip(reportId, ratios, resourceIndex, response);
|
||||||
return ResponseEntity.ok()
|
|
||||||
.contentType(MediaType.parseMediaType("application/octet-stream"))
|
|
||||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + zipDTO.getTestId() + ".zip\"")
|
|
||||||
.body(zipDTO.getContent());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,16 +172,16 @@ public class EngineFactory {
|
||||||
/*
|
/*
|
||||||
{"timeout":10,"statusCode":["302","301"],"params":[{"name":"param1","enable":true,"value":"0","edit":false}],"domains":[{"domain":"baidu.com","enable":true,"ip":"127.0.0.1","edit":false}]}
|
{"timeout":10,"statusCode":["302","301"],"params":[{"name":"param1","enable":true,"value":"0","edit":false}],"domains":[{"domain":"baidu.com","enable":true,"ip":"127.0.0.1","edit":false}]}
|
||||||
*/
|
*/
|
||||||
Map<String, byte[]> testResourceFiles = new HashMap<>();
|
Map<String, InputStream> testResourceFiles = new HashMap<>();
|
||||||
byte[] props = getJMeterProperties(loadTestReport, engineContext);
|
byte[] props = getJMeterProperties(loadTestReport, engineContext);
|
||||||
byte[] sysProps = getSystemProperties(loadTestReport, engineContext);
|
byte[] sysProps = getSystemProperties(loadTestReport, engineContext);
|
||||||
byte[] hosts = getDNSConfig(loadTestReport, engineContext);
|
byte[] hosts = getDNSConfig(loadTestReport, engineContext);
|
||||||
// JMeter Properties
|
// JMeter Properties
|
||||||
testResourceFiles.put("ms.properties", props);
|
testResourceFiles.put("ms.properties", new ByteArrayInputStream(props));
|
||||||
// System Properties
|
// System Properties
|
||||||
testResourceFiles.put("sys.properties", sysProps);
|
testResourceFiles.put("sys.properties", new ByteArrayInputStream(sysProps));
|
||||||
// DNS
|
// DNS
|
||||||
testResourceFiles.put("hosts", hosts);
|
testResourceFiles.put("hosts", new ByteArrayInputStream(hosts));
|
||||||
|
|
||||||
final EngineSourceParser engineSourceParser = EngineSourceParserFactory.createEngineSourceParser(engineContext.getFileType());
|
final EngineSourceParser engineSourceParser = EngineSourceParserFactory.createEngineSourceParser(engineContext.getFileType());
|
||||||
|
|
||||||
|
@ -191,8 +191,8 @@ public class EngineFactory {
|
||||||
|
|
||||||
if (CollectionUtils.isNotEmpty(resourceFiles)) {
|
if (CollectionUtils.isNotEmpty(resourceFiles)) {
|
||||||
resourceFiles.forEach(cf -> {
|
resourceFiles.forEach(cf -> {
|
||||||
byte[] csvContent = fileMetadataService.loadFileAsBytes(cf.getId());
|
InputStream in = fileMetadataService.getFileAsStream(cf.getId());
|
||||||
testResourceFiles.put(cf.getName(), csvContent);
|
testResourceFiles.put(cf.getName(), in);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
engineContext.setTestResourceFiles(testResourceFiles);
|
engineContext.setTestResourceFiles(testResourceFiles);
|
||||||
|
|
|
@ -3,6 +3,7 @@ package io.metersphere.parse.xml.reader;
|
||||||
import io.metersphere.base.domain.TestResourcePool;
|
import io.metersphere.base.domain.TestResourcePool;
|
||||||
import io.metersphere.commons.exception.MSException;
|
import io.metersphere.commons.exception.MSException;
|
||||||
import io.metersphere.commons.utils.CommonBeanFactory;
|
import io.metersphere.commons.utils.CommonBeanFactory;
|
||||||
|
import io.metersphere.commons.utils.IOUtils;
|
||||||
import io.metersphere.config.KafkaProperties;
|
import io.metersphere.config.KafkaProperties;
|
||||||
import io.metersphere.engine.EngineContext;
|
import io.metersphere.engine.EngineContext;
|
||||||
import io.metersphere.i18n.Translator;
|
import io.metersphere.i18n.Translator;
|
||||||
|
@ -18,6 +19,8 @@ import org.dom4j.Element;
|
||||||
import org.dom4j.Node;
|
import org.dom4j.Node;
|
||||||
import org.dom4j.tree.BaseElement;
|
import org.dom4j.tree.BaseElement;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -75,7 +78,7 @@ public class JmeterDocumentParser implements EngineSourceParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void parseHashTree(Element hashTree) {
|
private void parseHashTree(Element hashTree) throws IOException {
|
||||||
if (invalid(hashTree)) {
|
if (invalid(hashTree)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -269,7 +272,7 @@ public class JmeterDocumentParser implements EngineSourceParser {
|
||||||
item.setText(filename);
|
item.setText(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processCsvDataSet(Element element) {
|
private void processCsvDataSet(Element element) throws IOException {
|
||||||
List<Element> childNodes = element.elements();
|
List<Element> childNodes = element.elements();
|
||||||
String fileName = null;
|
String fileName = null;
|
||||||
for (Element item : childNodes) {
|
for (Element item : childNodes) {
|
||||||
|
@ -366,7 +369,7 @@ public class JmeterDocumentParser implements EngineSourceParser {
|
||||||
item.setText(String.valueOf(BooleanUtils.toBoolean(recycle)));
|
item.setText(String.valueOf(BooleanUtils.toBoolean(recycle)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private void splitCsvFile(Node item) {
|
private void splitCsvFile(Node item) throws IOException {
|
||||||
String filename = item.getText();
|
String filename = item.getText();
|
||||||
filename = StringUtils.removeStart(filename, "/test/");
|
filename = StringUtils.removeStart(filename, "/test/");
|
||||||
// 已经分割过的不再二次分割
|
// 已经分割过的不再二次分割
|
||||||
|
@ -379,11 +382,11 @@ public class JmeterDocumentParser implements EngineSourceParser {
|
||||||
}
|
}
|
||||||
double[] ratios = context.getRatios();
|
double[] ratios = context.getRatios();
|
||||||
int resourceIndex = context.getResourceIndex();
|
int resourceIndex = context.getResourceIndex();
|
||||||
byte[] content = context.getTestResourceFiles().get(filename);
|
InputStream content = context.getTestResourceFiles().get(filename);
|
||||||
if (content == null) {
|
if (content == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
StringTokenizer tokenizer = new StringTokenizer(new String(content), StringUtils.LF);
|
StringTokenizer tokenizer = new StringTokenizer(IOUtils.toString(content, StandardCharsets.UTF_8), StringUtils.LF);
|
||||||
if (!tokenizer.hasMoreTokens()) {
|
if (!tokenizer.hasMoreTokens()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -436,7 +439,7 @@ public class JmeterDocumentParser implements EngineSourceParser {
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
// 替换文件
|
// 替换文件
|
||||||
context.getTestResourceFiles().put(filename, csv.toString().getBytes(StandardCharsets.UTF_8));
|
context.getTestResourceFiles().put(filename, new ByteArrayInputStream(csv.toString().getBytes(StandardCharsets.UTF_8)));
|
||||||
context.getSplitFlag().put(filename, true);
|
context.getSplitFlag().put(filename, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,15 +5,16 @@ import io.metersphere.base.mapper.LoadTestReportMapper;
|
||||||
import io.metersphere.base.mapper.ext.ExtLoadTestReportMapper;
|
import io.metersphere.base.mapper.ext.ExtLoadTestReportMapper;
|
||||||
import io.metersphere.commons.exception.MSException;
|
import io.metersphere.commons.exception.MSException;
|
||||||
import io.metersphere.commons.utils.LogUtil;
|
import io.metersphere.commons.utils.LogUtil;
|
||||||
import io.metersphere.dto.ZipDTO;
|
|
||||||
import io.metersphere.engine.EngineContext;
|
import io.metersphere.engine.EngineContext;
|
||||||
import io.metersphere.engine.EngineFactory;
|
import io.metersphere.engine.EngineFactory;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.apache.commons.collections4.MapUtils;
|
import org.apache.commons.collections4.MapUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
@ -29,7 +30,7 @@ public class JmeterFileService {
|
||||||
@Resource
|
@Resource
|
||||||
private LoadTestReportMapper loadTestReportMapper;
|
private LoadTestReportMapper loadTestReportMapper;
|
||||||
|
|
||||||
public ZipDTO downloadZip(String reportId, double[] ratios, int resourceIndex) {
|
public void downloadZip(String reportId, double[] ratios, int resourceIndex, HttpServletResponse response) {
|
||||||
try {
|
try {
|
||||||
LoadTestReportWithBLOBs loadTestReport = loadTestReportMapper.selectByPrimaryKey(reportId);
|
LoadTestReportWithBLOBs loadTestReport = loadTestReportMapper.selectByPrimaryKey(reportId);
|
||||||
int wait = 0;
|
int wait = 0;
|
||||||
|
@ -45,52 +46,47 @@ public class JmeterFileService {
|
||||||
MSException.throwException("测试报告不存在或还没产生");
|
MSException.throwException("测试报告不存在或还没产生");
|
||||||
}
|
}
|
||||||
EngineContext context = EngineFactory.createContext(loadTestReport, ratios, resourceIndex);
|
EngineContext context = EngineFactory.createContext(loadTestReport, ratios, resourceIndex);
|
||||||
byte[] bytes = zipFilesToByteArray(context);
|
zipFilesToByteArray(context, response);
|
||||||
return new ZipDTO(loadTestReport.getTestId(), bytes);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LogUtil.error(e.getMessage(), e);
|
LogUtil.error(e.getMessage(), e);
|
||||||
MSException.throwException(e);
|
MSException.throwException(e);
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] zipFilesToByteArray(EngineContext context) throws IOException {
|
private void zipFilesToByteArray(EngineContext context, HttpServletResponse response) throws IOException {
|
||||||
String testId = context.getTestId();
|
String testId = context.getTestId();
|
||||||
String fileName = testId + ".jmx";
|
String fileName = testId + ".jmx";
|
||||||
|
|
||||||
Map<String, byte[]> files = new HashMap<>();
|
|
||||||
|
|
||||||
// 每个测试生成一个文件夹
|
// 每个测试生成一个文件夹
|
||||||
files.put(fileName, context.getContent());
|
|
||||||
// 保存jmx
|
// 保存jmx
|
||||||
LoadTestReportWithBLOBs record = new LoadTestReportWithBLOBs();
|
LoadTestReportWithBLOBs record = new LoadTestReportWithBLOBs();
|
||||||
record.setId(context.getReportId());
|
record.setId(context.getReportId());
|
||||||
record.setJmxContent(new String(context.getContent()));
|
record.setJmxContent(new String(context.getContent()));
|
||||||
extLoadTestReportMapper.updateJmxContentIfAbsent(record);
|
extLoadTestReportMapper.updateJmxContentIfAbsent(record);
|
||||||
// 保存 byte[]
|
// 关联文件
|
||||||
Map<String, byte[]> jarFiles = context.getTestResourceFiles();
|
Map<String, InputStream> testResourceFiles = context.getTestResourceFiles();
|
||||||
if (!MapUtils.isEmpty(jarFiles)) {
|
try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
|
||||||
for (String k : jarFiles.keySet()) {
|
// jmx 本身
|
||||||
byte[] v = jarFiles.get(k);
|
zos.putNextEntry(new ZipEntry(fileName));
|
||||||
files.put(k, v);
|
zos.write(context.getContent());
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return listBytesToZip(files);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private byte[] listBytesToZip(Map<String, byte[]> mapReport) throws IOException {
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
||||||
ZipOutputStream zos = new ZipOutputStream(baos);
|
|
||||||
for (Map.Entry<String, byte[]> report : mapReport.entrySet()) {
|
|
||||||
ZipEntry entry = new ZipEntry(report.getKey());
|
|
||||||
entry.setSize(report.getValue().length);
|
|
||||||
zos.putNextEntry(entry);
|
|
||||||
zos.write(report.getValue());
|
|
||||||
}
|
|
||||||
zos.closeEntry();
|
zos.closeEntry();
|
||||||
zos.close();
|
// 关联文件
|
||||||
return baos.toByteArray();
|
if (!MapUtils.isEmpty(testResourceFiles)) {
|
||||||
|
for (String name : testResourceFiles.keySet()) {
|
||||||
|
InputStream in = testResourceFiles.get(name);
|
||||||
|
zos.putNextEntry(new ZipEntry(name));
|
||||||
|
byte[] bytes = new byte[4096];
|
||||||
|
int len;
|
||||||
|
while ((len = in.read(bytes)) != -1) {
|
||||||
|
zos.write(bytes, 0, len);
|
||||||
|
}
|
||||||
|
in.close();
|
||||||
|
zos.closeEntry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response.setHeader("Content-Disposition", "attachment;filename=" + testId + ".zip");
|
||||||
|
response.flushBuffer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue