SpringBoot 打造图片阅后即焚功能

news2025/4/6 21:06:27

阅后即焚”(Snapchat-like feature)是指一种社交媒体或信息传递功能,用户在阅读某条信息或查看某张图片后,该信息或图片会自动销毁,无法再次查看。这种功能的主要目的是保护用户的隐私和信息安全,防止敏感信息被未经授权的人获取。

一、背景与需求分析

信息安全和隐私保护愈发受到重视。随着社交媒体和即时通讯工具的普及,很多用户希望能够分享临时信息而不留下痕迹。图片阅后即焚功能正是在这样的背景下应运而生。它不仅能满足用户的隐私需求,还能增强信息的安全性。本文将详细介绍如何使用Spring Boot和MySQL实现图片阅后即焚功能,包括系统架构、技术选型、代码实现和测试等内容。

1.1 互联网隐私保护现状

随着互联网的发展,用户的隐私保护意识日益增强。社交媒体上频繁发生的信息泄露事件让用户开始关注自己分享的内容。为了保护个人隐私,许多人希望在分享图片时,能够设置某种限制,使得接收方在查看后无法再次访问这些图片。这种需求不仅适用于个人用户,也在企业内部交流、社交平台和在线教育等场景中逐渐被重视。

1.2 图片阅后即焚的需求

阅后即焚功能主要包含以下几个方面的需求:

  • 上传与存储:用户可以上传图片,系统需将其安全存储。

  • 过期机制:图片在查看后自动删除,用户无法再次访问。

  • 用户友好界面:提供简单直观的或下载。

  • 反馈机制:系统应能够给用户提供关于图片界面,让用户方便操作。

  • 安全性:确保上传的图片不会被非法访问上传和查看状态的反馈信息,例如上传成功或失败的提示。

二、系统架构设计

2.1 技术选型

本系统主要使用以下技术栈:

  • 后端:Spring Boot —— 采用此框架可以快速构建和部署RESTful API,并具备良好的可扩展性。

  • 数据库:MySQL —— 作为关系型数据库,MySQL具有强大的数据管理能力,适合存储和查询结构化数据。

  • 前端:Thymeleaf + HTML/CSS/JavaScript —— Thymeleaf作为模板引擎,可以快速生成动态HTML页面。

  • 文件存储:本地文件系统或云存储服务(如 AWS S3)—— 提供灵活的文件存储方案。

2.2 系统架构图

以下是系统架构图,展示了各个模块之间的关系:

+------------------+
|   用户界面       |
|  (Thymeleaf)     |
+--------+---------+
         |
         |
+--------v---------+
|   Spring Boot    |
|    控制器层      |
+--------+---------+
         |
         |
+--------v---------+
|    服务层        |
|   (业务逻辑)     |
+--------+---------+
         |
         |
+--------v---------+
|   数据访问层    |
|  (MySQL/JPA)    |
+--------+---------+
         |
         |
+--------v---------+
|    文件存储      |
| (本地/云存储)   |
+------------------+

三、环境搭建

3.1 创建Spring Boot项目

使用Spring Initializr(start.spring.io/)创建一个新的Spring Boot项目,选择以下依赖:

  • Spring Web:用于构建RESTful API。

  • Spring Data JPA:简化数据访问层的开发。

  • MySQL Driver:用于连接MySQL数据库。

  • Thymeleaf:用于生成动态网页。

在生成项目后,将其导入到IDE中(如IntelliJ IDEA或Eclipse),并确保项目可以正常编译和运行。

3.2 数据库配置

在MySQL中创建一个新的数据库,例如 image_sharing_db。可以使用以下SQL命令:

CREATE DATABASE image_sharing_db;

然后在 application.properties 文件中配置数据库连接:

# MySQL 数据库配置
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 添加依赖

确保在 pom.xml 中添加以下依赖:

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

3.4 文件存储目录

为了存储用户上传的图片,需要在项目中创建一个文件存储目录。可以在项目根目录下创建一个名为 uploads 的文件夹,确保该文件夹具有可写权限。

四、功能实现

4.1 数据模型设计

创建一个 Image 实体类,表示上传的图片信息。包括文件名、上传时间、过期时间等字段。

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

@Entity
public class Image {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id; // 图片ID
    private String filename; // 文件名
    private LocalDateTime uploadTime; // 上传时间
    private LocalDateTime expirationTime; // 过期时间

    // getters and setters
    public Long getId() {
        return id;
    }

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

    public String getFilename() {
        return filename;
    }

    public void setFilename(String filename) {
        this.filename = filename;
    }

