当前位置: 首页 > article >正文

前端全局水印, 拖拉拽图片 ,拽入等比压缩,上传服务器后,java 转base64 加水印,然后前端http预览,确认保存,拽出删除。

完整的项目,包括前端(Vue 3)和后端(Java Spring Boot),并确保所有功能都能正常工作。以下是详细的步骤:

1. 前端部分

1.1 创建 Vue 3 项目

首先,使用 Vue CLI 创建一个新的 Vue 3 项目:

bash

npm install -g @vue/cli
vue create vue-drag-and-drop-upload
cd vue-drag-and-drop-upload

在创建过程中,选择默认的 Vue 3 预设。

1.2 安装必要的依赖

安装 vuedraggableaxios

bash

npm install vuedraggable axios
1.3 创建组件 DragAndDropUpload.vue

src/components 目录下创建 DragAndDropUpload.vue 文件,并添加以下代码:

html

<template>
  <div class="drag-and-drop-container">
    <div class="watermark"></div>
    <div
      class="drop-area"
      @dragenter.prevent
      @dragover.prevent
      @drop.prevent="onDrop"
    >
      <p>Drag & Drop images here or click to upload</p>
      <input type="file" multiple @change="onFileChange" accept="image/*" />
    </div>

    <draggable
      :list="images"
      item-key="id"
      @end="onDragEnd"
      class="image-list"
    >
      <template #item="{ element }">
        <div class="image-item">
          <img :src="element.url" alt="Uploaded Image" />
          <button @click="removeImage(element)">Remove</button>
          <button v-if="!element.uploaded" @click="uploadImage(element.blob)">Upload</button>
          <span v-else>Uploaded</span>
        </div>
      </template>
    </draggable>
  </div>
</template>

<script>
import { ref, onMounted } from 'vue';
import draggable from 'vuedraggable';
import axios from 'axios';

export default {
  name: 'DragAndDropUpload',
  components: {
    draggable,
  },
  setup() {
    const images = ref([]);

    const onDrop = (event) => {
      const files = event.dataTransfer.files;
      handleFiles(files);
    };

    const onFileChange = (event) => {
      const files = event.target.files;
      handleFiles(files);
    };

    const handleFiles = (files) => {
      for (let i = 0; i < files.length; i++) {
        const file = files[i];
        if (file.type.startsWith('image/')) {
          compressImage(file).then(compressedBlob => {
            const reader = new FileReader();
            reader.onload = (e) => {
              images.value.push({
                id: Date.now() + i,
                url: e.target.result,
                blob: compressedBlob,
                uploaded: false,
              });
            };
            reader.readAsDataURL(compressedBlob);
          });
        }
      }
    };

    const removeImage = (image) => {
      images.value = images.value.filter(img => img.id !== image.id);
    };

    const onDragEnd = () => {
      console.log('Drag ended');
    };

    const compressImage = (file) => {
      return new Promise((resolve) => {
        const img = new Image();
        img.src = URL.createObjectURL(file);

        img.onload = () => {
          const canvas = document.createElement('canvas');
          let width = img.width;
          let height = img.height;

          // Maintain aspect ratio and set maximum dimensions
          const maxWidth = 800;
          const maxHeight = 600;
          if (width > height) {
            if (width > maxWidth) {
              height *= maxWidth / width;
              width = maxWidth;
            }
          } else {
            if (height > maxHeight) {
              width *= maxHeight / height;
              height = maxHeight;
            }
          }

          canvas.width = width;
          canvas.height = height;
          const ctx = canvas.getContext('2d');
          ctx.drawImage(img, 0, 0, width, height);

          canvas.toBlob(resolve, file.type, 0.8); // 0.8 is the quality of the compressed image
        };
      });
    };

    const uploadImage = (blob) => {
      const formData = new FormData();
      formData.append('file', blob);

      axios.post('http://172.168.0.1/upload', formData, {
        headers: {
          'Content-Type': 'multipart/form-data',
        },
      })
      .then(response => {
        console.log('Image processed successfully:', response.data);
        alert('Image processed successfully!');
        // Update the image preview with watermarked image
        const watermarkedUrl = `data:image/png;base64,${response.data.base64}`;
        const updatedImages = images.value.map(image => {
          if (image.blob === blob) {
            return { ...image, url: watermarkedUrl, uploaded: true };
          }
          return image;
        });
        images.value = updatedImages;
      })
      .catch(error => {
        console.error('Error processing image:', error);
        alert('Error processing image.');
      });
    };

    const addGlobalWatermark = () => {
      const watermarkDiv = document.createElement('div');
      watermarkDiv.className = 'global-watermark';
      watermarkDiv.textContent = 'Sample Watermark';
      document.body.appendChild(watermarkDiv);
    };

    onMounted(() => {
      addGlobalWatermark();
    });

    return {
      images,
      onDrop,
      onFileChange,
      handleFiles,
      removeImage,
      onDragEnd,
      compressImage,
      uploadImage,
    };
  },
};
</script>

