5.6 KiB
5.6 KiB
学生信息导入模板下载功能修复记录
日期: 2025-07-31
修改人: AI Assistant
修改原因: 修复下载导入模板时的"Broken pipe"错误
问题描述
在学生信息管理页面中,点击"下载模板"按钮时出现以下错误:
org.springframework.web.context.request.async.AsyncRequestNotUsableException: ServletOutputStream failed to write: Broken pipe
错误分析
- 文件较大: 模板文件504KB,传输时间较长
- 客户端断开连接: 用户在下载过程中可能关闭了浏览器或网络中断
- 服务器继续写入: 服务器仍在尝试向已断开的连接写入数据
- 异常处理不当: 后端代码在响应已提交后仍尝试写入错误信息
解决方案
1. 后端优化 (XgStudentMsgController.java)
主要改进:
- 文件存在性检查: 在开始下载前检查模板文件是否存在
- 连接状态监控: 在写入过程中检查客户端连接状态
- 缓冲区优化: 使用较小的缓冲区(4KB)并定期刷新
- 异常处理增强: 专门处理连接断开相关的异常
- 响应头优化: 添加缓存控制头
关键代码改进:
// 检查文件是否存在
if (!resource.exists()) {
log.error("模板文件不存在: student_template.xlsx");
if (!response.isCommitted()) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
response.setContentType("text/plain;charset=UTF-8");
response.getWriter().write("模板文件不存在");
}
return;
}
// 连接状态检查
while ((bytesRead = inputStream.read(buffer)) != -1) {
// 检查响应是否已提交(客户端是否断开连接)
if (response.isCommitted()) {
log.warn("客户端连接已断开,停止写入数据");
break;
}
try {
outputStream.write(buffer, 0, bytesRead);
totalBytes += bytesRead;
// 定期刷新缓冲区,确保数据及时发送
if (totalBytes % 16384 == 0) { // 每16KB刷新一次
outputStream.flush();
}
} catch (IOException e) {
// 检查是否是连接断开错误
if (e.getMessage() != null &&
(e.getMessage().contains("Broken pipe") ||
e.getMessage().contains("Connection reset") ||
e.getMessage().contains("Connection refused"))) {
log.warn("客户端连接断开,停止下载: {}", e.getMessage());
break;
} else {
throw e;
}
}
}
2. 前端优化 (index.vue)
主要改进:
- 超时处理: 添加60秒下载超时机制
- 错误分类: 根据不同的错误类型显示相应的错误信息
- 用户体验: 优化文件名和提示信息
- 资源清理: 确保超时定时器正确清理
关键代码改进:
/** 下载模板操作 */
const importTemplate = () => {
// 显示下载进度提示
const loadingMessage = ElMessage({
message: '正在下载模板,请稍候...',
type: 'info',
duration: 0,
zIndex: 4000
});
// 设置下载超时时间
const downloadTimeout = setTimeout(() => {
ElMessage.closeAll();
ElMessage.error('下载超时,请稍后重试');
}, 60000); // 60秒超时
proxy
?.download('studentMsg/studentMsg/importTemplate', {}, `学生信息导入模板_${new Date().getTime()}.xlsx`)
.then(() => {
// 下载成功后关闭提示和超时定时器
clearTimeout(downloadTimeout);
ElMessage.closeAll();
ElMessage.success('模板下载成功');
})
.catch((error) => {
// 下载失败时关闭提示和超时定时器
clearTimeout(downloadTimeout);
ElMessage.closeAll();
console.error('下载模板失败:', error);
// 根据错误类型显示不同的错误信息
if (error.message && error.message.includes('timeout')) {
ElMessage.error('下载超时,请稍后重试');
} else if (error.message && error.message.includes('403')) {
ElMessage.error('权限不足,无法下载模板');
} else if (error.message && error.message.includes('404')) {
ElMessage.error('模板文件不存在,请联系管理员');
} else if (error.message && error.message.includes('500')) {
ElMessage.error('服务器内部错误,请稍后重试');
} else if (error.message && error.message.includes('Network Error')) {
ElMessage.error('网络连接异常,请检查网络后重试');
} else {
ElMessage.error('下载模板失败,请稍后重试');
}
});
};
修改文件列表
-
后端文件:
backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgStudentMsgController.java- 优化
importTemplate()方法
- 优化
-
前端文件:
web/src/views/xg/xsjcxx/studentMsg/studentMsg/index.vue- 优化
importTemplate()方法
- 优化
测试建议
- 正常下载测试: 验证模板文件能正常下载
- 网络中断测试: 在下载过程中断开网络连接
- 浏览器关闭测试: 在下载过程中关闭浏览器
- 大文件传输测试: 验证大文件的传输稳定性
- 并发下载测试: 多个用户同时下载模板
预期效果
- ✅ 解决"Broken pipe"错误
- ✅ 提升下载稳定性
- ✅ 改善用户体验
- ✅ 增强错误处理能力
- ✅ 减少服务器资源浪费
注意事项
- 模板文件大小(504KB)相对较大,建议后续考虑压缩或优化
- 定期检查模板文件是否存在和完整性
- 监控下载失败率,及时发现问题
- 考虑添加下载统计和监控功能