    public LocalDateTime getUploadTime() {
        return uploadTime;
    }

    public void setUploadTime(LocalDateTime uploadTime) {
        this.uploadTime = uploadTime;
    }

    public LocalDateTime getExpirationTime() {
        return expirationTime;
    }

    public void setExpirationTime(LocalDateTime expirationTime) {
        this.expirationTime = expirationTime;
    }
}

4.2 数据访问层

创建一个数据访问接口 ImageRepository,用于与数据库交互。

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

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

这里我们使用 JpaRepository 提供的基本CRUD操作,方便对 Image 实体的数据库操作。

4.3 控制器实现

创建一个控制器 ImageController,处理图片的上传和查看请求。

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;

/**
 * By zhangT
 */
@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)); // 设置过期时间为1分钟
            Image savedImage = imageRepository.save(image);

            // 添加图片链接和ID到页面模型
            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<String> burnImage(@PathVariable Long id) {
        Optional<Image> 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 用户界面设计

4.4.1 上传页面

创建 upload.html,让用户可以上传图片,显示用户上传的图片,提示用户图片已过期。

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>阅后即焚</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            display: flex;
            flex-direction: column;
            align-items: center;
        }
        .upload-container {
            margin-top: 50px;
            text-align: center;
        }
        .preview-container img {
            max-width: 100%;
            height: auto;
            margin-top: 10px;
        }
        .message {
            color: green;
            font-weight: bold;
            margin-top: 10px;
        }
         /* 燃烧效果的 CSS */
        .burn {
            animation: burn 1s forwards;
        }

        @keyframes burn {
            from {
                opacity: 1;
            }
            to {
                opacity: 0;
                transform: scale(1.5);
            }
        }

        #imageContainer {
            position: relative;
        }

        #burnEffect {
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: rgba(255, 0, 0, 0.5);
            display: none;
            z-index: 10;
        }
    </style>
</head>
<body>
<div class="upload-container">
    <h1>阅后即焚</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 d="imageContainer" class="preview-container" th:if="${imageUrl}">
    <h2>图片预览</h2>
    <img th:src="${imageUrl}" alt="上传图片">
    <div id="burnEffect"></div>
</div>

<button id="burnButton">阅后即焚</button>

<script>
    // 点击阅后即焚按钮的事件
    document.getElementById("burnButton").onclick = function() {
        const burnEffect = document.getElementById("burnEffect");
        burnEffect.style.display = "block";  // 显示燃烧效果
        burnEffect.classList.add("burn");    // 添加燃烧动画效果

        // 延迟后删除图片
        setTimeout(function() {
            document.querySelector("img").style.display = "none"; // 隐藏图片
            burnEffect.style.display = "none";  // 隐藏燃烧效果
            alert("图片已阅后即焚,无法恢复。");
        }, 2000); // 1秒后删除
    };

    // 向服务器发送阅后即焚请求
<!--    const imageId = /*[[${imageId}]]*/ 0;-->
<!--    fetch(`/burn/${imageId}`, { method: 'POST' })-->
<!--        .then(response => {-->
<!--            if (response.ok) {-->
<!--                response.text().then(msg => {-->
<!--                    console.log(msg);-->
<!--                    setTimeout(() => {-->
<!--                        imageElement.style.display = 'none';-->
<!--                        burnButton.style.display = 'none';-->
<!--                    }, 2000);  // 燃烧效果结束后隐藏图片-->
<!--                });-->
<!--            } else {-->
<!--                console.error("Failed to burn image.");-->
<!--            }-->
<!--        });-->
</script>
</body>
</html>

4.5 错误处理

在 ImageController 中实现统一的错误处理机制,捕获并处理可能出现的异常。可以通过 @ControllerAdvice 来实现全局异常处理:

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.1 性能优化

为了提高系统性能,可以考虑以下优化策略:

  • 图片压缩:在上传图片时,压缩图片以减少存储空间和上传时间。可以使用第三方库如 Thumbnailator 进行图片处理。

  • 异步处理:将图片的处理和存储任务异步化,避免阻塞用户请求。可以使用 @Async 注解或消息队列实现。

5.2 安全性

  • 文件名安全性:为了避免文件名冲突和安全隐患,上传的文件名可以使用UUID进行重命名。

import java.util.UUID;

// 在uploadImage方法中生成新的文件名
String newFilename = UUID.randomUUID().toString() + "_" + filename;
Path path = Paths.get(UPLOAD_DIR + newFilename);
  • 文件类型检查:确保上传的文件是图片格式(如JPEG、PNG等),避免用户上传恶意文件。

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

