核心概念
-
依赖库:
(图片来源网络,侵删)- struts-core.jar: Struts 1 框架的核心库。
- commons-fileupload.jar: Apache Commons FileUpload 库,用于解析
multipart/form-data请求。 - commons-io.jar: Apache Commons IO 库,提供了一些实用的 I/O 工具类(虽然不是绝对必须,但强烈推荐)。
- commons-logging.jar: 日志库,Struts 依赖它。
-
ActionForm 的特殊处理:
- 用于文件上传的
ActionForm必须继承org.apache.struts.action.ActionForm。 - 在
Form中,你需要定义一个org.apache.struts.upload.FormFile类型的属性来接收上传的文件。 - 关键点: Struts 1 的控制器 (
ActionServlet) 会自动检测ActionForm中是否有FormFile类型的属性,如果存在,它会使用commons-fileupload来解析请求,并将文件内容封装到FormFile对象中。
- 用于文件上传的
-
FormFile接口:- 这是 Struts 1 封装上传文件信息的核心接口。
- 常用方法:
String getFileName(): 获取原始文件名 (my-photo.jpg)。String getContentType(): 获取文件的 MIME 类型 (image/jpeg)。byte[] getFileData(): 获取文件的字节数组。InputStream getInputStream(): 获取文件的输入流。long getFileSize(): 获取文件的大小(字节)。
完整示例:实现一个文件上传功能
假设我们要创建一个简单的页面,允许用户上传一张图片,并显示上传结果。
第 1 步:添加依赖库
将以下 JAR 文件添加到你的 WEB-INF/lib 目录下:
struts-1.3.10.jar(或你使用的版本)commons-fileupload-1.4.jar(或更高版本)commons-io-2.11.0.jar(或更高版本)commons-logging-1.2.jar
第 2 步:创建上传表单页面 (upload.jsp)
这个页面包含一个文件输入框和一个提交按钮,注意 enctype="multipart/form-data" 属性,这是文件上传所必需的。
<%@ 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>
<h2>请选择要上传的图片文件</h2>
<!--
enctype="multipart/form-data" 是文件上传的必需属性
action="/struts-file-upload/fileUpload.do" 对应 struts-config.xml 中的 path
-->
<html:form action="/fileUpload" enctype="multipart/form-data">
<table>
<tr>
<td>选择文件:</td>
<td>
<!-- html:file 标签会自动渲染为文件输入框 -->
<html:file property="myFile" />
</td>
</tr>
<tr>
<td colspan="2" align="center">
<html:submit value="上传" />
</td>
</tr>
</table>
</html:form>
</body>
</html>
第 3 步:创建 ActionForm (UploadForm.java)
这个 Form 类将接收来自 upload.jsp 的数据,我们定义一个 FormFile 类型的属性 myFile,其名称必须与 JSP 中 html:file 标签的 property 属性值一致。
package com.example.form;
import org.apache.struts.action.ActionForm;
import org.apache.struts.upload.FormFile;
/**
* 文件上传的 ActionForm
*/
public class UploadForm extends ActionForm {
private static final long serialVersionUID = 1L;
// FormFile 属性,用于接收上传的文件
// 属性名 "myFile" 必须与 JSP 中 <html:file property="myFile"> 的 property 值一致
private FormFile myFile;
// 可以添加其他普通属性,比如文件描述等
private String description;
// getter 和 setter 方法
public FormFile getMyFile() {
return myFile;
}
public void setMyFile(FormFile myFile) {
this.myFile = myFile;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
第 4 步:创建 Action (FileUploadAction.java)
这是处理上传逻辑的核心,我们将从 FormFile 中获取文件数据,并将其保存到服务器的指定目录。
package com.example.action;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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 org.apache.struts.upload.FormFile;
import com.example.form.UploadForm;
/**
* 处理文件上传的 Action
*/
public class FileUploadAction extends Action {
@Override
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws Exception {
// 1. 将 Form 转换为自定义的 UploadForm
UploadForm uploadForm = (UploadForm) form;
// 2. 从 Form 中获取 FormFile 对象
FormFile file = uploadForm.getMyFile();
// 3. 检查是否有文件被选择
if (file == null || file.getFileSize() == 0) {
request.setAttribute("errorMessage", "请选择一个文件!");
return mapping.findForward("failure");
}
// 4. 获取文件信息
String fileName = file.getFileName();
long fileSize = file.getFileSize();
String contentType = file.getContentType();
System.out.println("正在上传文件: " + fileName);
System.out.println("文件大小: " + fileSize + " bytes");
System.out.println("文件类型: " + contentType);
// 5. 定义文件在服务器上的保存路径
// 为了安全,不要将文件保存在 Web 应用根目录下
String uploadDir = getServlet().getServletContext().getRealPath("/uploads");
// 确保上传目录存在
File dir = new File(uploadDir);
if (!dir.exists()) {
dir.mkdirs();
}
// 构建服务器上的完整文件路径
String serverPath = uploadDir + File.separator + fileName;
// 6. 将文件内容写入服务器
try (InputStream inputStream = file.getInputStream();
OutputStream outputStream = new FileOutputStream(new File(serverPath))) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
System.out.println("文件已成功保存到: " + serverPath);
} catch (IOException e) {
e.printStackTrace();
request.setAttribute("errorMessage", "文件上传失败: " + e.getMessage());
return mapping.findForward("failure");
}
// 7. 设置成功消息,并转发到成功页面
request.setAttribute("successMessage",
"文件 '" + fileName + "' 上传成功!大小: " + (fileSize / 1024) + " KB。");
return mapping.findForward("success");
}
}
第 5 步:配置 struts-config.xml
这是连接所有组件的关键,我们需要配置 ActionForm、Action 以及它们之间的映射。
<?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>
<!-- ========== Form Bean Definitions ========== -->
<form-beans>
<!--
name: 在 Action 中使用的 Form 名称
type: 对应的 Form 类的全限定名
-->
<form-bean name="uploadForm" type="com.example.form.UploadForm" />
</form-beans>
<!-- ========== Global Forward Definitions ========== -->
<global-forwards>
<!-- 可以定义一些全局的转发,比如首页 -->
<forward name="welcome" path="/upload.jsp" />
</global-forwards>
<!-- ========== Action Mapping Definitions ========== -->
<action-mappings>
<!--
path: 请求的 URL 路径,对应 JSP 中的 action="/fileUpload"
type: 处理该请求的 Action 类的全限定名
name: 使用的 Form Bean 的名称
scope: Form Bean 的作用域 (request 或 session)
validate: 是否在调用 Action 的 execute 方法前调用 Form 的 validate 方法
input: 如果验证失败,返回的页面
forward: 一个简单的转发
-->
<action path="/fileUpload"
type="com.example.action.FileUploadAction"
name="uploadForm"
scope="request"
validate="false"
input="/upload.jsp">
<!-- 定义 Action 的内部转发 -->
<forward name="success" path="/success.jsp" />
<forward name="failure" path="/upload.jsp" />
</action>
</action-mappings>
<!-- ========== Message Resources Definitions ========== -->
<message-resources parameter="com.example.ApplicationResources" />
</struts-config>
第 6 步:创建成功和失败页面
success.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">上传成功</title>
</head>
<body>
<h2>上传结果</h2>
<hr>
<p style="color: green; font-weight: bold;">
${successMessage}
</p>
<hr>
<p>
<html:link page="/upload.jsp">返回上传页面</html:link>
</p>
</body>
</html>
upload.jsp (修改失败提示部分)
在 upload.jsp 的表单上方添加一个错误显示区域:
<!-- 在 <body> 标签后添加 -->
<c:if test="${not empty errorMessage}">
<p style="color: red; font-weight: bold;">
${errorMessage}
</p>
</c:if>
注意:你需要在页面顶部添加 JSTL 核心库的 taglib 指令:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
第 7 步:配置 web.xml
确保 ActionServlet 已经正确配置,struts-config.xml 被加载。
<?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">
<!-- Struts 1 的核心控制器 -->
<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>
<!-- 将所有 .do 请求交给 ActionServlet 处理 -->
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
重要注意事项
-
文件大小限制:
- 默认情况下,
commons-fileupload对上传文件的大小有限制(通常为 250MB),如果上传的文件超过这个限制,会抛出SizeLimitExceededException。 - 你可以通过修改
struts-config.xml中的controller元素来调整这个限制。 - 在
<struts-config>根标签下添加:<controller maxFileSize="10MB" maxFileSize="10MB" bufferSize="4096" />maxFileSize: 单个文件的最大大小。maxFileSize: 整个请求(所有文件)的最大大小。bufferSize: 用于读取文件的缓冲区大小。
- 默认情况下,
-
安全性和性能:
- 文件名安全: 不要直接使用用户提供的文件名,它可能包含恶意路径(如
../../malicious.txt),你应该对文件名进行清理,例如只保留文件名部分,并生成一个唯一的文件名来避免覆盖。 - 存储位置: 不要将文件存储在 Web 应用可以直接访问的目录下(如
webapp/uploads),否则用户可以直接通过 URL 访问上传的文件,最佳实践是存储在WEB-INF之外的某个安全位置,并通过一个专门的 Servlet 或 Action 来提供文件下载服务,并在此过程中进行权限验证。 - 内存使用:
FormFile.getFileData()会将整个文件加载到内存中,对于非常大的文件,这可能会导致OutOfMemoryError,更安全的方式是使用FormFile.getInputStream()和OutputStream逐块读写文件,如示例代码所示。
- 文件名安全: 不要直接使用用户提供的文件名,它可能包含恶意路径(如
-
commons-fileupload版本:- Struts 1 依赖于
commons-fileupload,确保你使用的commons-fileupload版本与 Struts 1 版本兼容,较新的 Struts 1 版本通常支持较新的commons-fileupload版本。
- Struts 1 依赖于
这个示例涵盖了 Struts 1 文件上传的所有核心步骤,你可以基于此进行扩展和优化。
