Java Struts2

Struts2 – Upload Image, Save to Folder, Store Path in MySQL Database

Struts2 – Upload Image, Save to Folder, Store Path in MySQL Database

This tutorial shows how to upload a user profile image in Struts2: the image file is saved to a server folder with a unique renamed filename, and the file path along with the user's form data is stored in a MySQL database. The key Struts2 feature is the file upload interceptor — it automatically handles multipart/form-data and exposes three fields on the action for the uploaded file.

How Struts2 File Upload Works

Browser submits multipart/form-data form
    ↓
Struts2 FileUploadInterceptor (part of defaultStack)
    ↓
Creates three action fields for each <input type="file" name="myFile">:
    - File       myFile             → the temp file on disk
    - String     myFileFileName     → original filename from browser
    - String     myFileContentType  → MIME type (e.g. "image/jpeg")
    ↓
Action.execute() reads these fields, copies file to target folder
    ↓
DAO saves user data + file path to database

MySQL Table

CREATE TABLE user_profile (
    id          INT          AUTO_INCREMENT PRIMARY KEY,
    name        VARCHAR(200) NOT NULL,
    email       VARCHAR(200) NOT NULL,
    gender      VARCHAR(10),
    image_path  VARCHAR(500)
);

Maven Dependencies (pom.xml)

<dependencies>
    <dependency>
        <groupId>org.apache.struts</groupId>
        <artifactId>struts2-core</artifactId>
        <version>2.5.33</version>
    </dependency>
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <version>8.3.0</version>
    </dependency>
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.15.1</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

ImageUpload.jsp – Upload Form

The form must use enctype="multipart/form-data" for file upload. The file input name (myFile) determines the names of the three action fields Struts2 creates.

<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html>
<html>
<head>
    <title>Upload Profile Image</title>
    <link rel="stylesheet"
          href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
</head>
<body>
<div class="container mt-4">
    <h3>User Registration with Profile Image</h3>
    <hr>

    <!-- Display validation errors if any -->
    <s:if test="hasActionErrors()">
        <div class="alert alert-danger">
            <s:actionerror />
        </div>
    </s:if>

    <form action="register" method="post"
          enctype="multipart/form-data" class="col-md-6">

        <div class="mb-3">
            <label>Name *</label>
            <input type="text" name="name" class="form-control" required>
        </div>

        <div class="mb-3">
            <label>Email *</label>
            <input type="email" name="email" class="form-control" required>
        </div>

        <div class="mb-3">
            <label>Gender</label>
            <select name="gender" class="form-select">
                <option value="Male">Male</option>
                <option value="Female">Female</option>
                <option value="Other">Other</option>
            </select>
        </div>

        <div class="mb-3">
            <label>Profile Image (JPG/PNG, max 2MB)</label>
            <!--
                name="myFile" → Struts2 creates:
                  File       myFile             (temp file)
                  String     myFileFileName     (original name)
                  String     myFileContentType  (MIME type)
            -->
            <input type="file" name="myFile" class="form-control"
                   accept="image/jpeg,image/png">
        </div>

        <button type="submit" class="btn btn-primary">Register</button>
    </form>
</div>
</body>
</html>

RegisterAction.java

The action declares the three file upload fields. The execute method validates the file type, generates a unique filename using UUID to avoid collisions, copies the temp file to the upload folder, and saves the record to the database.

package com.java9r.action;

import com.java9r.dao.UserProfileDAO;
import com.java9r.model.UserProfile;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

public class RegisterAction extends ActionSupport {

    private static final long serialVersionUID = 1L;

    // Upload folder — change this to your server path
    private static final String UPLOAD_DIR = "C:/uploads/profiles/";

    // Allowed MIME types
    private static final List<String> ALLOWED_TYPES =
        Arrays.asList("image/jpeg", "image/png", "image/gif");

    // Regular form fields
    private String name;
    private String email;
    private String gender;

    // File upload fields — names must follow the convention: fieldName, fieldNameFileName, fieldNameContentType
    private File   myFile;
    private String myFileFileName;
    private String myFileContentType;

    private final UserProfileDAO dao = new UserProfileDAO();

    @Override
    public String execute() {

        // Validate file type
        if (myFile != null && !ALLOWED_TYPES.contains(myFileContentType)) {
            addActionError("Only JPG, PNG, and GIF images are allowed.");
            return ERROR;
        }

        String savedPath = null;

        // Process the uploaded file
        if (myFile != null && myFile.length() > 0) {

            // Extract file extension from original filename
            String ext = "";
            int dot = myFileFileName.lastIndexOf('.');
            if (dot > 0) {
                ext = myFileFileName.substring(dot); // ".jpg", ".png"
            }

            // Generate a unique filename: UUID + extension — avoids name collisions and overwrites
            String uniqueName = UUID.randomUUID().toString() + ext;

            // Ensure the upload directory exists
            File uploadDir = new File(UPLOAD_DIR);
            if (!uploadDir.exists()) {
                uploadDir.mkdirs();
            }

            File destination = new File(UPLOAD_DIR + uniqueName);

            try {
                // Copy from Struts2 temp file to the target location
                FileUtils.copyFile(myFile, destination);
                savedPath = UPLOAD_DIR + uniqueName;
            } catch (IOException e) {
                addActionError("Failed to save image: " + e.getMessage());
                return ERROR;
            }
        }

        // Save user record + image path to database
        UserProfile profile = new UserProfile();
        profile.setName(name);
        profile.setEmail(email);
        profile.setGender(gender);
        profile.setImagePath(savedPath);

        boolean ok = dao.save(profile);
        return ok ? SUCCESS : ERROR;
    }