5.3 日志记录

使用 SLF4J 和 Logback 记录系统运行日志,包括上传、查看和错误信息,以便后期分析和监控。

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

public class ImageController {
    private static final Logger logger = LoggerFactory.getLogger(ImageController.class);

    // 在相应的位置记录日志
    logger.info("Image uploaded successfully: {}", filename);
}

六、测试与部署

6.1 效果验证

阅读即焚.gif

6.2 部署

可以使用Docker容器化部署Spring Boot应用,确保其在各个环境中都能稳定运行。编写 Dockerfile,配置相应的基础镜像和运行环境。

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

七、总结

本文仅以Demo展示的方式介绍了如何使用Spring Boot和MySQL实现图片阅后即焚功能。通过分析需求、设计系统架构、实现功能、进行优化和测试,最终构建出一个安全、易用的图片分享平台。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2241078.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FFmpeg 4.3 音视频-多路H265监控录放C++开发十三.2:avpacket中包含多个 NALU如何解析头部分析

前提&#xff1a; 注意的是&#xff1a;我们这里是从avframe转换成avpacket 后&#xff0c;从avpacket中查看NALU。 在实际开发中&#xff0c;我们有可能是从摄像头中拿到 RGB 或者 PCM&#xff0c;然后将pcm打包成avframe&#xff0c;然后将avframe转换成avpacket&#xff0…

Vue之插槽(slot)

插槽是vue中的一个非常强大且灵活的功能&#xff0c;在写组件时&#xff0c;可以为组件的使用者预留一些可以自定义内容的占位符。通过插槽&#xff0c;可以极大提高组件的客服用和灵活性。 插槽大体可以分为三类&#xff1a;默认插槽&#xff0c;具名插槽和作用域插槽。 下面…

华为鸿蒙HarmonyOS NEXT升级HiCar:打造未来出行新体验

随着科技的不断进步&#xff0c;智能出行已成为我们生活中不可或缺的一部分。华为凭借其在智能科技领域的深厚积累&#xff0c;推出了全新的鸿蒙HarmonyOS NEXT系统&#xff0c;旨在为用户打造一个“人车家”的无缝协同出行体验。这一系统的核心亮点之一&#xff0c;就是其内置…

Clickhouse集群新建用户、授权以及remote权限问题

新建用户 create user if not exists user on cluster 集群名称 IDENTIFIED WITH plaintext_password BY 密码;给用户授查询、建表、删表的权限 GRANT create table,select,drop table ON 数据库实例.* TO user on cluster 集群名称 ;在其他节点下用户建本地表成功&#…

Serverless架构在实时数据处理中的应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 Serverless架构在实时数据处理中的应用 Serverless架构在实时数据处理中的应用 Serverless架构在实时数据处理中的应用 引言 Ser…

Scrapy爬取heima论坛所有页面内容并保存到数据库中

前期准备&#xff1a; Scrapy入门_win10安装scrapy-CSDN博客 新建 Scrapy项目 scrapy startproject mySpider03 # 项目名为mySpider03 进入到spiders目录 cd mySpider03/mySpider03/spiders 创建爬虫 scrapy genspider heima bbs.itheima.com # 爬虫名为heima &#…

QSS 设置bug

问题描述&#xff1a; 在QWidget上add 一个QLabel&#xff0c;但是死活不生效 原因&#xff1a; c 主程序如下&#xff1a; QWidget* LOGO new QWidget(logo_wnd);LOGO->setFixedSize(logo_width, 41);LOGO->setObjectName("TittltLogo");QVBoxLayout* tit…

TKinter实现与Dash应用的同步启停控制

使用Python集成Tkinter和Dash&#xff1a;创建交互式数据可视化应用 在数据可视化项目中&#xff0c;我们经常需要结合传统GUI和现代Web可视化框架的优势。本文将介绍如何整合Tkinter和Dash&#xff0c;创建一个既有桌面应用界面&#xff0c;又能展示交互式图表的应用程序。 …

FreeRTOS学习13——任务相关API函数

任务相关API函数 任务相关API函数任务相关API函数介绍任务相关 API 函数详解函数 uxTaskPriorityGet()函数 vTaskPrioritySet()函数 uxTaskGetSystemState()函数 vTaskGetInfo()函数 xTaskGetApplicationTaskTag()函数 xTaskGetCurrentHandle()函数 xTaskGetHandle()函数 xTask…

2022-2023全国高校计算机能力挑战赛区域赛python组编程题

