Sending Email with attachment using Struts2

This tutorial shows how to send email with an attachment. This tutorial uses one input form where a user gives input for ‘Recipient’s Email’ where the intended email will be sent, ‘Email Subject’, ‘Email Message’ and uploads one file which will be sent as an attachment with the email.

This email sending example uses gmail smtp server to send the mail. If you need only file upload example then you can search in the search box you will get file upload examples in Struts, JSF, Codeigniter.

For testing purpose you can also send the email to yourself and check your mailbox whether you receive the email or not.

Prerequisites

Java at least 8, Struts 2.5, commons-fileupload, commons-io, Java Servlet, Gradle 6.5.1, Maven 3.6.3, Tomcat Server 9.0.24

Project Setup

You can create gradle or maven based project in your favorite IDE. The name of the project is struts-2-multiple-files-upload.

You can use build.gradle script for your gradle based project:

plugins {
	id 'war'
    id 'java-library'
}

sourceCompatibility = 12
targetCompatibility = 12

repositories {
    mavenCentral()
}

dependencies {
	implementation("org.apache.struts:struts2-core:2.5.22")
	implementation 'javax.servlet:javax.servlet-api:4.0.1'
	implementation 'org.apache.logging.log4j:log4j-core:2.13.3'
	implementation 'org.apache.logging.log4j:log4j-api:2.13.3'
	implementation 'com.sun.mail:javax.mail:1.6.2'
	implementation 'commons-fileupload:commons-fileupload:1.4'
	implementation 'commons-io:commons-io:2.7'
}

You can use below pom.xml file for your maven based project:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	
	<groupId>com.roytuts</groupId>
	<artifactId>struts-2-send-email-attachment</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>at least 1.8</java.version>
	</properties>
	
	<dependencies>		
		<dependency>
			<groupId>org.apache.struts</groupId>
			<artifactId>struts2-core</artifactId>
			<version>2.5.22</version>
		</dependency>
		
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>4.0.1</version>
		</dependency>
		
		<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.7</version>
		</dependency>
            
                <dependency>
			<groupId>com.sun.mail</groupId>
			<artifactId>javax.mail</artifactId>
			<version>1.6.2</version>
		</dependency>
		
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-core</artifactId>
			<version>2.13.3</version>
		</dependency>
		
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-api</artifactId>
			<version>2.13.3</version>
		</dependency>
	</dependencies>
	
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.8.1</version>
                <configuration>
					<source>${java.version}</source>
					<target>${java.version}</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

Struts Configuration

You need to tie the action class, view and URL together in this configuration file. The configuration file name is struts.xml and kept under src/main/resources folder.

I have restricted the maximum file size to be uploaded. I have also allowed certain file types to be attached. The UI/input form mapping is done to the action class.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
	<!-- upload max file size limit -->
	<constant name="struts.multipart.maxSize" value="10485760" />
	<package name="default" namespace="/" extends="struts-default">
		<!-- action and action class mapping -->
		<action name="sendEmail"
			class="com.roytuts.struts.send.email.attachment.action.SendEmailAction">
			<result name="success">/index.jsp</result>

			<!-- when we need to show the input form to the user -->
			<result name="input">/index.jsp</result>

			<!-- we use default interceptor. interceptor are like cross cutting concern. -->
			<interceptor-ref name="defaultStack">
				<!-- maximum file size to be allowed to upload -->
				<param name="maximumSize">10485760</param>
				<!-- which files are allowed to be uploaded -->
				<param name="allowedTypes">text/plain,image/jpeg,image/png,image/gif,image/pjpeg</param>
			</interceptor-ref>
		</action>
	</package>
</struts>

Deployment Descriptor

The entry point for any web application is deployment descriptor which is read during the application start up when you deploy on a server.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	id="WebApp_ID" version="3.0">

	<display-name>Struts2 Multiple Files Upload</display-name>
	
	<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>
	
	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>
</web-app>

View File

