下面我将分步提供一个完整、可运行的示例。
核心概念
-
依赖库:
commons-fileupload: 用于解析multipart/form-data类型的请求,这是文件上传所必需的。commons-io: 提供了一些实用的 IO 工具类,可以简化文件操作,虽然不是绝对必需,但强烈推荐使用。
-
Struts 配置 (
struts-config.xml):- 在
struts-config.xml中,你需要配置一个controller元素,并设置processorClass为org.apache.struts.upload.CommonsMultipartRequestHandler,这会告诉 Struts 使用commons-fileupload来处理包含文件的请求。 - 在 Action 的
form-bean定义中,需要将type设置为org.apache.struts.validator.DynaValidatorForm或自定义的ActionForm,并声明一个org.apache.struts.upload.FormFile类型的属性来接收上传的文件。
- 在
-
JSP 表单:
- 表单的
method必须是post。 - 表单的
enctype必须是multipart/form-data,这个属性告诉浏览器要以特殊的方式编码表单数据,以便能够上传文件和文本。
- 表单的
完整步骤示例
第 1 步:添加依赖库
确保你的项目中包含了以下 JAR 文件,如果你使用 Maven,可以在 pom.xml 中添加以下依赖:
<dependencies>
<!-- Struts 1 Core -->
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts-core</artifactId>
<version>1.3.10</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts-taglib</artifactId>
<version>1.3.10</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts-el</artifactId>
<version>1.3.10</version>
</dependency>
<!-- Commons FileUpload and IO for file upload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<!-- Other dependencies like Servlet API, JSTL, etc. -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
如果你不使用 Maven,请手动下载这些 JAR 文件并添加到你的项目的 WEB-INF/lib 目录下。
第 2 步:创建 JSP 上传页面 (upload.jsp)
这个页面包含一个文件输入框和一个提交按钮。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">Struts 1 文件上传示例</title>
</head>
<body>
<h1>上传文件</h1>
<html:form action="/upload" method="post" enctype="multipart/form-data">
请选择文件: <html:file property="myFile" />
<br /><br />
<html:submit value="上传" />
</html:form>
</body>
</html>
关键点:
action="/upload": 对应struts-config.xml中的action-path。enctype="multipart/form-data": 必须设置,否则文件上传会失败。property="myFile": 这个property的值必须与后面 ActionForm 中定义的属性名一致。
第 3 步:配置 struts-config.xml
这是最关键的一步,需要配置 controller 和 form-bean。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.3//EN"
"http://struts.apache.org/dtds/struts-config_1_3.dtd">
<struts-config>
<!-- 1. 配置 Controller,使用 CommonsMultipartRequestHandler 处理文件上传 -->
<controller
processorClass="org.apache.struts.upload.CommonsMultipartRequestHandler"
maxFileSize="10485760" <!-- 10MB -->
contentType="multipart/form-data" />
<!-- 2. 配置 FormBean -->
<form-beans>
<form-bean name="uploadForm"
type="org.apache.struts.upload.FormFileUploadForm" />
</form-beans>
<!-- 3. 配置 ActionMapping -->
<action-mappings>
<action path="/upload"
type="com.example.action.FileUploadAction"
name="uploadForm"
scope="request"
validate="true"
input="/upload.jsp">
<forward name="success" path="/success.jsp" />
<forward name="failure" path="/failure.jsp" />
</action>
</action-mappings>
<!-- Message Resources 配置 -->
<message-resources parameter="com.example.ApplicationResources" />
</struts-config>
关键点:
<controller>: 这是文件上传的核心配置。processorClass: 指定为CommonsMultipartRequestHandler。maxFileSize: (可选) 设置单个文件的最大大小,单位是字节。1048576010MB。contentType: (可选) 明确指定处理multipart/form-data类型。
<form-bean>:name:uploadForm。type: 我们需要创建一个自定义的 ActionForm,这里先写org.apache.struts.upload.FormFileUploadForm。
<action>:name: 指定使用uploadForm这个 FormBean。path:/upload,与 JSP 中的action对应。scope:request或session,通常用request。input: 如果表单验证失败,返回的页面。
第 4 步:创建自定义的 ActionForm (FormFileUploadForm.java)
这个类用于接收表单数据,特别是文件。
package com.example.form;
import org.apache.struts.upload.FormFile;
import org.apache.struts.action.ActionForm;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;
public class FormFileUploadForm extends ActionForm {
private FormFile myFile; // 必须与 JSP 中的 property="myFile" 一致
// Getters and Setters for the FormFile
public FormFile getMyFile() {
return myFile;
}
public void setMyFile(FormFile myFile) {
this.myFile = myFile;
}
// 可以添加其他普通属性,比如文件名描述等
private String description;
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
// 可以在这里添加验证逻辑
@Override
public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
ActionErrors errors = new ActionErrors();
if (myFile == null || myFile.getFileSize() == 0) {
errors.add("myFile", new ActionMessage("error.file.required"));
return errors;
}
// 检查文件类型
String contentType = myFile.getContentType();
if (!"image/jpeg".equals(contentType) && !"image/png".equals(contentType)) {
errors.add("myFile", new ActionMessage("error.file.type.invalid"));
}
return errors;
}
}
关键点:
- 这个类必须继承
ActionForm。 - 属性
myFile的类型必须是org.apache.struts.upload.FormFile。 - 属性名
myFile必须与 JSP 的property和struts-config.xml中 FormBean 的引用完全一致。 FormFile对象包含了文件的所有信息:文件名、内容类型、文件大小和文件内容(字节数组)。
第 5 步:创建 Action 类 (FileUploadAction.java)
Action 类负责处理业务逻辑,包括保存文件。
package com.example.action;
import com.example.form.FormFileUploadForm;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
public class FileUploadAction extends Action {
@Override
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws Exception {
FormFileUploadForm uploadForm = (FormFileUploadForm) form;
org.apache.struts.upload.FormFile file = uploadForm.getMyFile();
// 1. 检查是否有文件上传
if (file == null || file.getFileSize() == 0) {
// 如果没有文件,可以返回错误页面或添加错误信息
// 这里我们直接返回失败
return mapping.findForward("failure");
}
// 2. 获取文件信息
String fileName = file.getFileName();
String contentType = file.getContentType();
long fileSize = file.getFileSize();
InputStream inputStream = file.getInputStream();
// 3. 定义服务器上存储文件的目录
// 为了安全,不要将文件直接放在 WebRoot 下,最好放在 WEB-INF 外部
// 这里为了方便示例,我们放在 WebContent/uploads 目录下
String uploadDir = getServlet().getServletContext().getRealPath("/uploads");
File uploadDirFile = new File(uploadDir);
// 如果目录不存在,则创建
if (!uploadDirFile.exists()) {
uploadDirFile.mkdirs();
}
// 4. 为上传的文件生成一个唯一文件名,防止覆盖
// 使用时间戳 + 原始文件名
String uniqueFileName = new Date().getTime() + "_" + fileName;
File destFile = new File(uploadDir, uniqueFileName);
// 5. 将文件内容写入到服务器
try (FileOutputStream outputStream = new FileOutputStream(destFile)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
// 写入失败,返回失败页面
return mapping.findForward("failure");
} finally {
// 确保流被关闭
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 6. 可以将上传成功的文件信息存入 request 或 session,供后续页面显示
request.setAttribute("uploadedFileName", uniqueFileName);
request.setAttribute("originalFileName", fileName);
request.setAttribute("fileSize", fileSize);
request.setAttribute("contentType", contentType);
// 7. 返回成功
return mapping.findForward("success");
}
}
第 6 步:创建成功和失败页面
success.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">上传成功</title>
</head>
<body>
<h1>文件上传成功!</h1>
<p>原始文件名: <bean:write name="uploadedFileName" scope="request" /></p>
<p>服务器文件名: <bean:write name="originalFileName" scope="request" /></p>
<p>文件大小: <bean:write name="fileSize" scope="request" /> bytes</p>
<p>文件类型: <bean:write name="contentType" scope="request" /></p>
<p>文件已保存在服务器的 "uploads" 目录下。</p>
</body>
</html>
failure.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">上传失败</title>
</head>
<body>
<h1 style="color: red;">文件上传失败!</h1>
<p>请检查文件是否选择,或文件是否符合要求。</p>
<p><a href="javascript:history.back()">返回</a></p>
</body>
</html>
第 7 步:配置 web.xml
确保 web.xml 中正确配置了 Struts 的 ActionServlet 和 filter(如果使用)。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>detail</param-name>
<param-value>2</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
完成以上所有步骤后,你的 Struts 1 文件上传功能就可以工作了。
- 用户访问
upload.jsp。 - 用户选择文件并点击“上传”按钮。
- 请求发送到
FileUploadAction。 - Struts 框架 根据
struts-config.xml的配置,使用CommonsMultipartRequestHandler解析请求,将文件数据填充到FormFileUploadForm的myFile属性中。 FileUploadAction从FormFile中获取文件流,并将其写入到服务器的uploads目录。- 根据执行结果,Action 转发到
success.jsp或failure.jsp。
注意事项
- 安全性:
- 文件名: 不要直接使用用户提供的文件名,因为它可能包含恶意字符或路径(如
../../../malicious.txt),建议重命名文件,如使用 UUID 或时间戳。 - 文件类型: 检查文件的
Content-Type和文件扩展名,防止上传可执行文件(如.jsp,.php,.exe)。 - 文件大小: 在
struts-config.xml中设置maxFileSize限制,防止上传过大的文件导致服务器资源耗尽。
- 文件名: 不要直接使用用户提供的文件名,因为它可能包含恶意字符或路径(如
- 目录权限: 确保 Web 服务器对
uploads目录有写入权限。 - 性能: 上传大文件会消耗较多内存和 CPU,请合理配置服务器和上传限制。
- Struts 2 的区别: Struts 2 的文件上传机制更简单,它内置了对文件上传的支持,并且使用了更现代的
interceptor(拦截器)模式,与 Struts 1 的RequestHandler方式完全不同,如果你现在开始新项目,强烈建议使用 Struts 2 或更现代的框架如 Spring MVC。
