Backend Development 15 min read

Implementing Image Self‑Destruct Feature with Spring Boot and MySQL

This guide walks through building a Spring Boot and MySQL‑backed “burn after viewing” image sharing service, covering architecture, environment setup, entity and repository design, upload and automatic deletion logic, Thymeleaf UI with burn animation, error handling, optimization, and Docker deployment.

Java Tech Enthusiast
Java Tech Enthusiast
Java Tech Enthusiast
Implementing Image Self‑Destruct Feature with Spring Boot and MySQL

Introduction

The article describes how to build a "burn after viewing" image sharing feature using Spring Boot, MySQL, and Thymeleaf. The goal is to let users upload images that are automatically deleted after being viewed, enhancing privacy and security.

Background and Requirements

Privacy protection is increasingly important in social media and instant messaging. Users want temporary image sharing without leaving traces. The functional requirements include upload & storage, expiration mechanism, user‑friendly UI, feedback, and security.

Upload and store images securely.

Delete the image automatically after it is viewed.

Provide a simple UI for upload and preview.

Give feedback on upload success/failure.

Ensure the uploaded files cannot be accessed illegally.

System Architecture Design

The system consists of a front‑end (Thymeleaf), a Spring Boot back‑end, a MySQL database, and a file storage layer (local or cloud). The architecture diagram is:

+------------------+
|   User Interface |
|   (Thymeleaf)    |
+--------+---------+
         |
+--------v---------+
|   Spring Boot    |
|   Controller     |
+--------+---------+
         |
+--------v---------+
|   Service Layer  |
|   (Business)     |
+--------+---------+
         |
+--------v---------+
|   Data Access    |
|   (MySQL/JPA)    |
+--------+---------+
         |
+--------v---------+
|   File Storage   |
| (Local/Cloud)    |
+------------------+

Environment Setup

Create a Spring Boot project with the following dependencies: Spring Web, Spring Data JPA, MySQL Driver, and Thymeleaf. Configure MySQL database image_sharing_db and set connection properties in application.properties .

# MySQL configuration
spring.datasource.url=jdbc:mysql://localhost:3306/image_sharing_db?useSSL=false&serverTimezone=UTC
spring.datasource.username=your_username
spring.datasource.password=your_password
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect

Add the MySQL connector dependency in pom.xml :

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

Create a directory uploads with write permission for storing uploaded files.

Feature Implementation

Data Model

import javax.persistence.*;
import java.time.LocalDateTime;

@Entity
public class Image {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String filename;
    private LocalDateTime uploadTime;
    private LocalDateTime expirationTime;
    // getters and setters omitted for brevity
}

Repository

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ImageRepository extends JpaRepository
{}

Controller

package com.example.demo.controller;

import com.example.demo.entity.Image;
import com.example.demo.repository.ImageRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.Optional;
import java.util.UUID;

@Controller
@RequestMapping("/images")
public class ImageController {
    private static final String UPLOAD_DIR = "src/main/resources/static/uploads/";
    @Autowired
    private ImageRepository imageRepository;

    @GetMapping("/upload")
    public String uploadPage() { return "upload"; }

    @PostMapping("/upload")
    public String uploadImage(@RequestParam("file") MultipartFile file, Model model) {
        String filename = UUID.randomUUID().toString() + "_" + file.getOriginalFilename();
        Path path = Paths.get(UPLOAD_DIR + filename);
        try {
            Files.createDirectories(path.getParent());
            file.transferTo(path);
            Image image = new Image();
            image.setFilename(filename);
            image.setUploadTime(LocalDateTime.now());
            image.setExpirationTime(LocalDateTime.now().plusMinutes(1));
            Image saved = imageRepository.save(image);
            model.addAttribute("message", "Image uploaded successfully.");
            model.addAttribute("imageUrl", "/uploads/" + filename);
            model.addAttribute("imageId", saved.getId());
        } catch (IOException e) {
            model.addAttribute("message", "Failed to upload image: " + e.getMessage());
        }
        return "upload";
    }

    @PostMapping("/burn/{id}")
    public ResponseEntity
burnImage(@PathVariable Long id) {
        Optional
opt = imageRepository.findById(id);
        if (opt.isPresent()) {
            Image img = opt.get();
            Path path = Paths.get(UPLOAD_DIR + img.getFilename());
            try {
                Files.deleteIfExists(path);
                imageRepository.delete(img);
                return ResponseEntity.ok("Image burned successfully");
            } catch (IOException e) {
                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                        .body("Failed to burn image");
            }
        }
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Image not found");
    }
}

UI Design

The upload page upload.html uses Thymeleaf to display the upload form, preview the image, and trigger a burn animation via JavaScript.

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>Burn After Viewing</title>
    <style>/* CSS for layout and burn animation */</style>
</head>
<body>
    <div class="upload-container">
        <h1>Burn After Viewing</h1>
        <form action="/images/upload" method="post" enctype="multipart/form-data">
            <input type="file" name="file" required>
            <button type="submit">Upload</button>
        </form>
        <p th:text="${message}" class="message"></p>
    </div>
    <div th:if="${imageUrl}" class="preview-container">
        <h2>Image Preview</h2>
        <img th:src="${imageUrl}" alt="Uploaded Image">
        <div id="burnEffect"></div>
    </div>
    <button id="burnButton">Burn</button>
    <script>
        document.getElementById('burnButton').onclick = function() {
            const burn = document.getElementById('burnEffect');
            burn.style.display = 'block';
            burn.classList.add('burn');
            setTimeout(() => {
                document.querySelector('img').style.display = 'none';
                burn.style.display = 'none';
                alert('Image has been burned and cannot be recovered.');
            }, 2000);
        };
    </script>
</body>
</html>

Error Handling

import org.springframework.http.HttpStatus;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;

@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(RuntimeException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public String handleRuntimeException(RuntimeException e, Model model) {
        model.addAttribute("message", "An error occurred: " + e.getMessage());
        return "error";
    }
}

System Optimization

Performance improvements include image compression (using Thumbnailator) and asynchronous processing with @Async or message queues. Security measures cover filename randomization with UUIDs and MIME‑type validation. Logging is added via SLF4J/Logback.

private static final Logger logger = LoggerFactory.getLogger(ImageController.class);
logger.info("Image uploaded successfully: {}", filename);

Testing and Deployment

After functional verification, the application can be containerized with Docker. Example Dockerfile :

FROM openjdk:11-jre-slim
VOLUME /tmp
COPY target/image-sharing-app.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

Conclusion

The tutorial demonstrates a complete workflow—from requirement analysis, architecture design, implementation, optimization, to testing and deployment—for building a secure, self‑destructing image sharing service using Spring Boot and MySQL.

Spring BootMySQLsecurityweb developmentImage BurnSelf-Destruct
Java Tech Enthusiast
Written by

Java Tech Enthusiast

Sharing computer programming language knowledge, focusing on Java fundamentals, data structures, related tools, Spring Cloud, IntelliJ IDEA... Book giveaways, red‑packet rewards and other perks await!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.