    // Getters and Setters
    public String getName()  { return name;  }
    public String getEmail() { return email; }
    public String getGender() { return gender; }
    public File   getMyFile() { return myFile; }
    public String getMyFileFileName()    { return myFileFileName;    }
    public String getMyFileContentType() { return myFileContentType; }

    public void setName(String name)     { this.name = name;     }
    public void setEmail(String email)   { this.email = email;   }
    public void setGender(String gender) { this.gender = gender; }
    public void setMyFile(File myFile)   { this.myFile = myFile; }
    public void setMyFileFileName(String myFileFileName)       { this.myFileFileName = myFileFileName; }
    public void setMyFileContentType(String myFileContentType) { this.myFileContentType = myFileContentType; }
}

UserProfile.java (model)

package com.java9r.model;

public class UserProfile {
    private int    id;
    private String name;
    private String email;
    private String gender;
    private String imagePath;

    public int    getId()        { return id;        }
    public String getName()      { return name;      }
    public String getEmail()     { return email;     }
    public String getGender()    { return gender;    }
    public String getImagePath() { return imagePath; }

    public void setId(int id)               { this.id = id;             }
    public void setName(String name)        { this.name = name;         }
    public void setEmail(String email)      { this.email = email;       }
    public void setGender(String gender)    { this.gender = gender;     }
    public void setImagePath(String path)   { this.imagePath = path;    }
}

UserProfileDAO.java

package com.java9r.dao;

import com.java9r.db.DBConnection;
import com.java9r.model.UserProfile;

import java.sql.*;

public class UserProfileDAO {

    public boolean save(UserProfile profile) {
        String sql = "INSERT INTO user_profile (name, email, gender, image_path) VALUES (?,?,?,?)";

        try (Connection con = DBConnection.getConnection();
             PreparedStatement ps = con.prepareStatement(sql)) {

            ps.setString(1, profile.getName());
            ps.setString(2, profile.getEmail());
            ps.setString(3, profile.getGender());
            ps.setString(4, profile.getImagePath());  // null if no file uploaded

            return ps.executeUpdate() > 0;

        } catch (SQLException e) {
            e.printStackTrace();
            return false;
        }
    }
}

struts.xml – File Size Limit Configuration

<?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="false"/>

    <!-- Maximum upload size: 2MB (in bytes) -->
    <constant name="struts.multipart.maxSize" value="2097152"/>

    <package name="default" namespace="/" extends="struts-default">

        <action name="index">
            <result>/ImageUpload.jsp</result>
        </action>

        <action name="register"
                class="com.java9r.action.RegisterAction">
            <result name="success">/success.jsp</result>
            <result name="error">/ImageUpload.jsp</result>

            <!-- FileUpload interceptor is already in defaultStack.
                 These params set per-action overrides. -->
            <interceptor-ref name="defaultStack">
                <param name="fileUpload.allowedTypes">
                    image/jpeg,image/png,image/gif
                </param>
                <param name="fileUpload.maximumSize">2097152</param>
            </interceptor-ref>
        </action>

    </package>

</struts>

success.jsp

<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html>
<html>
<head>
    <title>Registration Successful</title>
    <link rel="stylesheet"
          href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
</head>
<body>
<div class="container mt-4">
    <div class="alert alert-success">
        <h4>Registration successful!</h4>
        <p>Welcome, <strong><s:property value="name"/></strong></p>
        <p>Email: <s:property value="email"/></p>
    </div>
    <a href="index" class="btn btn-primary">Register Another</a>
</div>
</body>
</html>

web.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">

    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>
            org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter
        </filter-class>
    </filter>
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>

The Three File Upload Fields Explained

Action Field Type Contains Example value
myFile java.io.File The uploaded file as a temp file on disk /tmp/upload_xyz123.tmp
myFileFileName String Original filename from the user's computer profile_ravi.jpg
myFileContentType String MIME type detected from file content image/jpeg

The naming convention is mandatory: if the file input is name="myFile", the three action fields must be named exactly myFile, myFileFileName, and myFileContentType.

Issues Fixed vs Original Post

Original Fixed
Buggy rename: new File("myFileFileName") (string literal, not variable) UUID-based unique filename: UUID.randomUUID() + ext
Hard-coded E:/Upload/ path (Windows-only) Configurable constant; mkdirs() creates the folder if it doesn't exist
No file type validation MIME type checked both in action and via interceptor allowedTypes
Deprecated com.mysql.jdbc.Driver Modern com.mysql.cj.jdbc.Driver via shared DBConnection
No error handling in DAO Returns boolean; action adds error messages shown in JSP

Summary

Struts2 file upload is handled by the FileUploadInterceptor (included in the default interceptor stack). For every <input type="file" name="X"> in the form, the interceptor populates three action fields: X (the temp File), XFileName (original name), and XContentType (MIME type). The action then copies the temp file to the final destination using FileUtils.copyFile(), generates a UUID-based filename to prevent collisions, and saves the file path to the database. Set file size limits with struts.multipart.maxSize in struts.xml and validate MIME types with fileUpload.allowedTypes in the action's interceptor configuration.

Topics: Java Struts2
Older Post →

Comments

https://www.blogger.com/comment/frame/6690124484600543990?po=6769604017325216499&hl=en&saa=85391&origin=https://www.java9r.com