导出的图片在页面中已经初始化好了,需要拿到图片的base64数据编码传到后端
Base64 是一种将二进制数据编码为可打印 ASCII 字符的方式,常用于在文本协议(如 HTTP、JSON)中安全传输二进制数据(如图片、文件)。以下是关于图片 Base64 编码的详细解释:
1. Base64 的核心原理
2. Base64 编码图片的优缺点
优点 | 缺点 |
---|---|
✅ 可直接嵌入 HTML/CSS/JSON,无需额外文件请求 | ❌ 体积比原始二进制大 33%(每 3 字节变 4 字符) |
✅ 避免跨域问题(CORS) | ❌ 不适合大文件(如高清图片) |
✅ 简化数据传输(如 API 接口直接传字符串) | ❌ 解码需要额外计算资源 |
- 二进制转文本:计算机中的图片本质是二进制数据(0 和 1 的组合),但直接传输二进制可能因字符集、协议限制导致错误。Base64 将二进制按规则映射到 64 个可打印字符(
A-Za-z0-9+/
),确保数据安全传输。 -
. 图片 Base64 的常见形式
Base64 编码的图片通常以 Data URL 形式出现,例如:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA...(省略后续字符)
- 前缀:
data:image/png;base64,
表示数据类型和编码方式。 - 数据部分:
iVBORw0KGgo...
是图片的 Base64 编码内容。 echarts.init
方法用于初始化图表,传入的参数chartDom
是图表容器的 DOM 元素。- 返回值
myChart
是一个 ECharts 实例对象,后续可用于设置图表选项、更新数据等操作。 - 调用
myChart.getDataURL
方法生成图表的数据URL。 - 设置导出图片的类型为
png
,像素比为2
,以提高清晰度。 - 排除
toolbox
组件,确保导出图片不包含工具箱。 - 设置背景颜色为
#404040
,保证导出图片的背景颜色一致。
在当前导出按钮,通过
导出的url就是图片的base64位编码
将base64位传入数组中传给后端
chartDataArray.push(chartDataURLC.value);
chartDataArray.push(chart3DDataURL.value);
const startTime= moment(timeLine.value[0]).format("YYYY-MM-DD");
const endTime= moment(timeLine.value[1]).format("YYYY-MM-DD");
const stationNum= searchData.value;
const deviceType= selectCode.value;
const params = {
startTime: startTime, // 替换为实际的日期
endTime: endTime, // 替换为实际的日期
stationNum: stationNum, // 替换为实际的站点编号
deviceType: deviceType, // 替换为实际的设备类型
chartDataURLS: JSON.stringify(chartDataArray)
};
const response = await axios({
method: 'post',
url: import.meta.env.VITE_APP_BASE_API + `/qc-api/getEquipmentWord`, // 替换为你的实际API地址
data: params,
responseType: 'blob',
});
后端接收数据用Requestbody接收
public ResponseEntity<byte[]> getReportWord(
@RequestBody ExportEquip exportEquip
) throws Exception {
解析其中的base64位编码,并统一校验,去掉前缀,然后存入到集合中
// 解析JSON数组字符串为List<String>
List<String> chartDataURLSList;
try {
chartDataURLSList = objectMapper.readValue(exportEquip.getChartDataURLS(), new TypeReference<List<String>>() {
});
} catch (IOException e) {
throw new IllegalArgumentException("无法解析chartDataURLSJson为List<String>", e);
}
//创建一个集合存放文件路径
List<String> imagePaths = new ArrayList<>();
List<byte[]> imageList = new ArrayList<>();
for (String chartDataURL : chartDataURLSList) {
// 移除前缀
chartDataURL = chartDataURL.replaceAll("data:image/png;base64,", "");
// 校验是否为空
if (chartDataURL == null || chartDataURL.isEmpty()) {
imagePaths.add("空");
continue;
}
byte[] imageBytes = Base64.getDecoder().decode(chartDataURL);
// 创建一个BufferedImage对象
try (InputStream is = new ByteArrayInputStream(imageBytes)) {
BufferedImage image = ImageIO.read(is);
// 指定保存图片的路径和文件名
String imagePath = Const.STATE_XLSX_TEMP_PATH + "chart_" + imageList.size() + ".png";
imagePaths.add(imagePath);
// 将图片保存到本地
ImageIO.write(image, "png", new File(imagePath));
} catch (IOException e) {
throw new RuntimeException("保存图片失败", e);
}
imageList.add(imageBytes);
}
在渲染模版时,放入对应的图片位置和宽高
// 设备报告模板路径
String path = ApiConst.EQUIPMENT_DOC_TEMP_PATH_OTHER;
// 根据设备类型代码获取设备名称,并设置到设备数据对象中
equipmentTableDto.setEquipmentName(collectionToDeviceNameMap.get(type));
equipmentTableDto.setPictureC(new PictureRenderData(538, 256, imagePaths.get(2)));
equipmentTableDto.setPicture(new PictureRenderData(269, 335, imagePaths.get(1)));
之后读取生成的word文档
// 创建File对象以访问生成的Word文档
File docFile = new File(Const.STATE_XLSX_TEMP_PATH + fileName);
// 初始化文档内容数组
byte[] body = null;
// 打开文件输入流以读取文档内容
InputStream is = new FileInputStream(docFile);
// 将文档内容读入字节数组
body = new byte[is.available()];
is.read(body);
// 创建HTTP响应头对象
HttpHeaders headers = new HttpHeaders();
// 设置正确的 Content-Type 和文件名
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
String encodedFileName = URLEncoder.encode("report.docx", "UTF-8").replace("+", "%20");
headers.add("Content-Disposition", "attachment; filename*=UTF-8''" + encodedFileName);
// 设置HTTP响应状态码为200 OK
HttpStatus statusCode = HttpStatus.OK;
// 创建并返回包含文档内容、响应头和状态码的ResponseEntity对象
ResponseEntity<byte[]> entity = new ResponseEntity<>(body, headers, statusCode);
return entity;
返回一个二进制流文件数据
前端解析并下载
// 从 Content-Disposition 获取文件名
const contentDisposition = response.headers['content-disposition'];
let fileName =startTime + "-" + endTime + "数据质控报告.docx"; // 默认值
alert(1);
console.log(response.data);
console.log(response.headers);
console.log(response.status);
// 创建 Blob 并下载
const blob = new Blob([response.data], { type: response.headers['content-type'] });
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
完整
const downloadReportWord = async () => {
chart3DDataURL.value = myChart.getDataURL({
type: 'png',
pixelRatio: 2,
excludeComponents: ['toolbox'],
backgroundColor: '#404040' // 确保导出的图片背景颜色一致
});
chartDataURLC.value = barChart.value.getDataURL({
type: 'png',
pixelRatio: 2,
excludeComponents: ['toolbox'],
backgroundColor: '#404040' // 确保导出的图片背景颜色一致
});
chartDataArray.push(chartDataURLC.value);
chartDataArray.push(chart3DDataURL.value);
const startTime= moment(timeLine.value[0]).format("YYYY-MM-DD");
const endTime= moment(timeLine.value[1]).format("YYYY-MM-DD");
const stationNum= searchData.value;
const deviceType= selectCode.value;
const params = {
startTime: startTime, // 替换为实际的日期
endTime: endTime, // 替换为实际的日期
stationNum: stationNum, // 替换为实际的站点编号
deviceType: deviceType, // 替换为实际的设备类型
chartDataURLS: JSON.stringify(chartDataArray)
};
const response = await axios({
method: 'post',
url: import.meta.env.VITE_APP_BASE_API + `/qc-api/getEquipmentWord`, // 替换为你的实际API地址
data: params,
responseType: 'blob',
});
// 从 Content-Disposition 获取文件名
const contentDisposition = response.headers['content-disposition'];
let fileName =startTime + "-" + endTime + "数据质控报告.docx"; // 默认值
alert(1);
console.log(response.data);
console.log(response.headers);
console.log(response.status);
// 创建 Blob 并下载
const blob = new Blob([response.data], { type: response.headers['content-type'] });
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
};
public ResponseEntity<byte[]> getReportWord(
@RequestBody ExportEquip exportEquip
) throws Exception {
// 解析JSON数组字符串为List<String>
List<String> chartDataURLSList;
try {
chartDataURLSList = objectMapper.readValue(exportEquip.getChartDataURLS(), new TypeReference<List<String>>() {
});
} catch (IOException e) {
throw new IllegalArgumentException("无法解析chartDataURLSJson为List<String>", e);
}
//创建一个集合存放文件路径
List<String> imagePaths = new ArrayList<>();
List<byte[]> imageList = new ArrayList<>();
for (String chartDataURL : chartDataURLSList) {
// 移除前缀
chartDataURL = chartDataURL.replaceAll("data:image/png;base64,", "");
// 校验是否为空
if (chartDataURL == null || chartDataURL.isEmpty()) {
imagePaths.add("空");
continue;
}
byte[] imageBytes = Base64.getDecoder().decode(chartDataURL);
// 创建一个BufferedImage对象
try (InputStream is = new ByteArrayInputStream(imageBytes)) {
BufferedImage image = ImageIO.read(is);
// 指定保存图片的路径和文件名
String imagePath = Const.STATE_XLSX_TEMP_PATH + "chart_" + imageList.size() + ".png";
imagePaths.add(imagePath);
// 将图片保存到本地
ImageIO.write(image, "png", new File(imagePath));
} catch (IOException e) {
throw new RuntimeException("保存图片失败", e);
}
imageList.add(imageBytes);
}
// 根据开始时间和结束时间生成文件名
String fileName = exportEquip.getStartTime() + "-" + exportEquip.getEndTime() + "数据质控报告.docx";
// 调用服务方法生成Word文档
qcService.exportEquipmentWord(Const.STATE_XLSX_TEMP_PATH + fileName, exportEquip.getStartTime(), exportEquip.getEndTime(), exportEquip.getDeviceType(), exportEquip.getStationNum(), imagePaths);
// 读取生成的文档
byte[] fileContent;
try (InputStream is = new FileInputStream(Const.STATE_XLSX_TEMP_PATH + fileName)) {
fileContent = IOUtils.toByteArray(is);
} catch (IOException e) {
// 处理异常
throw new RuntimeException("读取Word文件失败", e);
}
// 创建File对象以访问生成的Word文档
File docFile = new File(Const.STATE_XLSX_TEMP_PATH + fileName);
// 初始化文档内容数组
byte[] body = null;
// 打开文件输入流以读取文档内容
InputStream is = new FileInputStream(docFile);
// 将文档内容读入字节数组
body = new byte[is.available()];
is.read(body);
// 创建HTTP响应头对象
HttpHeaders headers = new HttpHeaders();
// 设置正确的 Content-Type 和文件名
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
String encodedFileName = URLEncoder.encode("report.docx", "UTF-8").replace("+", "%20");
headers.add("Content-Disposition", "attachment; filename*=UTF-8''" + encodedFileName);
// 设置HTTP响应状态码为200 OK
HttpStatus statusCode = HttpStatus.OK;
// 创建并返回包含文档内容、响应头和状态码的ResponseEntity对象
ResponseEntity<byte[]> entity = new ResponseEntity<>(body, headers, statusCode);
return entity;
}