凌峰创科服务平台

Struts2如何实现图片上传到服务器?

核心概念

  1. 表单: HTML 表单必须设置 enctype="multipart/form-data",这是浏览器能正确发送文件数据的关键。
  2. Struts2 拦截器: fileUpload 拦截器会自动处理 multipart/form-data 请求,它会解析请求,将文件内容、文件名等信息封装到 Action 对象的相应属性中。
  3. Action 属性: 你需要在 Action 类中定义三个属性:
    • 一个 File 类型:用于接收上传的文件内容。
    • 一个 String 类型:用于接收原始的文件名。
    • 一个 File[]List<File> 类型:用于支持多文件上传(可选)。
  4. 配置: 在 struts.xml 中配置 Action,并确保 fileUpload 拦截器在栈中(它默认在 defaultStack 中)。
  5. 服务器存储: Action 接收到文件后,需要使用 Java I/O 操作(如 Files.copy())将其从临时目录移动到你指定的服务器目标目录。

详细步骤

第 1步:项目环境准备

确保你的 Struts2 项目已经搭建好,并且包含了必要的 Struts2 核心依赖,如果你使用 Maven,pom.xml 中至少需要包含:

Struts2如何实现图片上传到服务器?-图1
(图片来源网络,侵删)
<dependencies>
    <!-- Struts2 核心包 -->
    <dependency>
        <groupId>org.apache.struts</groupId>
        <artifactId>struts2-core</artifactId>
        <version>2.5.33</version> <!-- 使用一个稳定的版本 -->
    </dependency>
    <!-- 为了更好的文件上传体验,建议添加 commons-io 和 commons-fileupload -->
    <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>
</dependencies>

第 2步:创建上传页面 (upload.jsp)

WebContent (或 webapp) 目录下创建一个 JSP 页面,用于显示上传表单。

