杰瑞科技汇

Struts2下载文件如何实现?配置与代码要点?

核心概念:stream 结果类型

Struts2 的 stream 结果类型会将 Action 中某个属性(通常是 InputStream)的值直接写入到 HttpServletResponse 的输出流中,从而将文件发送给客户端。

Struts2下载文件如何实现?配置与代码要点?-图1
(图片来源网络,侵删)

它的主要配置参数有:

参数 必需 描述
contentType 文件的 MIME 类型,application/pdf, image/jpeg,浏览器会根据此类型决定如何处理文件(如内联显示或下载)。
contentLength 文件的大小(字节),这有助于浏览器显示下载进度条。
contentDisposition 控制文件是内联显示还是作为附件下载。这是实现下载的关键,通常设置为 attachment; filename="文件名"
inputName 指定 Action 中哪个属性作为 InputStream 的来源,默认值是 inputStream
bufferSize 读写缓冲区大小,默认为 1024 字节。

完整示例:实现文件下载

假设我们要实现一个下载服务器上文件的页面。

步骤 1:准备工作

  1. 创建 Web 项目:确保你的项目已经集成了 Struts2,如果你不确定,可以检查 web.xml 中是否有 Struts2 的 Filter 配置。
  2. 准备下载文件:在你的 Web 项目中,创建一个目录来存放需要下载的文件,在 WebContent (或 webapp) 目录下创建一个 files 文件夹,并放入一个名为 test.txt 的文件。
    • 项目结构大致如下:
      your-project/
      ├── src/
      │   └── com/
      │       └── example/
      │           └── action/
      │               └── DownloadAction.java
      ├── WebContent/
      │   ├── files/
      │   │   └── test.txt  <-- 这是我们要下载的文件
      │   ├── index.jsp      <-- 下载页面
      │   └── struts.xml     <-- Struts2 核心配置文件
      └── WEB-INF/
          └── web.xml

步骤 2:创建 Action 类

Action 类的核心任务是提供文件的 InputStream,我们可以通过多种方式获取这个流,这里演示最常见的方式:从服务器的文件系统读取。

src/com/example/action/DownloadAction.java

