Backend Development 15 min read

Implementing a View‑Once Image Feature with Spring Boot and MySQL

This guide explains how to design and build a privacy‑focused, view‑once image sharing system using Spring Boot, MySQL, Thymeleaf, and optional cloud storage, covering requirements analysis, architecture, environment setup, code implementation, security considerations, performance optimizations, testing, and deployment.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Implementing a View‑Once Image Feature with Spring Boot and MySQL

Introduction

“View‑once” (Snapchat‑like) functionality automatically deletes a message or image after it has been read, protecting user privacy and preventing unauthorized access to sensitive content.

1. Background and Requirement Analysis

With growing concerns about information security and privacy, users increasingly demand temporary sharing of images that leave no trace after viewing. This feature satisfies personal privacy needs and can be applied in enterprise communication, social platforms, and online education.

1.1 Current State of Internet Privacy Protection

Frequent data leaks on social media have heightened users' awareness of privacy, prompting demand for mechanisms that restrict access to uploaded images after they are viewed.

1.2 Requirements for View‑Once Images

Upload & Storage : Users can upload images, which must be stored securely.

Expiration Mechanism : Images are automatically deleted after being viewed.

User‑Friendly Interface : Simple UI for uploading and previewing images.

Feedback Mechanism : System provides upload success/failure messages.

Security : Prevent illegal access to stored images.

2. System Architecture Design

2.1 Technology Stack

Backend : Spring Boot – rapid development of RESTful APIs with good scalability.

Database : MySQL – relational storage for image metadata.

Frontend : Thymeleaf + HTML/CSS/JavaScript – template engine for dynamic pages.

File Storage : Local file system or cloud storage (e.g., AWS S3).

2.2 Architecture Diagram

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

3. Environment Setup

3.1 Create Spring Boot Project

Use Spring Initializr (https://start.spring.io/) and add dependencies: Spring Web, Spring Data JPA, MySQL Driver, Thymeleaf.

3.2 Database Configuration

Create a MySQL database named image_sharing_db :

CREATE DATABASE image_sharing_db;

Configure application.properties :

# MySQL database 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

3.3 Add Maven Dependencies

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

3.4 File Storage Directory

Create an uploads folder in the project root with write permissions to store uploaded images.

4. Feature Implementation

4.1 Data Model

Define an Image entity with fields for filename, upload time, and expiration time:

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
}

4.2 Data Access Layer

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

@Repository
public interface ImageRepository extends JpaRepository<Image, Long> {}

4.3 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 savedImage = imageRepository.save(image);
            model.addAttribute("message", "Image_Uploaded_Successfully.");
            model.addAttribute("imageUrl", "/uploads/" + filename);
            model.addAttribute("imageId", savedImage.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
imageOptional = imageRepository.findById(id);
        if (imageOptional.isPresent()) {
            Image image = imageOptional.get();
            Path path = Paths.get("src/main/resources/static/uploads/" + image.getFilename());
            try {
                Files.deleteIfExists(path);
                imageRepository.delete(image);
                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");
    }
}

4.4 User Interface

The upload.html page provides a file input, upload button, preview area, and a “burn” button that triggers a CSS animation and optionally sends a server request to delete the image.

4.5 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";
    }
}

5. System Optimization

5.1 Performance

Image Compression : Use libraries like Thumbnailator to reduce file size before storage.

Asynchronous Processing : Apply @Async or message queues to avoid blocking requests.

5.2 Security

Filename Safety : Rename files with UUIDs to prevent collisions and path traversal.

File Type Validation : String contentType = file.getContentType(); if (!contentType.startsWith("image/")) { model.addAttribute("message", "Please upload a valid image file."); return "upload"; }

5.3 Logging

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

6. Testing and Deployment

6.1 Verification

A GIF demonstration shows the image disappearing after the burn button is clicked.

6.2 Docker Deployment

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

Conclusion

This demo illustrates how to build a secure, easy‑to‑use view‑once image sharing platform with Spring Boot and MySQL, covering requirement analysis, architecture design, implementation, optimization, testing, and containerized deployment.

backendprivacySpring BootMySQLThymeleafImage Burn
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

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.