Email Attachments

An email attachment is a file (report, PDF, image, document) sent along with the email message. In Spring Boot you create a multipart MimeMessage with MimeMessageHelper and add files using addAttachment(). The file can come from disk, from a database, or be generated in memory as a ByteArrayResource. It is used for reports, invoices, certificates, offer letters, and receipts.

Features

  • Attach one or many files to a single email
  • Generate files in memory, so nothing is written to disk
  • Set the file name and content type (text/csv, application/pdf, image/png)
  • Works with the same spring.mail.* SMTP configuration as 12.1 and 12.2
  • Combines with HTML bodies, since the multipart message supports both
  • Files can be attached from disk (FileSystemResource), memory (ByteArrayResource), or the classpath (ClassPathResource)

Email Attachment Types

Type Resource class When to use
In-memory attachment ByteArrayResource Reports generated on the fly; used in this example
File-system attachment FileSystemResource Files already stored on the server (uploaded certificates, saved PDFs)
Classpath attachment ClassPathResource Fixed files bundled with the app (terms and conditions, brochures)
Inline attachment helper.addInline(cid, resource) Images shown inside the HTML body, such as a college logo
Multiple attachments Call addAttachment() repeatedly Sending a report and a receipt together

Hands-on Experiment: Creating a spring boot application to understand how files can be attached to outgoing emails.

The following steps are involved in developing this hands-on experiment.

Step 1: Generate the Project

Step 2: Project Structure

Step 3: pom.xml

Step 4: application.properties

Step 5: Create Entity Class

Step 6: Create Repository Interface

Step 7: Create Service Class

Step 8: Create Controller Class

Step 9: Main Application Class

Step 10: Run the Main Application Class

Step 1: Configure the Project on Spring Initializr

Go to start.spring.io and set:

  • Project: Maven
  • Language: Java
  • Spring Boot version: 3.2.x
  • Group: com.example
  • Artifact: attachment
  • Name: attachment
  • Package name: com.example.attachment
  • Packaging: Jar
  • Java: 17
  • Dependencies: Spring Web, Spring Data JPA, H2 Driver.

Click Generate to download the ZIP, then extract and import it into your IDE as a Maven project.

Step 2: Generated Project Structure

Step 3: Explore pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.5</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>student-attachment-demo</artifactId>
    <version>1.0.0</version>
    <name>student-attachment-demo</name>
    <description>Email Attachments - Student report sent as attachment</description>

    <properties>
        <java.version>17</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

Step 4: application.properties

spring.application.name=student-attachment-demo

# Create the database first:  CREATE DATABASE student_smtp_db;
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
server.port=8082

# ---------- SMTP (Gmail example) ----------
spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username=ravi@gmail.com
# Use a 16-character Gmail App Password (NOT your normal password)
spring.mail.password=
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.connectiontimeout=5000
spring.mail.properties.mail.smtp.timeout=5000
spring.mail.properties.mail.smtp.writetimeout=5000

# ---------- Custom ----------
app.mail.from=your-email@gmail.com

# Attachment size limit is decided by the mail provider (Gmail: 25 MB)

Step 5: Create Entity Class

package com.example.attachment.entity;

import jakarta.persistence.*;

@Entity
@Table(name = "students")
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false, unique = true)
    private String email;

    private String course;

    private String department;

    private Integer marks;

    public Student() {
    }

    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }

    public String getCourse() { return course; }
    public void setCourse(String course) { this.course = course; }

    public String getDepartment() { return department; }
    public void setDepartment(String department) { this.department = department; }

    public Integer getMarks() { return marks; }
    public void setMarks(Integer marks) { this.marks = marks; }
}

Step 6: Create below Repository Interface

package com.example.attachment.repository;

import com.example.attachment.entity.Student;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface StudentRepository extends JpaRepository<Student, Long> {

    boolean existsByEmail(String email);
}

Step 7: Create below Service class

package com.example.attachment.service;

import com.example.attachment.entity.Student;
import com.example.attachment.repository.StudentRepository;
import jakarta.mail.internet.MimeMessage;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.util.List;