<%@ 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>
<style>
    body { font-family: Arial, sans-serif; margin: 20px; }
    .upload-form { border: 1px solid #ccc; padding: 20px; width: 500px; }
    .form-group { margin-bottom: 15px; }
    .form-group label { display: block; margin-bottom: 5px; }
    .form-group input[type="file"] { width: 100%; }
    .form-group input[type="submit"] { padding: 10px 15px; background-color: #007bff; color: white; border: none; cursor: pointer; }
</style>
</head>
<body>
    <h2>上传图片</h2>
    <div class="upload-form">
        <!-- 
            1. method="post": 必须使用 POST 方法。
            2. enctype="multipart/form-data": 必须设置,用于上传文件。
        -->
        <s:form action="uploadImage" method="post" enctype="multipart/form-data">
            <div class="form-group">
                <label for="myImage">选择图片:</label>
                <!-- 
                    name 属性必须与 Action 中的 File 类型属性名完全一致。
                    accept 属性用于限制文件类型,是前端的一个友好提示。
                -->
                <s:file name="myImage" accept="image/*" label="选择图片"/>
            </div>
            <div class="form-group">
                <s:submit value="上传"/>
            </div>
        </s:form>
    </div>
</body>
</html>

第 3步:创建 Action 类 (UploadImageAction.java)

这是处理上传逻辑的核心,Action 会接收文件,并将其保存到服务器的指定目录。

package com.example.action;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.apache.commons.io.FileUtils;
import com.opensymphony.xwork2.ActionSupport;
public class UploadImageAction extends ActionSupport {
    // 1. 接收上传的文件 (与 JSP 中 s:file 的 name 属性对应)
    private File myImage;
    // 2. 接收原始文件名
    private String myImageFileName;
    // 3. 接收文件类型 (可选)
    private String myImageContentType;
    // 服务器上保存文件的根目录 ( WebContent/uploads/)
    // 注意: 在实际部署中,最好将文件保存在 WebContent 之外的目录,"D:/uploads"
    // 这里为了方便演示,我们放在 WebContent 下。
    private static final String SAVE_DIR = "uploads/";
    @Override
    public String execute() {
        // 检查是否有文件被选择
        if (myImage == null) {
            addActionError("请选择一个文件!");
            return INPUT;
        }
        try {
            // 创建服务器上的保存目录 (如果不存在)
            Path uploadPath = Paths.get(getServletContext().getRealPath("") + SAVE_DIR);
            if (!Files.exists(uploadPath)) {
                Files.createDirectories(uploadPath);
            }
            // 构建目标文件路径
            Path destination = Paths.get(uploadPath.toString(), myImageFileName);
            // 使用 Commons-IO 的 copy 方法将文件从临时位置复制到目标位置
            // Files.copy(myImage.toPath(), destination, StandardCopyOption.REPLACE_EXISTING);
            FileUtils.copyFile(myImage, destination.toFile());
            System.out.println("文件上传成功!");
            System.out.println("原始文件名: " + myImageFileName);
            System.out.println("文件类型: " + myImageContentType);
            System.out.println("保存路径: " + destination.toString());
            return SUCCESS; // 返回成功
        } catch (IOException e) {
            e.printStackTrace();
            addActionError("文件上传失败: " + e.getMessage());
            return ERROR; // 返回失败
        }
    }
    // --- Getters and Setters ---
    // Struts2 拦截器需要通过 setter 来设置这些属性
    public File getMyImage() {
        return myImage;
    }
    public void setMyImage(File myImage) {
        this.myImage = myImage;
    }
    public String getMyImageFileName() {
        return myImageFileName;
    }
    public void setMyImageFileName(String myImageFileName) {
        this.myImageFileName = myImageFileName;
    }
    public String getMyImageContentType() {
        return myImageContentType;
    }
    public void setMyImageContentType(String myImageContentType) {
        this.myImageContentType = myImageContentType;
    }
    // 为了获取 ServletContext,我们需要在 struts.xml 中注入
    private javax.servlet.ServletContext servletContext;
    public void setServletContext(javax.servlet.ServletContext servletContext) {
        this.servletContext = servletContext;
    }
    public javax.servlet.ServletContext getServletContext() {
        return servletContext;
    }
}

重要说明:

  • myImage: File 对象指向的是 Tomcat 等服务器在内存中创建的临时文件,当 execute() 方法执行完毕后,这个临时文件会被自动删除,所以你必须将它复制或移动到其他地方。
  • myImageFileName: 客户端选择的原始文件名(如 "my-photo.jpg")。
  • ServletContext 注入**: 为了获取 Web 应用的真实路径 (getServletContext().getRealPath("")),我们需要在struts.xml中配置params拦截器来注入ServletContext`。

第 4步:配置 struts.xml

src/main/resources 目录下的 struts.xml 文件中配置你的 Action。

Struts2如何实现图片上传到服务器?-图2
(图片来源网络,侵删)
<?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>
    <constant name="struts.devMode" value="true" />
    <package name="default" namespace="/" extends="struts-default">
        <!-- 
            配置 uploadImage Action
            1. name: 与 JSP 中 s:form 的 action 属性对应。
            2. class: 对应的 Action 类全名。
            3. method: 指定调用的方法,默认是 execute。
        -->
        <action name="uploadImage" class="com.example.action.UploadImageAction" method="execute">
            <!-- 
                注入 ServletContext,以便 Action 可以获取 Web 应用根目录。
                这个配置需要放在 defaultStack 之前,或者确保 params 拦截器在它之前被执行。
            -->
            <interceptor-ref name="params">
                <param name="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servletRequest\..*,^servletResponse\..*</param>
            </interceptor-ref>
            <!-- 
                fileUpload 拦截器默认在 defaultStack 中,所以不需要显式配置。
                但你可以在这里覆盖它的参数,例如设置最大允许上传的文件大小。
            -->
            <interceptor-ref name="fileUpload">
                <param name="allowedTypes">image/jpeg, image/png, image/gif, image/bmp</param>
                <param name="maximumSize">1048576</param> <!-- 1MB -->
            </interceptor-ref>
            <!-- 再次引用默认栈,因为它包含了 fileUpload 等其他必要拦截器 -->
            <interceptor-ref name="defaultStack"></interceptor-ref>
            <!-- 结果配置 -->
            <result name="success">/success.jsp</result>
            <result name="input">/upload.jsp</result> <!-- 输入验证失败时返回上传页面 -->
            <result name="error">/error.jsp</result>
        </action>
    </package>
</struts>

第 5步:创建结果页面

  1. 成功页面 (success.jsp)

    <%@ 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">上传成功</title>
    </head>
    <body>
        <h2>上传成功!</h2>
        <p>文件名: <s:property value="myImageFileName"/></p>
        <p>文件类型: <s:property value="myImageContentType"/></p>
        <p>你可以在这里显示上传的图片:</p>
        <img src="<s:property value="'uploads/' + myImageFileName"/>" alt="上传的图片" style="max-width: 300px;"/>
    </body>
    </html>
  2. 错误页面 (error.jsp)

    <%@ 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">上传失败</title>
    </head>
    <body>
        <h2 style="color: red;">上传失败!</h2>
        <s:property value="actionErrors"/>
        <p><a href="javascript:history.back()">返回上传页面</a></p>
    </body>
    </html>

运行和测试

  1. 部署项目: 将你的项目部署到 Tomcat 或其他支持 Servlet 的容器中。
  2. 启动服务器
  3. 访问页面: 在浏览器中访问 http://localhost:8080/你的项目名/upload.jsp
  4. 上传文件: 选择一张图片(.jpg.png 文件),点击“上传”按钮。
  5. 查看结果:
    • 如果成功,页面会跳转到 success.jsp,并显示上传的图片。
    • 如果失败(例如文件类型不符或超过大小限制),页面会跳转到 error.jspupload.jsp,并显示错误信息。

总结与最佳实践

  • 文件大小和类型: fileUpload 拦截器是文件上传的第一道防线,通过配置 maximumSizeallowedTypes 可以有效阻止非法或过大的文件上传。
  • 安全性: 永远不要直接使用用户上传的文件名,恶意用户可能提交包含 的文件名,用于路径遍历攻击,你应该对文件名进行清理和重命名,例如使用 UUID 或时间戳。
    // 在 execute 方法中,修改文件名
    String fileExtension = myImageFileName.substring(myImageFileName.lastIndexOf("."));
    String newFileName = UUID.randomUUID().toString() + fileExtension;
    Path destination = Paths.get(uploadPath.toString(), newFileName);
  • 存储位置: 不要将文件存放在 WebContent 目录下,因为它会被直接暴露在公网上,更好的做法是存放在应用服务器之外的目录,并通过一个专门的 Servlet 或 Action 来提供文件下载服务,这样可以进行权限控制。
  • 多文件上传: 如果需要支持多文件上传,将 Action 中的属性改为 List<File> myImagesList<String> myImageFileNames,并在 JSP 中使用 <s:file name="myImages" ... multiple="multiple"/>

通过以上步骤,你就可以在 Struts2 应用中稳定、安全地实现图片上传功能了。

Struts2如何实现图片上传到服务器?-图3
(图片来源网络,侵删)
分享:
扫描分享到社交APP
上一篇
下一篇