惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Vercel News
Vercel News
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
雷峰网
雷峰网
有赞技术团队
有赞技术团队
罗磊的独立博客
博客园 - 叶小钗
Jina AI
Jina AI
博客园 - 司徒正美
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
T
Tailwind CSS Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
人人都是产品经理
人人都是产品经理
Apple Machine Learning Research
Apple Machine Learning Research
阮一峰的网络日志
阮一峰的网络日志
Microsoft Security Blog
Microsoft Security Blog
大猫的无限游戏
大猫的无限游戏
量子位
MyScale Blog
MyScale Blog
V
Visual Studio Blog
博客园 - 聂微东
The Cloudflare Blog
Engineering at Meta
Engineering at Meta
小众软件
小众软件
宝玉的分享
宝玉的分享

Allen Hua 的网络博客

由于 Linux 桌面没有一个好用的天气程序就写了一个跨平台的CheckitoutWeather 给 caesium-image-compressor 图片压缩程序构建了 Linux AppImage v2.8.5 最新版 debian13(debian trixie)安装了nvidia闭源驱动后从x11切换到wayland的方法 山间摩旅追风,偶遇一场绚烂晚霞 最近使用debian系统的一些心得 机械革命无界14Pro笔记本debian forky成功驱动内置扬声器和麦克风 记录 typecho 1.2.0 升级到 1.3.0 过程 开发了一款openwrt插件:文本剪贴板 解决机械革命笔记本内屏高刷240Hz闪屏问题 使用 Java 写了一个局域网端口扫描器 - Allen Hua 的网络博客 openwrt使用外置根extroot机制扩展根分区大小 - Allen Hua 的网络博客 将机场ss节点批量转换成ss字符串链接批量添加到passwall - Allen Hua 的网络博客 给机械革命钛钽plus换屏:NY2换成NZ2 - Allen Hua 的网络博客 记录一次pve宿主机和上面的debian虚机无故down机事件 - Allen Hua 的网络博客 给图床部署cdn腾讯云的edgeone并排查Cache-Control max-age 3600的问题 - Allen Hua 的网络博客 从高山草甸到徽派古村:武功山反穿与皖浙赣自驾行记 - Allen Hua 的网络博客 增程器就是充电宝?别被忽悠了 - Allen Hua 的网络博客 浦口龙虎巷扫街,记录人间真实 博客图片压缩方案更新|AVIF|WebP|MozJPEG|标准JPEG Windows 电脑使用 Obs Studio 录制各个网站视频/桌面画面教程 为typecho博客添加latex支持 新能源汽车之纯电车使用交流慢充和直流快充的充电损耗对比 2025年3月更新全国5A景区名录 2025最新查看小米/红米手机电池健康度和循环次数方法 完美解决 seafile FILE_SERVER_ROOT 配置导致的内网外网不能同时访问和上传下载的问题 纯css实现typecho博客文章文字spoiler剧透效果 我对Typecho Facile主题的一些修改,图片懒加载优化,样式定制 2024年8月我的宜昌 - 重庆 - 川西小环线自驾旅行分享 2022年打卡南京市区人防工程纳凉点 2024年带着A7C2+腾龙28-200 再次来到红山森林动物园
压缩剪贴板内图片并立即写入剪贴板,减轻服务器存储压力
Allen Hua · 2023-11-03 · via Allen Hua 的网络博客

压缩剪贴板内图片并立即写入剪贴板,减轻服务器存储压力

阅读量:3452

warning: 这篇文章距离上次修改已过935天,其中的内容可能已经有所变动。

更新日志

  • v1.0.1 发现 jdk 内置的压缩效果实在太差,最终引入了 net.coobirdthumbnailator 库进行图片的压缩
  • v1.0.0 最初版本发布

背景

在使用 self-built chatting system 聊天时,用的是非官方的服务器,有时候从 X 或者 TG 上粘贴一张图片过来,9 MB 的大小让人深感 vps 硬盘不够大,得省着点用。所以我想压缩一下图片再粘贴到聊天框,于是我就寻找一个简单快捷的工作流,但是很遗憾我没有找到。

于是我用 Java 写了一段代码实现了这个逻辑

  • 从其他任意地方复制图片(此时系统剪贴板里应是一张图片)
  • 执行一条命令,直接将压缩后的图片写入剪贴板(此时剪贴板里是压缩后的图片)
  • 直接在聊天页面中粘贴,此时粘贴的是压缩后的图片,达到了我们想要照顾服务器硬盘太小的这个目的

使用效果

我用自建的 Mattermost 举例

压缩前

mm压缩前.jpg

压缩后

mm 压缩后.jpg

压缩前后对比

compress 压缩前后对比.jpg

源码实现

基于 OpenJDK11 开发和构建,打包成了 jar,全部使用 jdk 内嵌包,没有引入第三方图片压缩的库,按理说可以引入,因为压缩后的成片质量可能比自带的 Graphics2D g = resImage.createGraphics(); g.drawImage(image, 0, 0, newWidth, newHeight, null); 效果好,但我此处的需求很简单,就是聊天时用用,对于图片的清晰度可能要求不是很高,有些文字很多的都快包浆了的那种图片也不适合压缩后再发送。

