奇奇怪怪小问题-Java 批量添加水印

  • 需求

​ 用户选择要导出的图片数据-图片名称,打上水印,导出压缩包

  • 方案

​ 用户选择图片数据->前端发起导出请求->后端创建zip数据流->遍历图片->打上水印->添加至压缩包->前端接收二进制数据->完成下载

  • 实现

Controller

@RequestMapping("/export")
// 接收图片数据列表的方式根据请求的Content-Type决定,@RequestBody接收的是application/json
public void export(@RequestBody List<String> imageNameList, HttpServletResponse response) throws IOException {
    ...Service.export(response, imageNameList);
}

Service

/**
 * 添加水印
 *
 * @param text 水印文字
 * @param sourceImageFile 图片
 */
public BufferedImage addTextWatermark(String text, SmbFile sourceImageFile) throws IOException {
    BufferedImage sourceImage = ImageIO.read(sourceImageFile.getInputStream());
    Graphics2D g2d = (Graphics2D) sourceImage.getGraphics();
    // 设置水印透明度 0为完全透明
    AlphaComposite alphaChannel = AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.3f);
    g2d.setComposite(alphaChannel);
    // 设置水印颜色
    g2d.setColor(Color.GRAY);
    // 设置字体-Arial需确定系统中存在该字体,文字为中文需要更改为中文字体
    g2d.setFont(new Font("Arial", Font.BOLD, 128));
    // 绘制水印-高度宽度的设置可以利用window画图中图片像素查看大致位置
    g2d.drawString(text, sourceImage.getWidth() - 820, 100);
    g2d.dispose();
    return sourceImage;
}

public void export(HttpServletResponse response, List<String> imageNameList) throws IOException {

    // 不存在的图片列表
    List<String> nonExistentFiles = new ArrayList<>();
    
    // 共享文件夹需获取权限,本地文件夹不需要
    String sharePath = "smb://***//***//";
    NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication("", username, password);
    
    // 设置响应头和内容类型
    response.setHeader("Content-Disposition", "attachment;");
    response.setContentType("application/zip");
    //创建ZipOutputStream
    ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream());
    for (String fileName : imageNameList) {
        // 获取图片 --本地文件则用File
        SmbFile file = new SmbFile(sharePath + fileName + ".jpg", auth);
        if (!file.exists()) {   // 不存在则记录并跳过
            nonExistentFiles.add(fileName);
            continue;
        }
        // 打上水印,水印内容自定义
        String text = "";
        BufferedImage image = addTextWatermark(text, file);
        // 压缩已经打上水印的图片
        ZipEntry zipEntry = new ZipEntry(fileName + ".jpg");
        zipOutputStream.putNextEntry(zipEntry);
        ImageIO.write(image, "jpg", zipOutputStream);
        zipOutputStream.closeEntry();
    }
    zipOutputStream.close();
    response.getOutputStream().close();

    // 终端输出不存在的文件名
    System.out.println("All images have been processed and compressed.");
    System.out.println("Non-existent files: " + nonExistentFiles);
}

JavaScript

/*
	写在前言:如果不需要传递列表数据或者列表数据少的情况下 直接使用window.location = exportUrl,没必要使用XMLHttpRequest
	这里使用XMLHttpRequest的原因是因为列表数据多的情况下使用window.location会超过url长度限制
*/

...
获取选择的数据 imageNameList
...

// 获取按钮,禁用并改变文字,闲的话这部分可以做个进度条更美观
// 这边是采用JQ改变DOM,Vue的话可以直接改变数据动态渲染视图
var button = $('#按钮id');
button.prop('disabled', true);
button.text('正在导出图片');

var xhr = new XMLHttpRequest();
xhr.open('POST', '接口地址/export', true); // 设置请求方法和URL
xhr.responseType = 'blob'; // 设置响应类型为 blob
xhr.setRequestHeader('Content-Type', 'application/json');

// 定义 onprogress 事件处理程序,动态展示KB,XMLHttpRequest没办法像window.location流式下载,只能等blob数据全部接收完成才会开始下载。
// 该部分可省略,但这样用户看不到进度会焦虑。
xhr.onprogress = function(e) {
	// 因为压缩包是动态生成的,因此没办法展示百分比下载,这边动态展示KB
    button.text('正在导出图片 ' + Math.floor(e.loaded/1024).toString() + 'KB');
};

// 无论访问成功还是失败都会执行
xhr.onreadystatechange = function() {
    if (xhr.readyState === 4) {
       button.prop('disabled', false); // 取消按钮的禁用状态
       button.text('图片导出');	// 恢复按钮文字
    }
};

// 加载完成
xhr.onload = function (e) {
    if (xhr.status === 200) {
       // 请求成功,在这里处理响应数据
       var blob = xhr.response;
       var url = URL.createObjectURL(blob);
       var link = document.createElement('a');
       link.href = url;
       link.download = `图片${Date.now()}.zip`; // 设置下载的文件名
       link.click();
    } else {
       // 请求失败,在这里处理错误
       console.error('请求失败:' + xhr.statusText);
    }
};

// 在这里设置请求体数据,将列表参数转换为JSON格式
var requestData = JSON.stringify(imageNameList);
xhr.send(requestData); // 发送请求
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值