跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava

Servlet 原生 Response 实现文件下载

Servlet 响应下载文件主要通过设置 Content-Disposition 头信息并写入响应流实现。示例展示了如何通过 doGet 方法读取本地文件字节流,经缓冲输出流发送至客户端。关键点包括正确配置响应编码、处理中文文件名乱码问题以及 IO 流的关闭管理。相比直接链接,服务端控制能增加权限校验等扩展能力。

XiaoPingzi发布于 2025/1/19更新于 2026/7/2435 浏览
Servlet 原生 Response 实现文件下载

Servlet 原生 Response 实现文件下载

在 Web 开发中,有时候我们需要控制文件的下载行为,比如增加权限校验、记录日志或者处理中文文件名乱码等问题。这时候直接通过 HTML <a> 标签指向资源就不够用了,得靠后端 Servlet 配合 HttpServletResponse 来接管响应流。

前端测试页面

先准备一个简单的 HTML 页面,模拟用户点击下载链接的场景。这里对比了两种调用方式:一种是直接访问静态资源路径,另一种是走 Servlet 接口。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>文件下载测试</title>
</head>
<body>
    <h1>使用 a 标签直接指向服务器上的资源</h1>
    <a href="/download/a.flv">a.flv</a><br>
    <a href="/download/a.jpg">a.jpg</a><br>
    <a href="/download/a.mp3">a.mp3</a><br>
    <a href="/download/a.mp4">a.mp4</a><br>
    <a href="/download/a.txt">a.txt</a><br>
    <a href="/download/a.zip">a.zip</a><br>

    <h1>使用服务器端编码的方式实现文件下载</h1>
    <a href="/web1/downloadServlet?filename=a.flv">a.flv</a><br>
    <a href="/web1/downloadServlet?filename=a.jpg">a.jpg</a><br>
    <a href="/web1/downloadServlet?filename=a.mp3">a.mp3</a><br>
    <a href="/web1/downloadServlet?filename=a.mp4">a.mp4</a><br>
    <a href="/web1/downloadServlet?filename=a.txt">a.txt</a><br>
    <a href="/web1/downloadServlet?filename=a.zip">a.zip</a><br>
    <!-- 注意中文文件名需要 URL 编码 -->
    <a href="/web1/downLoadServlet2?filename=%E7%BE%8E%E5%A5%B3.jpg">美女.jpg</a><br>
</body>
</html>

Servlet 核心实现

接下来看核心的 Java 代码。这里用到了 ServletOutputStream 将文件字节流写入响应体,并通过设置 Header 告诉浏览器以附件形式打开。

package com.xiangshuai.servlet;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class DownloadServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        
        // 生产环境建议从配置文件或环境变量读取,避免硬编码路径
        String con = "F:/download"; 
        String filename = request.getParameter("filename");
        
        // 设置请求和响应的字符编码,防止中文乱码
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("UTF-8");
        
        // 关键:Content-Disposition 头信息
        // attachment 表示强制下载,inline 表示尝试在浏览器内打开
        // filename 参数指定下载后的文件名,中文需进行 URL 编码
        try {
            String encodedFilename = URLEncoder.encode(filename, "UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + encodedFilename);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        ServletOutputStream os = response.getOutputStream();

        try {
            // 构建文件对象并创建输入流
            File file = new File(con + "/" + filename);
            if (!file.exists()) {
                throw new IOException("File not found: " + filename);
            }
            bis = new BufferedInputStream(new FileInputStream(file));
            bos = new BufferedOutputStream(os);

            byte[] b = new byte[1024];
            int len = -1;
            while ((len = bis.read(b)) != -1) {
                bos.write(b, 0, len);
                bos.flush();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // IO 流必须关闭,否则可能导致连接池耗尽或内存泄漏
            if (bos != null) {
                try { bos.close(); } catch (IOException e) { e.printStackTrace(); }
            }
            if (bis != null) {
                try { bis.close(); } catch (IOException e) { e.printStackTrace(); }
            }
        }
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}

关键点解析

1. Content-Disposition 的作用

这个响应头是控制浏览器行为的关键。当设置为 attachment 时,浏览器会弹出保存对话框而不是直接在窗口渲染内容。如果类型是图片但设置了这个头,浏览器也会提示下载而不是显示图片。

2. 中文文件名处理

代码中使用了 URLEncoder.encode 对文件名进行了编码。如果不处理,包含中文的文件名在某些浏览器下会变成乱码(如 %E7%BE%8E...),导致下载后无法识别。虽然现代浏览器兼容性好了很多,但在企业级应用中加上这一步更稳妥。

3. 资源清理

IO 流操作最忌讳的就是忘记关闭。我们在 finally 块中检查流是否为空并执行 close()。这不仅能释放系统资源,还能确保缓冲区数据被彻底刷新到客户端。

4. 路径问题

示例中写死了 F:/download,这在 Windows 上没问题,但换到 Linux 服务器就会报错。实际项目中建议使用 ServletContext.getRealPath("/upload") 或者配置类注入的路径常量,保证跨平台兼容性。

通过这种方式,我们不仅实现了文件下载,还预留了后续添加权限验证、流量统计等扩展功能的入口。

目录

  1. Servlet 原生 Response 实现文件下载
  2. 前端测试页面
  3. Servlet 核心实现
  4. 关键点解析
  5. 1. Content-Disposition 的作用
  6. 2. 中文文件名处理
  7. 3. 资源清理
  8. 4. 路径问题
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • DeepSeek-R1-Distill-Llama-8B 优化技巧:提升文本生成质量
  • 基于开源鸿蒙(OpenHarmony)的【智能家居综合应用】系统
  • 前端技术演进趋势与百道场景化面试题精选
  • Java transient 关键字详解与 Flink State 实践
  • AI 时代的前端:不再切图,转向智能交互架构
  • AI 绘画提示词引导系数设置指南:从原理到实践
  • ClawPanel v4.4.0 发布:AI 智能助手与模型兼容性修复
  • Java 反射详解
  • 企业构建行业大模型的策略与实施路径
  • OpenClaw 生态 16 款 AI Agent 选型指南
  • VS Code 远程连接服务器后 Github Copilot 无法使用
  • Python 核心数据结构:集合与冻结集合
  • JavaScript 集合 (Set、WeakSet) 与映射 (Map、WeakMap)
  • 基于 Nexent 平台搭建育儿问答智能体实战
  • AG-UI:构建 AI 前端交互的统一协议
  • C++ 类和对象:默认成员函数详解
  • OpenCV 并行处理与构建配置指南
  • Java 并发核心:单例模式、生产者消费者、定时器及线程池实现
  • 基于 MCP 协议的图片素描风格转换工具使用指南
  • OpenAI 集成 Langchain 操作实战详解

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online