源码

import net.coobird.thumbnailator.Thumbnails;

import javax.imageio.ImageIO;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.DecimalFormat;

/**
 * @author: hellodk
 * @description CompressImageAndPaste
 * @date: 11/3/2023 1:13 PM
 */

public class CompressImageAndPaste {

    /**
     * 压缩倍率
     */
    private static final double compress_ratio = 0.4;

    public static void main(String[] args) {
        CompressImageAndPaste ciap = new CompressImageAndPaste();
        BufferedImage srcImage = ciap.readImageFromClipboard();
        if (srcImage == null) {
            return;
        }
        long sizeInBytes = ciap.getImageSizeInBytes(srcImage);
        String sizeInHumanReadable = ciap.formatSize(sizeInBytes);
        System.out.println("Original image size is " + sizeInHumanReadable + ".");
        BufferedImage compressedImage = ciap.compressImage(srcImage, compress_ratio); // 压缩比例设置为 0.4
        ciap.writeImage2Clipboard(compressedImage);
        long newSizeInBytes = ciap.getImageSizeInBytes(compressedImage);
        String newSizeInHumanReadable = ciap.formatSize(newSizeInBytes);
        System.out.print("Compressed image size is " + newSizeInHumanReadable);
        System.out.println(", and has been written to your clipboard, you can paste it anywhere.");
    }

    private BufferedImage readImageFromClipboard() {
        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
        Transferable transferable = clipboard.getContents(null);
        if (transferable != null && transferable.isDataFlavorSupported(DataFlavor.imageFlavor)) {
            try {
                return (BufferedImage) transferable.getTransferData(DataFlavor.imageFlavor);
            }
            catch (UnsupportedFlavorException | IOException e) {
                e.printStackTrace();
            }
        }
        else {
            System.out.println("Make sure your clipboard latest item is image and continue.");
            System.out.println("Jar usage description: read your clipboard image and compress it, finally write the compressed image to your clipboard, you can paste it anywhere.");
        }
        return null;
    }

    private BufferedImage compressImage(BufferedImage image, double compressRatio) {
        BufferedImage resImage = null;
        try {
            Path tmpFilePath = Files.createTempFile("compressed_image", ".jpg");
            File targetFile = tmpFilePath.toFile();
            Thumbnails.of(image)
                    .scale(1)
                    .outputQuality(compressRatio)
                    .toFile(targetFile);
            resImage = ImageIO.read(targetFile);
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        return resImage;
    }

    private void writeImage2Clipboard(BufferedImage image) {
        Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
        ImageTransferable it = new ImageTransferable(image);
        cb.setContents(it, null);
    }

    private long getImageSizeInBytes(BufferedImage image) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try {
            ImageIO.write(image, "jpg", outputStream);
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        byte[] compressedImageBytes = outputStream.toByteArray();
        long sizeInBytes = compressedImageBytes.length;
        return sizeInBytes;
    }

    private String formatSize(long sizeInBytes) {
        String[] units = {"B", "KB", "MB", "GB", "TB"};
        int unitIndex = 0;
        double size = sizeInBytes;
        while (size >= 1024 && unitIndex < units.length - 1) {
            size /= 1024;
            unitIndex++;
        }
        DecimalFormat df = new DecimalFormat("#.##");
        return df.format(size) + " " + units[unitIndex];
    }

    // 自定义Transferable类,用于将BufferedImage对象传输到剪贴板
    private static class ImageTransferable implements Transferable {
        private final BufferedImage image;

        public ImageTransferable(BufferedImage image) {
            this.image = image;
        }

        @Override
        public DataFlavor[] getTransferDataFlavors() {
            return new DataFlavor[]{DataFlavor.imageFlavor};
        }

        @Override
        public boolean isDataFlavorSupported(DataFlavor flavor) {
            return flavor.equals(DataFlavor.imageFlavor);
        }

        @Override
        public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
            if (flavor.equals(DataFlavor.imageFlavor)) {
                return image;
            }
            else {
                throw new UnsupportedFlavorException(flavor);
            }
        }
    }
}

使用方法

发布到了 GitHub: https://github.com/hellodk34/CompressImageAndPaste ,从 release 页面下载 jar 包(最新版本 v1.0.1)。需要在电脑上安装 jdk11 环境,然后直接执行

java -jar compress_image_and_paste.jar

即可。

一些执行日志:

java -jar compress_image_and_paste.jar
Make sure your clipboard latest item is image and continue.
Jar usage description: read your clipboard image and compress it, finally write the compressed image to your clipboard, you can paste it anywhere.

java -jar compress_image_and_paste.jar
Original image size is 9.18 MB.
Compressed image size is 1.47 MB, and has been written to your clipboard, you can paste it anywhere.