<style scoped>
.drag-and-drop-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-top: 20px;
}

.drop-area {
  border: 2px dashed #ccc;
  padding: 20px;
  text-align: center;
  width: 300px;
  position: relative;
}

.drop-area input[type="file"] {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
}

.image-list {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  margin-top: 20px;
}

.image-item {
  position: relative;
  margin: 10px;
}

.image-item img {
  width: 100px;
  height: 100px;
  object-fit: cover;
  border-radius: 5px;
}

.image-item button {
  position: absolute;
  bottom: 5px;
  right: 5px;
  background-color: red;
  color: white;
  border: none;
  padding: 5px 10px;
  border-radius: 5px;
  cursor: pointer;
}

.image-item span {
  position: absolute;
  bottom: 5px;
  right: 5px;
  background-color: green;
  color: white;
  padding: 5px 10px;
  border-radius: 5px;
}
</style>

<style>
.global-watermark {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 9999;
  pointer-events: none;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 30px;
  font-weight: bold;
  color: rgba(255, 255, 255, 0.3);
  transform: rotate(-45deg);
  user-select: none;
}
</style>



1.4 更新 App.vue

src/App.vue 中引入并使用 DragAndDropUpload 组件:

html

<template>
  <div id="app">
    <DragAndDropUpload />
  </div>
</template>

<script>
import DragAndDropUpload from './components/DragAndDropUpload.vue';

export default {
  name: 'App',
  components: {
    DragAndDropUpload,
  },
};
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>



1.5 构建 Vue 项目

构建项目以生成静态文件:

bash

npm run build

这将在 dist 目录中生成构建后的文件。

2. 后端部分

2.1 创建 Spring Boot 项目

使用 Spring Initializr 创建一个新的 Spring Boot 项目。选择以下依赖:

  • Spring Web
  • Spring Boot DevTools

下载项目并解压。

2.2 添加依赖

pom.xml 中添加必要的依赖:

xml

<dependencies>
    <!-- Spring Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Apache Commons IO -->
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.11.0</version>
    </dependency>

    <!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>

    <!-- Test dependencies -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>



2.3 创建控制器

创建一个 UploadController.java 文件来处理文件上传和处理请求:

java

package com.example.uploadservice.controller;

import com.example.uploadservice.service.ImageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

@RestController
@RequestMapping("/api")
public class UploadController {

    @Autowired
    private ImageService imageService;

    @PostMapping("/upload")
    public ResponseEntity<?> uploadFile(@RequestParam("file") MultipartFile file) {
        String base64Image = imageService.processImage(file);
        return ResponseEntity.ok().body(base64Image);
    }
}



2.4 创建服务

创建一个 ImageService.java 文件来处理图片压缩和水印添加:

java

package com.example.uploadservice.service;

import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Service;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

@Service
public class ImageService {

    public String processImage(MultipartFile file) {
        try {
            BufferedImage originalImage = ImageIO.read(new ByteArrayInputStream(file.getBytes()));
            BufferedImage resizedImage = resize(originalImage, 800, 600);
            BufferedImage watermarkedImage = addWatermark(resizedImage, "Sample Watermark");
            return encodeToBase64(watermarkedImage);
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("Failed to process image", e);
        }
    }

    private BufferedImage resize(BufferedImage img, int maxW, int maxH) {
        int w = img.getWidth();
        int h = img.getHeight();
        float ratio = (float) w / h;
        if (w > maxW || h > maxH) {
            if (ratio >= 1) {
                h = Math.round(maxW / ratio);
                w = maxW;
            } else {
                w = Math.round(maxH * ratio);
                h = maxH;
            }
        }
        BufferedImage resizedImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = resizedImg.createGraphics();
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g2d.drawImage(img, 0, 0, w, h, null);
        g2d.dispose();
        return resizedImg;
    }

    private BufferedImage addWatermark(BufferedImage img, String watermarkText) {
        int width = img.getWidth();
        int height = img.getHeight();
        BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        Graphics2D graphics = bufferedImage.createGraphics();
        graphics.drawImage(img, 0, 0, null);
        Font font = new Font("Arial", Font.BOLD, 30);
        Color color = new Color(255, 255, 255, 128);
        graphics.setFont(font);
        graphics.setColor(color);
        FontMetrics metrics = graphics.getFontMetrics(font);
        int x = (width - metrics.stringWidth(watermarkText)) / 2;
        int y = (height - metrics.getHeight()) / 2 + metrics.getAscent();
        graphics.drawString(watermarkText, x, y);
        graphics.dispose();
        return bufferedImage;
    }

    private String encodeToBase64(BufferedImage img) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(img, "png", baos);
        byte[] bytes = baos.toByteArray();
        return IOUtils.encodeBase64String(bytes);
    }
}



2.5 运行 Spring Boot 应用

在项目根目录下运行 Spring Boot 应用:

bash

./mvnw spring-boot:run

确保防火墙允许 172.168.0.1 的 8080 端口流量。

3. Nginx 配置

3.1 创建一个新的站点配置文件

创建一个新的配置文件 /etc/nginx/sites-available/vue-drag-and-drop-upload

bash

sudo nano /etc/nginx/sites-available/vue-drag-and-drop-upload
3.2 编辑配置文件

vue-drag-and-drop-upload 文件中添加以下内容:

conf

server {
    listen 80;
    server_name example.com www.example.com;

    # Root directory of the built Vue app
    root /path/to/your/project/dist;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }

    # Proxy API requests to backend server
    location /upload {
        proxy_pass http://172.168.0.1:8080/api/upload;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}



请将 /path/to/your/project/dist 替换为你的实际项目路径。

3.3 启用站点配置

创建符号链接到 sites-enabled 目录以启用该站点配置:

bash

sudo ln -s /etc/nginx/sites-available/vue-drag-and-drop-upload /etc/nginx/sites-enabled/
3.4 测试配置并重新加载 Nginx

测试 Nginx 配置是否有语法错误:

bash

sudo nginx -t

如果没有错误,重新加载 Nginx 以应用新的配置:

bash

sudo systemctl reload nginx

总结

通过以上步骤,你已经成功实现了以下功能:

  • Vue 3.0 项目:使用 Vue CLI 创建项目,并实现拖拽图片上传、等比压缩和前端预览。
  • 图片压缩:使用 canvas 进行图片等比压缩。
  • 图片预览:在前端预览压缩后的图片。
  • 图片上传:使用 axios 将压缩后的图片上传到 172.168.0.1 服务器。
  • Java 服务端:使用 Spring Boot 处理图片上传、添加水印并返回 Base64 格式的图片。
  • Nginx 配置:配置 Nginx 以提供静态文件服务,并设置反向代理以处理 API 请求,使用 HTTP 模式。
  • 图片删除:支持从列表中删除图片。
  • 全局水印:在页面上添加全局水印。

希望这个完整的示例对你有所帮助!


http://www.kler.cn/a/488793.html

相关文章:

  • 跟着逻辑先生学习FPGA-第六课 无源蜂鸣器发声实验
  • Apache Sedona和Spark将geojson瓦片化例子
  • Linux:深入了解fd文件描述符
  • RocketMQ 和 Kafka 有什么区别?
  • Perturbed-Attention Guidance(PAG) 笔记
  • DAY15 神经网络的参数和变量
  • VS Code 可视化查看 C 调用链插件 C Relation
  • 腾讯云AI代码助手编程挑战赛-知识百科AI
  • Unity3D Huatuo热更环境安装与示例项目详解
  • MYSQL------MySQL 复制MySQL Cluster 架构
  • Xsens惯性动捕技术优化人型机器人AI训练流程
  • 搭建docker私有化仓库Harbor
  • flask-admin 在modelview 默认视图下重写create_model_actions来实现列表数据的批量处理actions
  • (长期更新)《零基础入门 ArcGIS(ArcMap) 》实验六----流域综合处理(超超超详细!!!)
  • 2025年第三届“华数杯”国际赛A题解题思路与代码(Matlab版)
  • 《Spring Framework实战》5:Spring Framework 概述
  • [微服务]redis数据结构
  • C#实现二维码和条形码识别:OpenCvSharp教程
  • WebRtc03: Web服务器原理和Nodejs搭建
  • 回归预测 | MATLAB实LSTM多输入单输出回归预测
  • [Git] git reset --hard / git reset --soft
  • Perl语言的数据结构
  • STM32的存储结构
  • abap安装cl_json类
  • 玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖
  • Flutter项目开发模版,开箱即用(Plus版本)