1. HttpServletResponse基础入门
第一次接触HttpServletResponse时,我盯着这个长长的类名发呆了十分钟。后来才发现,它其实就是服务器给浏览器回信的"邮递员"。每次用户访问网站,服务器都会通过这个对象把网页内容、图片或者文件打包发送给浏览器。想象一下,你在餐厅点餐,服务员把做好的菜端上桌的过程——HttpServletResponse就是那个负责上菜的服务员。
这个对象最神奇的地方在于它能控制回应的方方面面。比如设置状态码告诉浏览器"页面找到了"(200)或者"页面搬家了"(302),还能决定返回的内容是HTML网页还是PDF文件。我刚开始学的时候,最常犯的错误就是忘记设置Content-Type,结果浏览器把JSON数据当成了文件下载,闹出不少笑话。
// 最简单的响应示例 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); out.println("<h1>你好,世界!</h1>"); }2. 响应头深度解析
2.1 必知必会的核心响应头
响应头就像是快递包裹上的标签,告诉浏览器该怎么处理这个包裹。这几个响应头是我项目中最常用的:
- Content-Type:决定浏览器如何解析内容。有次我误设成
application/octet-stream,导致HTML页面变成了文件下载 - Cache-Control:控制缓存行为,对静态资源优化特别重要
- Content-Disposition:文件下载时的关键设置,能指定下载文件名
- Set-Cookie:设置Cookie的神器,但要注意安全属性
// 设置多个响应头的正确姿势 response.setHeader("Cache-Control", "no-cache"); response.setHeader("Pragma", "no-cache"); response.setDateHeader("Expires", 0);2.2 解决中文乱码的终极方案
中文乱码问题困扰了我整整一周,直到搞明白这几个要点:
- 服务器编码要统一:
response.setCharacterEncoding("UTF-8") - 告诉浏览器用什么编码:
response.setContentType("text/html;charset=UTF-8") - 获取Writer前必须设置好编码
有次线上事故就是因为测试环境用GBK而生产环境用UTF-8,导致用户看到的全是乱码。现在我的项目里都会统一加上这段代码:
// 万无一失的编码设置 response.setContentType("text/html;charset=UTF-8"); response.setCharacterEncoding("UTF-8");3. 状态码与页面控制
3.1 状态码实战指南
状态码不是随便设的,每个数字都有特殊含义。这些是我踩过坑后总结的经验:
- 302重定向时一定要设置Location头
- 404页面要友好,最好自定义错误页面
- 500错误要记录日志,但不要暴露系统信息给用户
// 正确的重定向方式 response.setStatus(HttpServletResponse.SC_FOUND); // 302 response.setHeader("Location", "/new-location"); // 或者更简单的写法 response.sendRedirect("/new-location");3.2 自动刷新与定时跳转
自动刷新在支付成功页面特别有用。我做过一个电商项目,支付成功后5秒跳转到订单页:
// 5秒后跳转到百度 response.setHeader("Refresh", "5;url=https://www.baidu.com");更复杂的场景可以用JavaScript实现,比如倒计时显示:
// 在JSP或HTML中嵌入的JS代码 let seconds = 5; setInterval(() => { document.getElementById("countdown").innerHTML = --seconds; if(seconds <= 0) location.href = "/order"; }, 1000);4. 文件流处理实战
4.1 文件下载全攻略
文件下载看着简单,实际要注意的细节很多。这是我优化过的下载代码:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String filePath = "/data/files/report.pdf"; File file = new File(filePath); // 设置响应头 response.setContentType("application/pdf"); response.setHeader("Content-Disposition", "attachment; filename=\"季度报告.pdf\""); response.setContentLength((int)file.length()); // 使用try-with-resources确保流关闭 try (InputStream in = new FileInputStream(file); OutputStream out = response.getOutputStream()) { byte[] buffer = new byte[4096]; int length; while ((length = in.read(buffer)) > 0) { out.write(buffer, 0, length); } } }4.2 大文件下载优化
处理大文件时,我发现了几个性能优化点:
- 使用缓冲流减少IO操作
- 合理设置缓冲区大小(通常4KB-8KB最佳)
- 支持断点续传(Range请求)
// 支持断点续传的代码片段 String rangeHeader = request.getHeader("Range"); if (rangeHeader != null) { // 解析Range头并实现部分内容返回 response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // ... 具体实现略 }5. 实际项目中的经验分享
在电商后台管理系统项目中,我遇到了一个棘手问题:导出Excel报表时内存溢出。后来通过流式处理解决了:
- 使用POI的SXSSFWorkbook替代XSSFWorkbook
- 分批次写入数据
- 及时清理临时文件
另一个坑是下载中文文件名在IE浏览器乱码,解决方案是:
String fileName = URLEncoder.encode("中文文件.xlsx", "UTF-8"); response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"; filename*=utf-8''" + fileName);最后给个实用建议:处理文件流时一定要用try-with-resources或者在finally块中关闭流,否则会导致内存泄漏。我就曾经因为忘记关流,导致服务器每隔几天就要重启一次。