首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

SpringMVC 實現圖片文字水印完整教程(含 JSP 前端)

     編輯:Java 相關知識

SpringMVC 實現圖片文字水印完整教程

本文提供一個基於 SpringMVC + JSP 的完整圖片文字水印實現方案,涵蓋項目配置、控制器、服務層、水印核心算法及前端頁面,適合 Java Web 開發者快速集成水印功能。

項目結構概覽

  • 框架:SpringMVC 4.x + JSP + JSTL
  • 核心依賴:Spring Web、Spring Context、Commons FileUpload、Commons IO
  • 水印技術:Java AWT (Graphics2D, AlphaComposite) + JAI (JPEG 編碼)

1. 核心配置文件

web.xml - 核心 Servlet 配置

<web-app version="3.1" xmlns="https://xmlns.jcp.org/xml/ns/javaee">
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

springmvc.xml - SpringMVC 核心配置

<beans xmlns="https://www.springframework.org/schema/beans"
       xmlns:mvc="https://www.springframework.org/schema/mvc"
       xmlns:context="https://www.springframework.org/schema/context">

    <mvc:annotation-driven/>
    <mvc:default-servlet-handler/>
    <context:component-scan base-package="com.wenteryan"/>

    <!-- 視圖解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/"></property>
        <property name="suffix" value=".jsp"></property>
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>
    </bean>

    <!-- 文件上傳解析器:支援 10GB 大文件 -->
    <bean id="multipartResolver" 
          class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="defaultEncoding" value="UTF-8"/>
        <property name="maxUploadSize" value="10485760000"/>
        <property name="maxInMemorySize" value="40960"/>
    </bean>
</beans>

2. 控制層:WatermarkAction

@Controller
public class WatermarkAction {
    @Autowired private MarkService markService;
    @Autowired private UploadService uploadService;

    @RequestMapping(value="/watermark", method=RequestMethod.POST)
    public ModelAndView watermark(@RequestParam("image") CommonsMultipartFile file, 
                                  HttpSession session) throws Exception {
        
        String uploadPath = "/images";
        String realPath = session.getServletContext().getRealPath(uploadPath);
        
        // 1. 保存原圖
        String originalUrl = uploadService.uploadImage(file, uploadPath, realPath);
        // 2. 生成水印圖
        String watermarkUrl = markService.watermark(file, uploadPath, realPath);

        ModelAndView mv = new ModelAndView("watermark");
        mv.addObject("imageUrl", originalUrl);
        mv.addObject("logoImageUrl", watermarkUrl);
        return mv;
    }
}

3. 服務層接口與實現

MarkService 接口 - 定義水印參數常量

public interface MarkService {
    String MARK_TEXT = "wenteryan";      // 水印文字
    String FONT_NAME = "微軟雅黑";       // 字體
    int FONT_SIZE = 120;                 // 字號
    int FONT_STYLE = Font.BOLD;          // 樣式
    Color FONT_COLOR = Color.RED;        // 顏色
    int X = 10, Y = 10;                  // 起始座標
    float ALPHA = 0.3F;                  // 透明度 (0.0-1.0)

    String watermark(CommonsMultipartFile file, String uploadPath, String realPath);
}

UploadServiceImpl - 文件存儲

@Service
public class UploadServiceImpl implements UploadService {
    @Override
    public String uploadImage(CommonsMultipartFile file, String uploadPath, String realPath) {
        try (InputStream is = file.getInputStream();
             OutputStream os = new FileOutputStream(realPath + "/" + file.getOriginalFilename())) {
            
            byte[] buffer = new byte[1024];
            int len;
            while ((len = is.read(buffer)) > 0) {
                os.write(buffer, 0, len);
            }
        } catch (IOException e) {
            throw new RuntimeException("文件上傳失敗", e);
        }
        return uploadPath + "/" + file.getOriginalFilename();
    }
}

MarkServiceImpl - 水印核心算法 (關鍵實現)

@Service
public class MarkServiceImpl implements MarkService {
    @Override
    public String watermark(CommonsMultipartFile file, String uploadPath, String realPath) {
        String logoName = "logo_" + file.getOriginalFilename();
        File outputFile = new File(realPath, logoName);

        try (InputStream is = file.getInputStream();
             OutputStream os = new FileOutputStream(outputFile)) {

            // 1. 讀取原圖
            BufferedImage srcImg = ImageIO.read(is);
            int width = srcImg.getWidth();
            int height = srcImg.getHeight();

            // 2. 創建畫布
            BufferedImage buffered = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            Graphics2D g = buffered.createGraphics();
            g.drawImage(srcImg, 0, 0, width, height, null);

            // 3. 設置水印樣式
            g.setFont(new Font(FONT_NAME, FONT_STYLE, FONT_SIZE));
            g.setColor(FONT_COLOR);
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, ALPHA));

            // 4. 計算文字寬度並繪製 (居中偏移邏輯)
            int textWidth = FONT_SIZE * getTextLength(MARK_TEXT);
            int x = Math.min(X, width - textWidth);
            int y = Math.min(Y + FONT_SIZE, height); // 基線座標
            
            g.drawString(MARK_TEXT, x, y);
            g.dispose();

            // 5. 輸出 JPEG (使用 ImageIO 替代過時的 JPEGCodec)
            ImageIO.write(buffered, "JPEG", os);

        } catch (IOException e) {
            throw new RuntimeException("水印生成失敗", e);
        }
        return uploadPath + "/" + logoName;
    }

    /** 計算文字顯示寬度(中文算 2 個字符寬度) */
    private int getTextLength(String text) {
        int len = text.length();
        for (char c : text.toCharArray()) {
            if (String.valueOf(c).getBytes().length > 1) len++; // 雙字節字符
        }
        return (len % 2 == 0) ? len / 2 : len / 2 + 1;
    }
}

4. 前端頁面

index.jsp - 上傳表單

<form action="watermark" method="post" enctype="multipart/form-data">
    <h2>請選擇上傳的圖片</h2>
    <input type="file" name="image" id="image" required/>
    <button type="submit" class="btn btn-success">開始上傳並加水印</button>
</form>

watermark.jsp - 結果展示

<div class="panel-body">
    <h4>原圖</h4>
    <img src="${pageContext.request.contextPath}${imageUrl}" class="img-responsive"/>
    
    <h4>水印圖</h4>
    <img src="${pageContext.request.contextPath}${logoImageUrl}" class="img-responsive"/>
    
    <a href="${pageContext.request.contextPath}/" class="btn btn-warning">返回</a>
</div>

技術要點總結與改進建議

  1. 字體依賴:服務器需安裝 "微軟雅黑" 字體,否則會回退默認字體導致亂碼。建議打包 TTF 字體文件並通過 Font.createFont 動態加載。
  2. JPEG 編碼:原代碼使用 com.sun.image.codec.jpeg.JPEGCodec 屬於 Sun 專有 API,已過時。建議改用標準 ImageIO.write(image, "JPEG", outputStream)。
  3. 透明度處理:AlphaComposite.SRC_ATOP 僅在目標有像素處顯示源像素,配合透明度實現半透明水印效果。
  4. 擴展性:可輕鬆擴展支援圖片水印(繪製 BufferedImage)、九宮格平鋪、對角線排列等高級效果。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved