提交 f30ace05 作者: lixiaojuan

打包

上级 96886500
# 构建阶段
FROM swr.cn-southwest-2.myhuaweicloud.com/wd/maven:3.8.5-openjdk-17 AS builder
USER root
WORKDIR /workspace
COPY . .
RUN mvn clean install -DskipTests
# 运行时阶段:直接用精简版 openjdk 镜像
FROM swr.cn-southwest-2.myhuaweicloud.com/wd/openjdk:17-fontconfig
MAINTAINER lixingyu
USER root
WORKDIR /workspace
# 复制 jar 包
COPY --from=builder /workspace/target/*.jar /workspace/app.jar
# 环境变量
ARG ENV_NAME
ARG NACOS_SERVER
ARG NACOS_NAMESPACE
ARG NACOS_REGISTER_IP
ARG PORT
ENV APP_OPTS1="-Dspring.profiles.active=${ENV_NAME}"
ENV APP_OPTS2="-Djasypt.encryptor.password=1@wdLkj90#chMsdzxA%2024"
ENV APP_OPTS3="-Dfile.encoding=utf-8"
ENV APP_OPTS4="-Duser.timezone=Asia/Shanghai"
# 无头模式避免 AWT 调用
ENV APP_OPTS9="-Djava.awt.headless=true"
ENV APP_OPTS5="-DNACOS_SERVER=${NACOS_SERVER}"
ENV APP_OPTS6="-DNACOS_NAMESPACE=${NACOS_NAMESPACE}"
ENV APP_OPTS7="-DNACOS_REGISTER_IP=${NACOS_REGISTER_IP}"
ENV JVM_OPTS="-Xmx2024M -Xms256M"
EXPOSE ${PORT}
ENTRYPOINT ["sh","-c","java $APP_OPTS1 $APP_OPTS2 $APP_OPTS3 $APP_OPTS4 $APP_OPTS9 $APP_OPTS5 $APP_OPTS6 $APP_OPTS7 $JVM_OPTS -jar /workspace/app.jar"]
\ No newline at end of file
#!/bin/bash
# build.sh - 功能包括:镜像构建,推送镜像,处理none镜像。
# author: lixingyu
# 使用方法:
# $ ./build.sh <env_name> <build_timestamp>
# 参数:
# env_name:环境变量 build_timestamp:构建时间戳
set -eu
# 获取脚本的绝对路径(包括文件名)
script_path=$(readlink -f "$0")
# 获取脚本所在的目录的绝对路径
script_dir=$(dirname "$script_path")
# 函数定义
function print_usage {
# 打印使用说明
sed -n '2,7p' "$0"
}
function handle {
local env_name=$1
local build_timestamp=$2
echo "执行的环境变量: ${env_name}"
echo "执行的构建时间戳: ${build_timestamp}"
if [ ! -f "$script_dir/env/env.${env_name}" ]; then
echo "错误: 文件 'env.${env_name}' 不存在." >&2
exit 1
fi
. $script_dir/env/env.${env_name}
local username="${repo_username}"
local passwd="${repo_passwd}"
local name="${svc_name}-${svc_env}"
local version=${version}-${build_timestamp}
local internal_ip="${internal_ip}"
echo "构建名称: ${name}"
echo "构建端口: ${svc_port}"
echo "部署节点: ${internal_ip}"
echo "构建版本: ${version}"
echo "推送仓库: ${domain}/${namespace}"
echo "完整镜像: ${domain}/${namespace}/${name}:${version}"
echo "---构建镜像---"
docker build --build-arg NACOS_REGISTER_IP=${internal_ip} --build-arg NACOS_SERVER=${nacos_server} --build-arg NACOS_NAMESPACE=${nacos_namespace} \
--build-arg ENV_NAME=${svc_env} --build-arg PORT=${svc_port} -f ./build/Dockerfile -t ${domain}/${namespace}/${name}:${version} .
echo "---推送镜像---"
docker login -u=$username -p=${passwd} ${domain}
docker push ${domain}/${namespace}/${name}:${version}
echo "---清理none镜像---"
docker container prune -f
docker image prune -af
}
# 主程序入口点
function main {
if [ "$#" -ne 2 ]; then
print_usage
echo "错误: 需要提供两个个参数 <env_name> <build_timestamp>" >&2
exit 1
fi
local env_name=$1
local build_timestamp=$2
handle "${env_name}" "${build_timestamp}"
}
# 错误处理
trap 'echo 发生了错误,脚本中断.' ERR
# 调用主函数
main "$@"
# 镜像仓库
## 版本 T1.0.0: T为测试版,R为稳定版
version="T1.0.0" # 可自定义
## 镜像仓库地址
domain="swr.cn-southwest-2.myhuaweicloud.com"
## 镜像分组,按部门区分
namespace="wd"
# 服务配置
## 部署节点的内网IP
internal_ip=server-1-95-67-224.ciglobal.cn
## 数据挂载根目录
root_dir="/zzsn"
## 需根据日志配置填写,比如:logback-spring.xml
svc_logs="/workspace/logs"
## 环境变量 test or prod
svc_env="prod"
## 服务名
svc_name="excel-export-service"
## 服务端口
svc_port="9120"
# nacos配置
## 往nacos注册IP,配置文件使用变量 NACOS_REGISTER_IP
## nacos服务地址, 配置文件使用 NACOS_SERVER
nacos_server="server-1-95-77-159.ciglobal.cn:8848"
## nacos命名空间, 配置文件使用 NACOS_NAMESPACE
nacos_namespace="smartProd"
# 资源限制
## cpu
limit_cpu=1.0
limit_mem=2g
...@@ -8,7 +8,7 @@ namespace="wd" ...@@ -8,7 +8,7 @@ namespace="wd"
# 服务配置 # 服务配置
## 部署节点的内网IP ## 部署节点的内网IP
internal_ip=server-1-95-14-24.ciglobal.cn internal_ip=server-1-95-77-159.ciglobal.cn
## 数据挂载根目录 ## 数据挂载根目录
root_dir="/zzsn" root_dir="/zzsn"
## 需根据日志配置填写,比如:logback-spring.xml ## 需根据日志配置填写,比如:logback-spring.xml
...@@ -18,7 +18,7 @@ svc_env="test" ...@@ -18,7 +18,7 @@ svc_env="test"
## 服务名 ## 服务名
svc_name="excel-export-service" svc_name="excel-export-service"
## 服务端口 ## 服务端口
svc_port="8089" svc_port="9120"
# nacos配置 # nacos配置
## 往nacos注册IP,配置文件使用变量 NACOS_REGISTER_IP ## 往nacos注册IP,配置文件使用变量 NACOS_REGISTER_IP
......
#!/bin/bash
# run.sh - 功能包括:运行容器,处理none镜像。
# author: lixingyu
# 使用方法:
# $ ./run.sh <env_name> <build_timestamp>
# 参数:
# env_name:环境变量 build_timestamp:构建时间戳
set -eu
# 获取脚本的绝对路径(包括文件名)
script_path=$(readlink -f "$0")
# 获取脚本所在的目录的绝对路径
script_dir=$(dirname "$script_path")
# 函数定义
function print_usage {
# 打印使用说明
sed -n '2,7p' "$0"
}
function handle {
local env_name=$1
local build_timestamp=$2
echo "执行的环境变量: ${env_name}"
echo "执行的构建时间戳: ${build_timestamp}"
if [ ! -f "$script_dir/env/env.${env_name}" ]; then
echo "错误: 文件 'env.${env_name}' 不存在." >&2
exit 1
fi
. $script_dir/env/env.${env_name}
local dir="${root_dir}/${svc_name}/${svc_env}"
local name="${svc_name}-${svc_env}"
local version=${version}-${build_timestamp}
local internal_ip="${internal_ip}"
echo "---运行容器: ${name}:${version}---"
# 构建 JSON 数据
json_data="{ \
\"container_name\": \"$name\", \
\"image_version\": \"$domain/$namespace/$name:$version\", \
\"ports\": [\"$svc_port:$svc_port\"], \
\"mount_infos\": [\"/etc/localtime:/etc/localtime:ro\", \"$dir/logs:$svc_logs\"], \
\"cpu_count\": $limit_cpu, \
\"memory_limit\": \"$limit_mem\", \
\"ulimit\": \"nofile=65535:65535\" \
}"
# 执行 curl 请求,并将响应结果存储在 response 变量中
response=$(curl -s -X POST -H "Content-Type: application/json" -H "X-API-Key: uOyKfp20pdM3MFhr3KAQBoe1UHCaZLUeeLephB57MPvGXTY05Eis5eaxta6fEtpa" -d "$json_data" "http://$internal_ip:10080/start-container")
echo "响应结果: ${response}"
# 检查响应中是否包含 "succeed"
if echo "$response" | grep -q "successfully"; then
echo "部署成功"
exit 0
else
echo "部署失败"
exit 1
fi
}
# 主程序入口点
function main {
if [ "$#" -ne 2 ]; then
print_usage
echo "错误: 需要提供两个个参数 <env_name> <build_timestamp>" >&2
exit 1
fi
local env_name=$1
local build_timestamp=$2
handle "${env_name}" "${build_timestamp}"
}
# 错误处理
trap 'echo 发生了错误,脚本中断.' ERR
# 调用主函数
main "$@"
...@@ -121,7 +121,7 @@ ...@@ -121,7 +121,7 @@
<dependency> <dependency>
<groupId>org.apache.poi</groupId> <groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId> <artifactId>poi</artifactId>
<version>4.1.2</version> <version>5.4.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>commons-lang</groupId> <groupId>commons-lang</groupId>
...@@ -131,7 +131,7 @@ ...@@ -131,7 +131,7 @@
<dependency> <dependency>
<groupId>org.apache.poi</groupId> <groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId> <artifactId>poi-ooxml</artifactId>
<version>4.1.2</version> <version>5.4.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.alibaba.fastjson2</groupId> <groupId>com.alibaba.fastjson2</groupId>
......
...@@ -21,6 +21,7 @@ import java.io.IOException; ...@@ -21,6 +21,7 @@ import java.io.IOException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.*; import java.util.*;
import com.zzsn.excelexportservice.dto.ExportDataResponse.Header; import com.zzsn.excelexportservice.dto.ExportDataResponse.Header;
/** /**
* @Author: lxj * @Author: lxj
* @Date: 2025/9/18 11:07 * @Date: 2025/9/18 11:07
...@@ -29,6 +30,12 @@ import com.zzsn.excelexportservice.dto.ExportDataResponse.Header; ...@@ -29,6 +30,12 @@ import com.zzsn.excelexportservice.dto.ExportDataResponse.Header;
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
public class ExcelExportService { public class ExcelExportService {
static {
// 设置无头模式,避免字体配置问题
System.setProperty("java.awt.headless", "true");
}
@Resource @Resource
private CmsSearch cmsSearch; private CmsSearch cmsSearch;
...@@ -39,66 +46,124 @@ public class ExcelExportService { ...@@ -39,66 +46,124 @@ public class ExcelExportService {
sendError(response, "导出失败: 入参为空"); sendError(response, "导出失败: 入参为空");
return; return;
} }
SXSSFWorkbook workbook = new SXSSFWorkbook(1000);
workbook.setCompressTempFiles(true);
for (ExportReq exportReq : exportReqList) { SXSSFWorkbook workbook = null;
ExportDataResponse resp = null; try {
String serviceName = exportReq.getServiceName(); workbook = new SXSSFWorkbook(1000);
Map<String, Object> queryParams = exportReq.getQueryParams(); workbook.setCompressTempFiles(true);
ExportStrategy strategy = exportStrategyFactory.getStrategy(serviceName, exportReq.getApiPath()); for (ExportReq exportReq : exportReqList) {
if (strategy == null) { ExportDataResponse resp = null;
log.error("No strategy found for service: {}, path: {}", serviceName, exportReq.getApiPath()); String serviceName = exportReq.getServiceName();
sendError(response, "导出失败: 未找到匹配的服务策略"); Map<String, Object> queryParams = exportReq.getQueryParams() != null
return; ? exportReq.getQueryParams()
} : new HashMap<>();
ExportStrategy strategy = exportStrategyFactory.getStrategy(serviceName, exportReq.getApiPath());
if (strategy == null) {
log.error("No strategy found for service: {}, path: {}", serviceName, exportReq.getApiPath());
sendError(response, "导出失败: 未找到匹配的服务策略");
return;
}
resp = strategy.execute(request, queryParams, exportReq); resp = strategy.execute(request, queryParams, exportReq);
if (resp == null || resp.getCode() != 0) { if (resp == null || resp.getCode() != 0) {
log.error("导出数据失败, serviceName: {}, apiPath: {}, errorMsg: {}", log.error("导出数据失败, serviceName: {}, apiPath: {}, errorMsg: {}",
serviceName, exportReq.getApiPath(), resp != null ? resp.getMsg() : "null"); serviceName, exportReq.getApiPath(), resp != null ? resp.getMsg() : "null");
sendError(response, resp != null ? resp.getMsg() : "导出失败: 未知错误"); sendError(response, resp != null ? resp.getMsg() : "导出失败: 未知错误");
return; return;
} }
// 创建 sheet // 安全创建 sheet
Sheet sheet = workbook.createSheet( String sheetName = generateSafeSheetName(
StringUtils.isNotBlank(exportReq.getSheetName()) StringUtils.isNotBlank(exportReq.getSheetName())
? exportReq.getSheetName() ? exportReq.getSheetName()
: "Sheet_" + serviceName); : "Sheet_" + serviceName
);
Sheet sheet = workbook.createSheet(sheetName);
int startRow = 0;
int endCol = 13;
// 判断是否有图片
if (StringUtils.isNotBlank(exportReq.getBase64Img())) {
log.info("开始插入图片------->" + System.currentTimeMillis());
drawImg(workbook, sheet, exportReq.getBase64Img(), startRow, endCol);
startRow++; // 图片占用一行,从下一行开始写标题
}
int startRow = 0; List<Map<String, Object>> dataList = resp.getDataList() != null
int endCol = 13; ? resp.getDataList()
: new ArrayList<>();
List<Header> headers = resp.getHeaders() != null
? resp.getHeaders()
: new ArrayList<>();
// 判断是否有图片 // 写标题
if (StringUtils.isNotBlank(exportReq.getBase64Img())) { log.info("开始插入标题------->" + System.currentTimeMillis());
log.info("开始插入图片------->" + System.currentTimeMillis()); handlerExcelTitle(workbook, sheet, startRow, headers);
drawImg(workbook, sheet, exportReq.getBase64Img(), startRow, endCol);
startRow++; // 图片占用一行,从下一行开始写标题
}
List<Map<String, Object>> dataList = resp.getDataList(); // 写数据
List<Header> headers = resp.getHeaders(); log.info("开始写入数据------->" + System.currentTimeMillis());
handlerExcelData(workbook, sheet, startRow + 1, headers, dataList);
// 写标 // 设置手动列宽,避免自动列宽计算导致的字体问
log.info("开始插入标题------->" + System.currentTimeMillis()); setupManualColumnWidth(sheet, headers.size());
handlerExcelTitle(workbook, sheet, startRow, headers); }
// 写数据 // 最后写到 response
log.info("开始写入数据------->" + System.currentTimeMillis()); log.info("生成excel结束------->" + System.currentTimeMillis());
handlerExcelData(workbook, sheet, startRow + 1, headers, dataList); writeExcelToResponse("导出", workbook, response);
} catch (Exception e) {
log.error("导出Excel异常", e);
sendError(response, "导出失败: " + e.getMessage());
} finally {
// 确保资源释放
if (workbook != null) {
try {
workbook.close();
} catch (IOException e) {
log.warn("关闭workbook异常", e);
}
workbook.dispose();
}
} }
}
// 最后写到 response /**
log.info("生成excel结束------->" + System.currentTimeMillis()); * 生成安全的sheet名称
writeExcelToResponse("导出", workbook, response); */
private String generateSafeSheetName(String name) {
if (StringUtils.isBlank(name)) {
return "Sheet1";
}
// 移除Excel不允许的字符:\ / * [ ] : ?
String safeName = name.replaceAll("[\\\\/*\\[\\]:?]", "");
// 限制长度在31个字符以内
return safeName.length() > 31 ? safeName.substring(0, 31) : safeName;
}
/**
* 设置手动列宽
*/
private void setupManualColumnWidth(Sheet sheet, int columnCount) {
try {
// 根据列数设置合适的宽度,避免自动列宽计算
for (int i = 0; i < columnCount; i++) {
sheet.setColumnWidth(i, 5000); // 设置固定宽度
}
} catch (Exception e) {
log.warn("设置列宽失败,使用默认宽度", e);
}
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private <T> T getParam(Map<String, Object> params, String key, T defaultVal) { private <T> T getParam(Map<String, Object> params, String key, T defaultVal) {
if (params == null) {
return defaultVal;
}
Object val = params.get(key); Object val = params.get(key);
if (val == null) { if (val == null) {
return defaultVal; return defaultVal;
...@@ -111,62 +176,77 @@ public class ExcelExportService { ...@@ -111,62 +176,77 @@ public class ExcelExportService {
} }
} }
private static void sendError(HttpServletResponse response, String msg) { private static void sendError(HttpServletResponse response, String msg) {
response.reset(); try {
response.setStatus(1); response.reset();
response.setContentType("text/plain;charset=UTF-8"); response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
try { response.setContentType("text/plain;charset=UTF-8");
response.getWriter().write(msg != null ? msg : "未知错误"); response.getWriter().write(msg != null ? msg : "未知错误");
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); log.error("发送错误响应失败", e);
}
} }
}
// 插入图片(Base64) // 插入图片(Base64)
private void drawImg(SXSSFWorkbook workbook, Sheet sheet, String base64Img, int rowIndex, int endCol) { private void drawImg(SXSSFWorkbook workbook, Sheet sheet, String base64Img, int rowIndex, int endCol) {
Row firstRow = sheet.createRow(rowIndex); try {
for (int i = 0; i < endCol; i++) { Row firstRow = sheet.createRow(rowIndex);
firstRow.createCell(i); for (int i = 0; i < endCol; i++) {
} firstRow.createCell(i);
firstRow.setHeightInPoints(230);
// 合并单元格
sheet.addMergedRegion(new CellRangeAddress(rowIndex, rowIndex, 0, endCol));
if (base64Img != null && !base64Img.isBlank()) {
int start = base64Img.indexOf(",");
if (start != -1) {
base64Img = base64Img.substring(start + 1);
} }
byte[] imgBytes = Base64.getDecoder().decode(base64Img); firstRow.setHeightInPoints(230);
// 合并单元格
sheet.addMergedRegion(new CellRangeAddress(rowIndex, rowIndex, 0, endCol));
if (StringUtils.isNotBlank(base64Img)) {
int start = base64Img.indexOf(",");
String imgData = (start != -1) ? base64Img.substring(start + 1) : base64Img;
if (StringUtils.isNotBlank(imgData)) {
byte[] imgBytes = Base64.getDecoder().decode(imgData);
Drawing<?> drawingPatriarch = sheet.createDrawingPatriarch(); Drawing<?> drawingPatriarch = sheet.createDrawingPatriarch();
ClientAnchor anchor = drawingPatriarch.createAnchor(0, 0, 0, 0, 0, rowIndex, endCol, rowIndex + 1); ClientAnchor anchor = drawingPatriarch.createAnchor(0, 0, 0, 0, 0, rowIndex, endCol, rowIndex + 1);
int pictureIdx = workbook.addPicture(imgBytes, Workbook.PICTURE_TYPE_PNG); int pictureIdx = workbook.addPicture(imgBytes, Workbook.PICTURE_TYPE_PNG);
drawingPatriarch.createPicture(anchor, pictureIdx); drawingPatriarch.createPicture(anchor, pictureIdx);
}
}
} catch (Exception e) {
log.warn("插入图片失败", e);
} }
} }
// 写入标题 // 写入标题
private void handlerExcelTitle(Workbook workbook, Sheet sheet, int rowIndex, List<Header> headers) { private void handlerExcelTitle(Workbook workbook, Sheet sheet, int rowIndex, List<Header> headers) {
Row row = sheet.createRow(rowIndex); if (headers == null || headers.isEmpty()) {
CellStyle titleStyle = getTitleStyle(workbook); return;
if (headers != null && !headers.isEmpty()) { }
try {
Row row = sheet.createRow(rowIndex);
CellStyle titleStyle = getTitleStyle(workbook);
for (int i = 0; i < headers.size(); i++) { for (int i = 0; i < headers.size(); i++) {
Cell cell = row.createCell(i); Cell cell = row.createCell(i);
cell.setCellStyle(titleStyle); cell.setCellStyle(titleStyle);
cell.setCellValue(headers.get(i).getTitle()); String title = headers.get(i) != null ? headers.get(i).getTitle() : "";
cell.setCellValue(title != null ? title : "");
} }
} catch (Exception e) {
log.warn("写入标题失败", e);
} }
} }
// Title 样式 // Title 样式
private CellStyle getTitleStyle(Workbook workbook) { private CellStyle getTitleStyle(Workbook workbook) {
Font font = workbook.createFont();
font.setFontName("Courier New");
font.setBold(true);
CellStyle style = workbook.createCellStyle(); CellStyle style = workbook.createCellStyle();
style.setFont(font); try {
Font font = workbook.createFont();
font.setFontName("Arial"); // 使用通用字体
font.setBold(true);
style.setFont(font);
} catch (Exception e) {
log.warn("创建标题样式失败,使用默认样式", e);
}
style.setAlignment(HorizontalAlignment.CENTER); style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER); style.setVerticalAlignment(VerticalAlignment.CENTER);
return style; return style;
...@@ -175,54 +255,71 @@ private static void sendError(HttpServletResponse response, String msg) { ...@@ -175,54 +255,71 @@ private static void sendError(HttpServletResponse response, String msg) {
// 写入数据 // 写入数据
private void handlerExcelData(Workbook workbook, Sheet sheet, int startRowIndex, private void handlerExcelData(Workbook workbook, Sheet sheet, int startRowIndex,
List<Header> headers, List<Map<String, Object>> dataList) { List<Header> headers, List<Map<String, Object>> dataList) {
CellStyle dataStyle = getDataStyle(workbook); if (headers == null || headers.isEmpty() || dataList == null || dataList.isEmpty()) {
if (headers != null && !headers.isEmpty() && dataList != null && !dataList.isEmpty()) { return;
}
try {
CellStyle dataStyle = getDataStyle(workbook);
for (int j = 0; j < dataList.size(); j++) { for (int j = 0; j < dataList.size(); j++) {
Row row = sheet.createRow(startRowIndex + j); Row row = sheet.createRow(startRowIndex + j);
Map<String, Object> dataMap = dataList.get(j); Map<String, Object> dataMap = dataList.get(j);
if (dataMap == null) {
continue;
}
for (int i = 0; i < headers.size(); i++) { for (int i = 0; i < headers.size(); i++) {
Cell cell = row.createCell(i); Cell cell = row.createCell(i);
cell.setCellStyle(dataStyle); cell.setCellStyle(dataStyle);
Object value = dataMap.get(headers.get(i).getField());
Header header = headers.get(i);
if (header == null) {
cell.setCellValue("");
continue;
}
String field = header.getField();
Object value = dataMap.get(field);
cell.setCellValue(value == null ? "" : value.toString()); cell.setCellValue(value == null ? "" : value.toString());
} }
} }
} catch (Exception e) {
log.warn("写入数据失败", e);
} }
} }
// 数据样式 // 数据样式
private CellStyle getDataStyle(Workbook workbook) { private CellStyle getDataStyle(Workbook workbook) {
Font font = workbook.createFont();
font.setFontName("Courier New");
CellStyle style = workbook.createCellStyle(); CellStyle style = workbook.createCellStyle();
style.setFont(font); try {
Font font = workbook.createFont();
font.setFontName("Arial"); // 使用通用字体
style.setFont(font);
} catch (Exception e) {
log.warn("创建数据样式失败,使用默认样式", e);
}
style.setAlignment(HorizontalAlignment.CENTER); style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER); style.setVerticalAlignment(VerticalAlignment.CENTER);
return style; return style;
} }
// 将数据写入 HttpServletResponse(流式,不占用内存) // 将数据写入 HttpServletResponse
private void writeExcelToResponse(String fileNamePrefix, SXSSFWorkbook workbook, HttpServletResponse response) { private void writeExcelToResponse(String fileNamePrefix, SXSSFWorkbook workbook, HttpServletResponse response) {
try { try {
response.reset(); response.reset();
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-disposition", String fileName = URLEncoder.encode(
"attachment;filename=" + URLEncoder.encode(fileNamePrefix + System.currentTimeMillis() + ".xlsx", "UTF-8")); (fileNamePrefix != null ? fileNamePrefix : "导出") + System.currentTimeMillis() + ".xlsx",
"UTF-8"
);
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
response.setHeader("Access-Control-Expose-Headers", "Content-Disposition"); response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
workbook.write(response.getOutputStream()); workbook.write(response.getOutputStream());
response.getOutputStream().flush(); response.getOutputStream().flush();
} catch (IOException e) { } catch (IOException e) {
log.error("写入响应流失败", e);
sendError(response, "导出 Excel 异常:" + e.getMessage()); sendError(response, "导出 Excel 异常:" + e.getMessage());
} finally {
// 清理临时文件
try {
workbook.close();
} catch (IOException ignored) {
}
workbook.dispose();
} }
} }
} }
\ No newline at end of file
server: server:
port: 8082 port: 9120
spring: spring:
application: application:
name: excel-export-service name: excel-export-service
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论