Struts2下载文件如何实现?配置与代码要点?-图2
(图片来源网络,侵删)
package com.example.action;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class DownloadAction extends ActionSupport {
    // 1. 定义一个 InputStream 属性,用于存放文件流
    // Struts2 的 stream 结果类型会默认读取名为 "inputStream" 的属性
    private InputStream inputStream;
    // 2. 定义文件名,用于在下载对话框中显示
    private String fileName;
    // Struts2 会自动调用这个方法来获取输入流
    // 你也可以不实现这个方法,而是在 execute 中直接给 inputStream 赋值
    public InputStream getInputStream() throws Exception {
        // 获取要下载的文件在服务器上的真实路径
        String filePath = ServletActionContext.getServletContext().getRealPath("/files/test.txt");
        // 设置下载时显示的文件名(可以和原文件名不同)
        // 注意:处理中文文件名,防止乱码
        fileName = "下载的测试文件.txt";
        // 创建 FileInputStream 对象
        inputStream = new FileInputStream(new File(filePath));
        return inputStream;
    }
    // 可选:提供一个 getter 方法,以便在 JSP 中获取文件名
    // 这样可以在 JSP 中动态设置 content-disposition
    public String getFileName() {
        return fileName;
    }
    // 在 Action 执行完毕后,确保流被关闭
    @Override
    public String execute() throws Exception {
        // getInputStream() 方法会在 stream 结果类型处理时被调用
        // 所以这里我们只需要返回 SUCCESS
        return SUCCESS;
    }
    // 确保在 Action 销毁时关闭流
    @Override
    public void destroy() {
        try {
            if (inputStream != null) {
                inputStream.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

步骤 3:配置 struts.xml

这是最关键的一步,我们需要告诉 Struts2 当返回 SUCCESS 时,使用 stream 结果类型来处理。

WebContent/struts.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
    "http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
    <package name="default" namespace="/" extends="struts-default">
        <!-- 定义下载 Action -->
        <action name="download" class="com.example.action.DownloadAction">
            <!-- 
              指定当 execute() 方法返回 "SUCCESS" 时,使用 stream 结果类型
            -->
            <result name="success" type="stream">
                <!-- 
                  指定 Action 中哪个属性是输入流。
                  默认就是 "inputStream",所以这里可以省略。
                -->
                <param name="inputName">inputStream</param>
                <!-- 
                  设置文件的内容类型。
                  对于文本文件,通常是 text/plain。
                  对于 PDF,是 application/pdf。
                  对于图片,是 image/jpeg 等。
                  如果不确定,可以使用 application/octet-stream (通用二进制流)。
                -->
                <param name="contentType">text/plain</param>
                <!-- 
                  设置下载时的文件名。
                  这里我们通过 EL 表达式从 Action 的 getFileName() 方法获取。
                  注意:这里使用了 OGNL 表达式 ${fileName}。
                -->
                <param name="contentDisposition">attachment; filename="${fileName}"</param>
                <!-- 
                  设置缓冲区大小,可选。
                -->
                <param name="bufferSize">1024</param>
            </result>
        </action>
    </package>
</struts>

步骤 4:创建下载页面

创建一个简单的 JSP 页面,上面放置一个下载链接。

WebContent/index.jsp

Struts2下载文件如何实现?配置与代码要点?-图3
(图片来源网络,侵删)
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">Struts2 文件下载示例</title>
</head>
<body>
    <h2>Struts2 文件下载</h2>
    <p>点击下面的链接下载文件:</p>
    <!-- 
      s:url 标签用于生成 Action 的 URL。
      s:a 标签用于生成一个超链接。
    -->
    <s:url var="downloadUrl" action="download"/>
    <s:a href="%{downloadUrl}">下载 test.txt 文件</s:a>
</body>
</html>

步骤 5:部署和测试

  1. 将项目部署到 Tomcat 或其他 Servlet 容器中。
  2. 启动服务器,访问 http://localhost:8080/your-project-name/index.jsp
  3. 点击 "下载 test.txt 文件" 链接。
  4. 浏览器应该会弹出文件下载对话框,提示你保存名为 "下载的测试文件.txt" 的文件。

高级主题与常见问题

处理中文文件名乱码

不同浏览器处理中文文件名的方式不同,直接设置 filename="中文.txt" 可能会出现乱码,我们需要在 Action 中根据请求的 User-Agent 来进行编码。

修改 DownloadAction 中的 getFileName() 方法:

import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.interceptor.ServletRequestAware; // 实现 Aware 接口
public class DownloadAction extends ActionSupport implements ServletRequestAware {
    // ... 其他代码 ...
    private HttpServletRequest request;
    @Override
    public void setServletRequest(HttpServletRequest request) {
        this.request = request;
    }
    public String getFileName() throws Exception {
        String originalFileName = "测试文件.txt";
        // 获取请求头中的 User-Agent
        String userAgent = request.getHeader("USER-AGENT");
        // 对文件名进行编码
        if (userAgent != null) {
            userAgent = userAgent.toLowerCase();
            if (userAgent.contains("msie") || userAgent.contains("trident")) { // IE
                fileName = java.net.URLEncoder.encode(originalFileName, "UTF-8");
            } else if (userAgent.contains("mozilla")) { // Firefox, Chrome
                fileName = new String(originalFileName.getBytes("UTF-8"), "ISO-8859-1");
            } else { // 其他
                fileName = java.net.URLEncoder.encode(originalFileName, "UTF-8");
            }
        }
        return fileName;
    }
    // ... 其他代码 ...
}

下载动态生成的文件

如果你需要下载的不是服务器上已存在的文件,而是动态生成的(如报表、图片等),流程类似,只是获取 InputStream 的方式不同。

生成一个 CSV 文件:

import java.io.ByteArrayInputStream;
public class DynamicDownloadAction extends ActionSupport {
    private InputStream inputStream;
    @Override
    public String execute() throws Exception {
        // 动态生成文件内容
        String csvContent = "姓名,年龄\n张三,25\n李四,30";
        // 将字符串转换为 InputStream
        inputStream = new ByteArrayInputStream(csvContent.getBytes("UTF-8"));
        // 设置文件名
        fileName = "用户列表.csv";
        return SUCCESS;
    }
    // ... getter 和 setter ...
}

下载大文件时的内存问题

对于非常大的文件,使用 FileInputStream 是没有问题的,因为它使用了操作系统的文件映射机制,不会一次性将整个文件读入内存,但如果你的文件内容是动态生成在内存中的(如上面的 ByteArrayInputStream),并且文件很大,这可能会导致 OutOfMemoryError

对于大文件,最佳实践是将其先写入到服务器的临时文件中,然后提供一个 FileInputStream 来读取它,下载完成后,记得删除这个临时文件。


实现 Struts2 文件下载的核心三要素:

  1. Action:提供一个 InputStream 类型的属性,并为其提供 getter 方法,getter 方法负责打开文件流。
  2. struts.xml:配置 Action 的 resultstream 类型,并设置 inputNamecontentTypecontentDisposition 等参数。
  3. JSP:使用 <s:url><s:a> 标签创建一个指向 Action 的下载链接。

通过以上步骤,你就可以轻松地在 Struts2 应用中实现稳定、可靠的文件下载功能。

分享:
扫描分享到社交APP
上一篇
下一篇