Files
hzdyjs/docs/变更管理/code-adjust-record-2025-07-31-student-template-download-fix.md
T
2025-07-31 19:52:03 +08:00

166 lines
5.6 KiB
Markdown

# 学生信息导入模板下载功能修复记录
**日期**: 2025-07-31
**修改人**: AI Assistant
**修改原因**: 修复下载导入模板时的"Broken pipe"错误
## 问题描述
在学生信息管理页面中,点击"下载模板"按钮时出现以下错误:
```
org.springframework.web.context.request.async.AsyncRequestNotUsableException: ServletOutputStream failed to write: Broken pipe
```
### 错误分析
1. **文件较大**: 模板文件504KB,传输时间较长
2. **客户端断开连接**: 用户在下载过程中可能关闭了浏览器或网络中断
3. **服务器继续写入**: 服务器仍在尝试向已断开的连接写入数据
4. **异常处理不当**: 后端代码在响应已提交后仍尝试写入错误信息
## 解决方案
### 1. 后端优化 (XgStudentMsgController.java)
#### 主要改进:
- **文件存在性检查**: 在开始下载前检查模板文件是否存在
- **连接状态监控**: 在写入过程中检查客户端连接状态
- **缓冲区优化**: 使用较小的缓冲区(4KB)并定期刷新
- **异常处理增强**: 专门处理连接断开相关的异常
- **响应头优化**: 添加缓存控制头
#### 关键代码改进:
```java
// 检查文件是否存在
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秒下载超时机制
- **错误分类**: 根据不同的错误类型显示相应的错误信息
- **用户体验**: 优化文件名和提示信息
- **资源清理**: 确保超时定时器正确清理
#### 关键代码改进:
```javascript
/** 下载模板操作 */
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('下载模板失败,请稍后重试');
}
});
};
```
## 修改文件列表
1. **后端文件**:
- `backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgStudentMsgController.java`
- 优化 `importTemplate()` 方法
2. **前端文件**:
- `web/src/views/xg/xsjcxx/studentMsg/studentMsg/index.vue`
- 优化 `importTemplate()` 方法
## 测试建议
1. **正常下载测试**: 验证模板文件能正常下载
2. **网络中断测试**: 在下载过程中断开网络连接
3. **浏览器关闭测试**: 在下载过程中关闭浏览器
4. **大文件传输测试**: 验证大文件的传输稳定性
5. **并发下载测试**: 多个用户同时下载模板
## 预期效果
- ✅ 解决"Broken pipe"错误
- ✅ 提升下载稳定性
- ✅ 改善用户体验
- ✅ 增强错误处理能力
- ✅ 减少服务器资源浪费
## 注意事项
1. 模板文件大小(504KB)相对较大,建议后续考虑压缩或优化
2. 定期检查模板文件是否存在和完整性
3. 监控下载失败率,及时发现问题
4. 考虑添加下载统计和监控功能