@Service
public class StudentService {

    private final StudentRepository studentRepository;
    private final JavaMailSender mailSender;

    @Value("${app.mail.from}")
    private String fromAddress;

    public StudentService(StudentRepository studentRepository, JavaMailSender mailSender) {
        this.studentRepository = studentRepository;
        this.mailSender = mailSender;
    }

    public Student add(Student student) {
        if (studentRepository.existsByEmail(student.getEmail())) {
            throw new IllegalArgumentException("Email already registered: " + student.getEmail());
        }
        return studentRepository.save(student);
    }

    public List<Student> getAll() {
        return studentRepository.findAll();
    }

    // 1. Generate the report in memory (CSV) - no file is saved on disk
    public byte[] generateReport() {
        StringBuilder csv = new StringBuilder();
        csv.append("ID,Name,Email,Course,Department,Marks\n");
        for (Student s : studentRepository.findAll()) {
            csv.append(s.getId()).append(',')
               .append(clean(s.getName())).append(',')
               .append(clean(s.getEmail())).append(',')
               .append(clean(s.getCourse())).append(',')
               .append(clean(s.getDepartment())).append(',')
               .append(s.getMarks() == null ? "" : s.getMarks())
               .append('\n');
        }
        return csv.toString().getBytes(StandardCharsets.UTF_8);
    }

    // 2. Attach the report to the email and send it
    public int sendReport(String toEmail) throws Exception {
        byte[] report = generateReport();
        int count = (int) studentRepository.count();

        MimeMessage mimeMessage = mailSender.createMimeMessage();
        // true = multipart mode, required for attachments
        MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");

        helper.setFrom(fromAddress);
        helper.setTo(toEmail);
        helper.setSubject("Student Report - " + LocalDate.now());
        helper.setText("Hello,\n\nPlease find the attached student report containing "
                + count + " student(s).\n\nRegards,\nAdmissions Office");

        helper.addAttachment("student-report-" + LocalDate.now() + ".csv",
                new ByteArrayResource(report), "text/csv");

        mailSender.send(mimeMessage);
        return count;
    }

    // Avoid commas breaking the CSV columns
    private String clean(String value) {
        return value == null ? "" : value.replace(",", " ");
    }
}

Step 8: Create Controller class

package com.example.attachment.controller;

import com.example.attachment.entity.Student;
import com.example.attachment.service.StudentService;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/students")
public class StudentController {

    private final StudentService studentService;

    public StudentController(StudentService studentService) {
        this.studentService = studentService;
    }

    @PostMapping
    public ResponseEntity<?> add(@RequestBody Student student) {
        try {
            return ResponseEntity.status(HttpStatus.CREATED).body(studentService.add(student));
        } catch (IllegalArgumentException ex) {
            return ResponseEntity.badRequest().body(Map.of("error", ex.getMessage()));
        }
    }

    @GetMapping
    public List<Student> getAll() {
        return studentService.getAll();
    }

    // Download the same report in the browser (to preview what will be attached)
    @GetMapping("/report/download")
    public ResponseEntity<byte[]> download() {
        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=student-report.csv")
                .contentType(MediaType.parseMediaType("text/csv"))
                .body(studentService.generateReport());
    }

    // Generate the report and email it as an attachment
    @PostMapping("/report/email")
    public ResponseEntity<?> emailReport(@RequestBody Map<String, String> request) {
        String to = request.get("to");
        if (to == null || to.isBlank()) {
            return ResponseEntity.badRequest().body(Map.of("error", "'to' email is required"));
        }
        try {
            int count = studentService.sendReport(to);
            return ResponseEntity.ok(Map.of("message", "Report emailed successfully",
                    "to", to, "students", count));
        } catch (Exception ex) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(Map.of("error", "Mail failed: " + ex.getMessage()));
        }
    }
}

Step 9: Main Application

package com.example.attachment;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AttachmentApplication {

    public static void main(String[] args) {
        SpringApplication.run(AttachmentApplication.class, args);
    }
}

Step 10: Run the AttachmentApplication

Scroll to Top