mi目录 2022 1. 2. 1. 使用 format() 方法 2. 使用 f-string&#xff08;Python 3.6 及以上&#xff09; 2023 1. 2. 3. 4 闽农大宝玲楼 2022 1. 1.某动物研究员给动物园的动物们定了一个园区幸福值&#xff0c;其中园区幸福值的计算为一个园区内“所有动物的活动时…

在Element Ui中支持从系统粘贴版中获取图片和PDF,Docx,Doc,PPT等文档

在上一篇中&#xff0c;我们单纯的实现了Ctrl V实现从粘贴版中获取图片信息&#xff0c;但是点击上传的时候会有个bug&#xff0c;就是点击文件上传的时候&#xff0c;会出现一个bug&#xff0c;这篇&#xff0c;我们将在上一篇的基础上进行完善&#xff0c;并支持从粘贴版中获…

【Android学习】2024最新版Android Studio安装与配置

准备工作 Windows系统的要求 一、下载 Android Studio官网&#xff1a;https://developer.android.google.cn/studio?hlen 今天是2024年9月27日&#xff0c;Android Studio已经更新到了Koala版本 直接下载 二、安装 笔者当前环境变量中配置的JDK版本为1.8 双击.exe文件运行…

如何解决JAVA程序通过obloader并发导数导致系统夯住的问题 | OceanBase 运维实践

案例背景 某保险机构客户的数据中台&#xff0c;自系统上线后不久&#xff0c;会定期的用 obload 工具从上游业务系统导入数据至OceanBase数据库。但&#xff0c;不久便遇到了应用服务器的 Memory 与 CPU 资源占用持续攀升&#xff0c;最终导致系统夯住而不可用的异常。 memo…

2分钟在阿里云ECS控制台部署个人应用(图文示例)

作为一名程序员&#xff0c;我有大量的个人代码和应用托管在Github/Gitee这些代码仓库。当我想要部署这些代码到我的阿里云ECS服务器时&#xff0c;往往会很麻烦&#xff0c;主要问题有这些&#xff1a; 需要手动安装和配置git&#xff0c;过程非常繁琐。每次都需要登录到机器…

Oracle XE命令行创建数据库的一波三折(已解决)

这是一个目录 前言创建预备创建一、创建配置文件指定控制文件路径二、使用命令行创建数据库三、初始化数据库四、创建服务五、配置监听 Oracle XE尾声 前言 这篇文章是我在误装了oracle xe&#xff08;精简版数据库&#xff09;后有感而写&#xff0c;对于xe的具体区别将在文末…

第四十一章 Vue之初识VueX

目录 一、引言 1.1. vuex的概念 1.2. vuex使用场景 1.3. 优势 二、创建演示项目 2.1. 构建项目步骤 2.2. 项目最终生成结构 2.3. 创建项目文件 2.3.1. App.vue 2.3.2. Son1.vue 2.3.3. Son2.vue 三、创建一个空仓库 3.1. 安装vuex 3.2. 新建仓库 3.3. 挂载仓库…

机器学习-35-提取时间序列信号的特征

文章目录 1 特征提取方法1.1 特征提取过程1.2 两类特征提取方法2 基于数据驱动的方法2.1 领域特定特征提取2.2 基于频率的特征提取2.2.1 模拟信号2.2.2 傅里叶变换2.2.3 抽取最大幅值对应特征2.2.4 抽取峰值幅值对应特征2.3 基于统计的特征提取2.4 基于时间的特征提取3 参考附录…

My_SQL day3

知识点&#xff1a;约束 1.dafault 默认约束 2.not null 非空约束 3.unique key 唯一约束 4.primary key 主键约束 5.anto_increment 自增长约束 6.foreign key 外键约束 知识点&#xff1a;表关系 1.一对一 2.一对多 3.多对多 知识点&#xff1a;约束 1.default 默认约束 …

caozha-CEPCS(新冠肺炎疫情防控系统)

caozha-CEPCS&#xff0c;是一个基于PHP开发的新冠肺炎疫情防控系统&#xff0c;CEPCS&#xff08;全称&#xff1a;COVID-19 Epidemic Prevention and Control System&#xff09;&#xff0c;可以应用于单位、企业、学校、工业园区、村落等等。小小系统&#xff0c;希望能为大…

深度学习推荐系统的工程实现

参考自《深度学习推荐系统》——王喆&#xff0c;用于学习和记录。 介绍 之前章节主要从理论和算法层面介绍了推荐系统的关键思想。但算法和模型终究只是“好酒”&#xff0c;还需要用合适的“容器”盛载才能呈现出最好的味道&#xff0c;这里的“容器”指的就是实现推荐系统…