Create a view file called index.jsp under webapp directory with the following inputs:

  • Recipient’s Email – where the email will be sent
  • Subject – subject of the email
  • Message – message to the email
  • File – file which will be attached to the email
  • Send Email – submit button for validating the input form and sending the email to the intended user
  • You must use enctype=”multipart/form-data” in the form tag otherwise you won’t be able to upload a file
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
	pageEncoding="ISO-8859-1"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Struts2 - Send Email</title>
</head>
<body>
	<s:if test="hasActionErrors()">
		<s:actionerror />
	</s:if>
	<s:if test="hasActionMessages()">
		<s:actionmessage />
	</s:if>
	<s:form action="sendEmail" method="post" enctype="multipart/form-data">
		<s:textfield label="Recipient's Email" name="emailToAddress"></s:textfield>
		<s:textfield label="Subject" name="emailSubject"></s:textfield>
		<s:textarea label="Message" name="emailBodyText" rows="5" cols="50"></s:textarea>
		<s:file label="File" name="file"></s:file>
		<s:submit value="Send Email"></s:submit>
	</s:form>
</body>
</html>

Email Sender Class

Below class does the task of email sending process. It has one static void method sendMail() which takes five arguments with meaningful names.

package com.roytuts.struts.send.email.attachment;

import java.io.File;
import java.util.Properties;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;

public class EmailAttachmentSender {

	public static void sendEmail(final String emailToAddress, final String emailSubject, final String emailBodyText,
			final File file, final String fileName) {
		// make sure you put your gmail address
		final String username = "gmail@gmail.com";

		// make sure you put your correct gmail address password
		final String password = "gmail password";

		// We will put some properties for smtp configurations
		Properties props = new Properties();

		// do not change - start
		props.put("mail.smtp.user", "gmail@gmail.com");
		props.put("mail.smtp.host", "smtp.gmail.com");
		props.put("mail.smtp.port", "587");
		// props.put("mail.debug", "true");
		props.put("mail.smtp.auth", "true");
		props.put("mail.smtp.starttls.enable", "true");
		props.put("mail.smtp.EnableSSL.enable", "true");

		// do not change - end
		// we authentcate using your gmail email and password and on successful we
		// create the session
		Session session = Session.getInstance(props, new javax.mail.Authenticator() {
			protected PasswordAuthentication getPasswordAuthentication() {
				return new PasswordAuthentication(username, password);
			}
		});

		try {
			// we create new message
			Message message = new MimeMessage(session);

			// set the from 'email address'
			message.setFrom(new InternetAddress(username));

			// set 'to email address'
			message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(emailToAddress));

			// set email subject
			message.setSubject(emailSubject);

			// set email message
			message.setText(emailBodyText);

			// create MimeBodyPart for file attachment
			MimeBodyPart messageBodyPart = new MimeBodyPart();

			// we need this also for file attachment
			Multipart multipart = new MimeMultipart();

			// file data
			DataSource source = new FileDataSource(file);

			// attach file
			messageBodyPart.setDataHandler(new DataHandler(source));

			// set meaningful file name
			messageBodyPart.setFileName(fileName);


                        // set body text
			messageBodyPart.setText(emailBodyText);

			// add the whole content
			multipart.addBodyPart(messageBodyPart);

			// required for file
			message.setContent(multipart);
			
			System.out.println("Sending Email to " + emailToAddress + " from " + username + " with Subject '"
					+ emailSubject + "'.");

			// send the email
			Transport.send(message);

			System.out.println("Email with attachment successfully sent");
		} catch (MessagingException e) {
			e.printStackTrace();
		}
	}

}

You need to issue STARTTLS command otherwise you will get the error message similar to the following:

#smtplib.SMTPSenderRefused: (530, b'5.7.0 Must issue a STARTTLS command first. p7sm24605501pfn.14 - gsmtp', 'gmailaddress@gmail.com')

You will get below error if your security level is high:

#smtplib.SMTPAuthenticationError: (535, b'5.7.8 Username and Password not accepted. Learn more at\n5.7.8  https://support.google.com/mail/?p=BadCredentials x10sm26098036pfn.36 - gsmtp')

Therefore you need to lower your Gmail’s security settings.

Action Class

Now look into the Action class which will do the actual upload of file and email sending process. I implemented ServletContextAware to get the servlet related work done like uploading the file. The uploaded file is sent as an attachment in the email body. The action class is just a simple POJO(Plain Old Java Object).

Note in this class, attribute names are same as on the input jsp form. So the mapping will be automatically done between form input names and java class attribute names.

package com.roytuts.struts.send.email.attachment.action;

import java.io.File;

import javax.servlet.ServletContext;

import org.apache.struts2.util.ServletContextAware;

import com.opensymphony.xwork2.ActionSupport;
import com.roytuts.struts.send.email.attachment.EmailAttachmentSender;

public class SendEmailAction extends ActionSupport implements ServletContextAware {
	private static final long serialVersionUID = 1L;

	// email subject
	private String emailSubject;

	// email message
	private String emailBodyText;

	// email to address
	private String emailToAddress;

	// input file
	private File file;

	// input file name - if the input type name is 'file' then file name should be
	// 'fileFileName', if input type // name is 'f' then file name should be
	// 'fFileName'
	private String fileFileName;

	// servlet context
	private ServletContext servletContext;

	public String getEmailSubject() {
		return emailSubject;
	}

	public void setEmailSubject(String emailSubject) {
		this.emailSubject = emailSubject;
	}

	public String getEmailBodyText() {
		return emailBodyText;
	}

	public void setEmailBodyText(String emailBodyText) {
		this.emailBodyText = emailBodyText;
	}

	public String getEmailToAddress() {
		return emailToAddress;
	}

	public void setEmailToAddress(String emailToAddress) {
		this.emailToAddress = emailToAddress;
	}

	public File getFile() {
		return file;
	}

	public void setFile(File file) {
		this.file = file;
	}

	public String getFileFileName() {
		return fileFileName;
	}

	public void setFileFileName(String fileFileName) {
		this.fileFileName = fileFileName;
	}

	public ServletContext getServletContext() {
		return servletContext;
	}

	// overridden method when we implement ServletContextAware
	@Override
	public void setServletContext(ServletContext servletContext) {
		this.servletContext = servletContext;
	}

	// action method
	@Override
	public String execute() throws Exception {
		// send the email
		EmailAttachmentSender.sendEmail(emailToAddress, emailSubject, emailBodyText, file, fileFileName);
		addActionMessage("Email with attachment successfully sent!");

		// return to the success page which is the same page here
		return SUCCESS;
	}

	// validate method is called before the execute method to validate the input
	// fields
	@Override
	public void validate() {
		if (emailToAddress == null) {
			String errorMsg = "You must provide To Email Address!";
			addActionError(errorMsg);
		}
		if (emailSubject == null) {
			String errorMsg = "You must provide Email Subject!";
			addActionError(errorMsg);
		}
		if (emailBodyText == null) {
			String errorMsg = "You must provide Email Message!";
			addActionError(errorMsg);
		}
		if (file == null) {
			String errorMsg = "You must select a file!";
			addActionError(errorMsg);
		}
	}

}

Testing the Application

You can now hit the URL http://localhost:8080/struts-2-send-email-attachment/ and you will see the following output on the browser where you can compose message and attach a file to send to the intended recipient.

For example, I am sending an email with attachment to myself.

struts 2 send email with attachment

Once message sent you will see below success message on the page:

struts 2 email with attachment

In your mailbox you will see similar to the below image:

struts 2 email attachment

In the above image you are seeing the from email address as me, because I have sent email to myself.

Finally you can check the email content by clicking on it.

Source Code

Download

Thanks for reading.

Leave a Reply

Your email address will not be published. Required fields are marked *