编辑
2024-12-19
学习记录
00
请注意,本文编写于 108 天前,最后修改于 108 天前,其中某些信息可能已经过时。

目录

简介
主要特点:
安装
windows
linux(ubuntu)
java使用
直接使用命令行操作
使用im4java依赖操作
添加依赖
工具类
docker挂载
可能遇到的问题

提示

由于我需要读取多个图片生成word,之前都是使用thumbnailator进行压缩,但是这个工具如果输入的图片很大会很容易导致内存溢出,因为thumbnailator的每个图片的大小都是当前图片的四倍,每次都是小图片可以使用这个工具

简介

GraphicsMagick 是一套功能强大、稳定且高效的图像处理软件套件,它支持超过88种主要的图像文件格式(包括重要的如DIR、TIFF、JPEG、PNG、PDF、PS等),并提供了丰富的图像处理操作。它是从ImageMagick项目分叉出来的,旨在提供一个更稳定、更快捷、更易维护的分支。 官网

主要特点:

  • 快速:GraphicsMagick 在性能上通常优于 ImageMagick,并且在某些情况下可以快数倍。
  • 可靠:它有着更好的代码质量和稳定性,因此在处理大量图像或关键任务时表现更为出色。
  • 轻量级:相比ImageMagick,GraphicsMagick具有更小的内存占用和更快的启动速度。
  • 广泛支持:支持大量的图像格式转换,以及多种颜色空间间的变换,同时支持透明度、Alpha通道等高级特性。
  • 跨平台:可以在Linux、Windows、macOS等多个操作系统平台上运行。
  • 命令行工具:提供了一系列易于使用的命令行工具,例如 gm 命令,用于执行图像处理任务。
  • API库:除了命令行工具外,GraphicsMagick还提供C语言API库,允许开发者将图像处理功能集成到自己的应用程序中。此外,还有许多其他语言的绑定,比如Perl, Python, Ruby等。
  • 脚本语言扩展:可以通过各种脚本语言(如PHP、Perl)轻松调用GraphicsMagick的功能,以实现动态网站上的图像处理需求。
  • 图形用户界面:虽然主要是作为后台服务和命令行工具设计的,但也存在一些第三方提供的GUI前端来简化使用。
  • 社区支持与文档:拥有活跃的社区支持和详细的官方文档,方便用户学习和解决问题。

GraphicsMagick适合需要高效处理大量图像的应用场景,如Web服务器端图片处理、批处理作业、自动化工作流中的图像优化等。由于其高性能和可靠性,很多互联网公司和服务提供商都选择使用GraphicsMagick来进行日常的图像处理工作。如果你正在寻找一个稳健且高效的图像处理解决方案,GraphicsMagick无疑是一个值得考虑的选择。

安装

windows

下载地址

选择合适的版本下载 Q8 操作图片速度更快 Q16 操作图片速度稍慢,图片显示效果更好

双击GraphicsMagick-1.3.45-Q8-win64-dll.exe文件安装,一直下一步即可,安装地址可以根据需要修改

安装完成后打开命令行面板 输入gm version

输出以下内容就是安装成功

GraphicsMagick 1.3.45 2024-08-27 Q16 http://www.GraphicsMagick.org/ Copyright (C) 2002-2024 GraphicsMagick Group. Additional copyrights and licenses apply to this software. See http://www.GraphicsMagick.org/www/Copyright.html for details. Feature Support: Native Thread Safe yes Large Files (> 32 bit) yes Large Memory (> 32 bit) yes BZIP yes FlashPix no FreeType yes Ghostscript (Library) no HEIF/HVEC ("HEIC") no JBIG yes JPEG-2000 yes JPEG yes JPEG XL no Little CMS yes Loadable Modules yes Solaris mtmalloc no Google perftools tcmalloc no OpenMP yes (200203 "2.0") PNG yes TIFF yes TRIO no Solaris umem no WebP yes WMF yes X11 no XML yes ZLIB yes Windows Build Parameters: MSVC Version: 1800

linux(ubuntu)

执行以下安装命令

sudo apt-get install graphicsmagick -y

如果提示

graphicsmagick : 依赖: libgraphicsmagick-q16-3 (>= 1.3.5) 但是它将不会被安装

就再执行

sudo apt-get -f install

再次重新安装

sudo apt-get install graphicsmagick -y

安装成功输入gm version

输入以下内容就是安装成功

GraphicsMagick 1.3.35 2020-02-23 Q16 http://www.GraphicsMagick.org/ Copyright (C) 2002-2020 GraphicsMagick Group. Additional copyrights and licenses apply to this software. See http://www.GraphicsMagick.org/www/Copyright.html for details. Feature Support: Native Thread Safe yes Large Files (> 32 bit) yes Large Memory (> 32 bit) yes BZIP yes DPS no FlashPix no FreeType yes Ghostscript (Library) no JBIG yes JPEG-2000 no JPEG yes Little CMS yes Loadable Modules no Solaris mtmalloc no Google perftools tcmalloc no OpenMP yes (201511 "4.5") PNG yes TIFF yes TRIO no Solaris umem no WebP yes WMF yes X11 yes XML yes ZLIB yes Host type: x86_64-pc-linux-gnu Configured using the command: ./configure '--build' 'x86_64-linux-gnu' '--enable-shared' '--enable-static' '--enable-libtool-verbose' '--prefix=/usr' '--mandir=${prefix}/share/man' '--infodir=${prefix}/share/info' '--docdir=${prefix}/share/doc/graphicsmagick' '--with-gs-font-dir=/usr/share/fonts/type1/gsfonts' '--with-x' '--x-includes=/usr/include/X11' '--x-libraries=/usr/lib/X11' '--without-dps' '--without-modules' '--without-frozenpaths' '--with-webp=yes' '--with-zstd=yes' '--with-perl' '--with-perl-options=INSTALLDIRS=vendor' '--enable-quantum-library-names' '--with-quantum-depth=16' 'build_alias=x86_64-linux-gnu' 'CFLAGS=-g -O2 -fdebug-prefix-map=/build/graphicsmagick-KF8Hil/graphicsmagick-1.4+really1.3.35=. -fstack-protector-strong -Wformat -Werror=format-security' 'LDFLAGS=-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now' 'CPPFLAGS=-Wdate-time -D_FORTIFY_SOURCE=2' 'CXXFLAGS=-g -O2 -fdebug-prefix-map=/build/graphicsmagick-KF8Hil/graphicsmagick-1.4+really1.3.35=. -fstack-protector-strong -Wformat -Werror=format-security' Final Build Parameters: CC = gcc CFLAGS = -fopenmp -g -O2 -fdebug-prefix-map=/build/graphicsmagick-KF8Hil/graphicsmagick-1.4+really1.3.35=. -fstack-protector-strong -Wformat -Werror=format-security -Wall -pthread CPPFLAGS = -Wdate-time -D_FORTIFY_SOURCE=2 -I/usr/include/X11 -I/usr/include/freetype2 -I/usr/include/libxml2 CXX = g++ CXXFLAGS = -g -O2 -fdebug-prefix-map=/build/graphicsmagick-KF8Hil/graphicsmagick-1.4+really1.3.35=. -fstack-protector-strong -Wformat -Werror=format-security -pthread LDFLAGS = -Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -L/usr/lib/X11 LIBS = -ljbig -lwebp -lwebpmux -llcms2 -ltiff -lfreetype -ljpeg -lpng16 -lwmflite -lXext -lSM -lICE -lX11 -llzma -lbz2 -lxml2 -lz -lzstd -lm -lpthread

java使用

直接使用命令行操作

java
import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import java.io.*; import java.net.URL; import java.nio.file.Files; import java.nio.file.Paths; import java.util.UUID; @Slf4j public class GmUtils { public static byte[] compressImage(String imageUrl, int targetWidth, int targetHeight) { // 下载图片 File tempFile = null; File output = null; try { //从url读取到本地 tempFile = File.createTempFile("tempImage", ".jpg"); // 创建临时文件 tempFile.deleteOnExit(); try (InputStream is = new URL(imageUrl).openStream()) { try (FileOutputStream fos = new FileOutputStream(tempFile)) { byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = is.read(buffer)) != -1) { fos.write(buffer, 0, bytesRead); } } }catch (Exception e){ log.error("读取大图url失败"+e.getMessage()+",Url地址:"+imageUrl); return new byte[0]; } // 使用GraphicsMagick压缩图片 output = new File(tempFile.getAbsolutePath().split("\\.")[0] + "_resized.jpg"); output.deleteOnExit(); try { // 调用GraphicsMagick命令行工具进行压缩 String command = String.format("gm convert %s -resize %dx%d %s", tempFile.getAbsolutePath(), targetWidth, targetHeight, output.getAbsolutePath()); System.out.println(command); Process process = Runtime.getRuntime().exec(command); int exitCode = process.waitFor(); if (exitCode != 0) { throw new IOException("GraphicsMagick compression failed with exit code " + exitCode); } } catch (Exception e) { throw new IOException("Error during image compression", e); } // 读取压缩后的图片并返回byte流 try (FileInputStream fis = new FileInputStream(output)) { return IOUtils.toByteArray(fis); } } catch (Exception e) { e.printStackTrace(); log.error("压缩图片出错"+e.getMessage()); } finally { // 删除临时文件 tempFile.delete(); output.delete(); } return new byte[0]; } /** * 使用GraphicsMagick根据指定URL、压缩宽高压缩图片,并返回压缩后的图片字节流,最后删除临时图片 * * @param imageUrl 图片的URL地址 * @param width 压缩后的宽度 * @param height 压缩后的高度 * @return 压缩后的图片字节流 * @throws IOException 各种可能出现的IO异常,比如读取网络图片失败、文件操作失败等 */ public static byte[] compressImagee(String imageUrl, int width, int height) throws IOException { // 1. 先从URL下载图片到本地临时文件 File tempInputFile = File.createTempFile("temp_input_", ".jpg"); try (BufferedInputStream in = new BufferedInputStream(new URL(imageUrl).openStream()); FileOutputStream fileOutputStream = new FileOutputStream(tempInputFile)) { byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = in.read(buffer))!= -1) { fileOutputStream.write(buffer, 0, bytesRead); } } // 2. 生成临时输出文件路径(使用UUID确保文件名唯一) String tempOutputFileName = UUID.randomUUID().toString() + ".jpg"; File tempOutputFile = new File(tempOutputFileName); // 3. 构建GraphicsMagick的命令行参数,执行图片压缩命令 String command = String.format("gm convert \"%s\" -resize %dx%d \"%s\"", tempInputFile.getAbsolutePath(), width, height, tempOutputFile.getAbsolutePath()); try { Process process = new ProcessBuilder(command.split(" ")).start(); process.waitFor(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new IOException("图片压缩过程被中断", e); } // 4. 读取压缩后的图片字节流 byte[] compressedImageBytes = Files.readAllBytes(Paths.get(tempOutputFile.getAbsolutePath())); // 5. 删除临时的输入文件和输出文件 tempInputFile.delete(); tempOutputFile.delete(); return compressedImageBytes; } }

使用im4java依赖操作

添加依赖

xml
<dependency> <groupId>org.im4java</groupId> <artifactId>im4java</artifactId> <version>1.4.0</version> </dependency>

工具类

java
import org.im4java.core.*; import org.im4java.process.Pipe; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.HashMap; import java.util.Map; public class GraphicsMagickUtil { // 日志 private static Logger logger = LoggerFactory.getLogger(GraphicsMagickUtil.class); // GraphicsMagic的安装路径 private static final String PATH = "D:\\GraphicsMagick-1.3.45-Q16"; /** * 压缩图片 * * @param url 图片地址 * @param imageType 图片类型 * @param width 压缩宽度 * @param height 压缩高度 * @return */ public static byte[] compressImage(String url, String imageType, double width, double height) { try(InputStream is=new URL(url).openStream()) { Map<String, String> optionsMap = new HashMap<>(); String widHeight = width + "x" + height; optionsMap.put("-scale", widHeight);// 按照给定比例缩放图片 optionsMap.put("-gravity", "center"); // 缩放参考位置 对图像进行定位 optionsMap.put("-extent", width + "x" + height); // 限制JPEG文件的最大尺寸 optionsMap.put("+profile", "*");// 去除Exif信息 return getByte(is, imageType, optionsMap); } catch (Exception e) { logger.info("压缩图片失败:", e.getMessage()); return null; } } /** * 压缩图片 * * @param sourceInputStream 源文件流 * @param imageType 图片类型 * @param width 压缩宽度 * @param height 压缩高度 * @return */ public static byte[] compressImage(InputStream sourceInputStream, String imageType, double width, double height) { try { Map<String, String> optionsMap = new HashMap<>(); String widHeight = width + "x" + height; optionsMap.put("-scale", widHeight);// 按照给定比例缩放图片 optionsMap.put("-gravity", "center"); // 缩放参考位置 对图像进行定位 optionsMap.put("-extent", width + "x" + height); // 限制JPEG文件的最大尺寸 optionsMap.put("+profile", "*");// 去除Exif信息 return getByte(sourceInputStream, imageType, optionsMap); } catch (Exception e) { logger.info("压缩图片失败:{}", e.getMessage()); return null; } } /** * 根据坐标裁剪图片 * * @param sourceInputStream 源图片流 * @param imageType 图片类型 * @param x 起始横坐标 * @param y 起始纵坐标 * @param x1 结束横坐标 * @param y1 结束纵坐标 * @return */ public static byte[] cutImage(InputStream sourceInputStream, String imageType, int x, int y, int x1, int y1) { try { int width = x1 - x; int height = y1 - y; Map<String, String> optionsMap = new HashMap<>(); String resize = width + "x" + height + "^"; String crop = width + "x" + height + "+" + x + "+" + y; optionsMap.put("-resize", resize); // 图片裁剪为宽度不超过(width)px,高度不超过(height)px的缩略图 optionsMap.put("-crop", crop); // 严格裁剪生成大小为 (width)x(height) optionsMap.put("-gravity", "center"); // 缩放参考位置 对图像进行定位 optionsMap.put("+profile", "*");// 去除Exif信息 return getByte(sourceInputStream, imageType, optionsMap); } catch (Exception e) { logger.info("根据坐标裁剪图片失败:{}", e.getMessage()); return null; } } /** * 图片格式转换 * * @param sourceInputStream 源文件流 * @param toFormatImageType 转换后的图片格式 * @param optionsMap 图片处理参数(质量等) * @return */ private static byte[] convertFormat(InputStream sourceInputStream, String toFormatImageType, Map<String, String> optionsMap) { try { return getByte(sourceInputStream, toFormatImageType, optionsMap); } catch (Exception e) { logger.info("图片格式转换失败:{}", e.getMessage()); return null; } } /** * 添加文字水印 * * @param sourceInputStream 源图片文件流 * @param imageType 图片格式 * @param waterText 水印文字内容 * @return * @throws Exception */ public static byte[] addImgText(InputStream sourceInputStream, String imageType, String waterText) { try { Map<String, String> optionsMap = new HashMap<>(); waterText = "text 100,100 '" + waterText + "'"; optionsMap.put("-font", "宋体"); // 添加水印字体 optionsMap.put("-pointsize", "30"); // 水印文字大小 optionsMap.put("-fill", "#BCBFC8");// 水印文字颜色 optionsMap.put("-draw", waterText);// 水印文字内容 return getByte(sourceInputStream, imageType, optionsMap); } catch (Exception e) { logger.info("添加文字水印:{}", e.getMessage()); return null; } } /** * 获取处理之后的图片二进制流 * * @param sourceInputStream * @param imageType * @param optionsMap * @return * @throws InterruptedException * @throws IOException * @throws IM4JavaException */ public static byte[] getByte(InputStream sourceInputStream, String imageType, Map<String, String> optionsMap) throws InterruptedException, IOException, IM4JavaException { IMOperation imOperation = buildIMOperation(imageType, optionsMap); try(ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()){ Pipe pipeIn = new Pipe(sourceInputStream, null); Pipe pipeOut = new Pipe(null, byteArrayOutputStream); ImageCommand convertCmd = getImageCommand(CommandType.convert); convertCmd.setInputProvider(pipeIn); convertCmd.setOutputConsumer(pipeOut); convertCmd.run(imOperation); return byteArrayOutputStream.toByteArray(); } } /** * 创建 IMOperation * * @param imageType 图片类型,示例:jpg、png等 * @param optionsMap 图片处理参数 * @return */ private static IMOperation buildIMOperation(String imageType, Map<String, String> optionsMap) { IMOperation imOperation = new IMOperation(); imOperation.addImage("-"); if (optionsMap != null && !optionsMap.isEmpty()) { for (Map.Entry<String, String> entry : optionsMap.entrySet()) { imOperation.addRawArgs(entry.getKey(), entry.getValue()); } } imOperation.addImage(imageType + ":-"); return imOperation; } /** * 获取 ImageCommand * * @param command 命令类型 * @return */ private static ImageCommand getImageCommand(CommandType command) { ImageCommand cmd = null; switch (command) { case convert: cmd = new ConvertCmd(true); break; case identify: cmd = new IdentifyCmd(true); break; case compositecmd: cmd = new CompositeCmd(true); break; } cmd.setSearchPath(SearchPath()); return cmd; } /** * 定义命令类型内部类 */ private enum CommandType { convert("转换处理"), identify("图片信息"), compositecmd("图片合成"); private String name; CommandType(String name) { this.name = name; } } /** *判断是windows环境还是linux环境,自动设置GraphicsMagic搜索路径 */ private static String SearchPath(){ String osName = System.getProperty("os.name").toLowerCase(); if (osName.contains("linux")) { return null; } else { return PATH; } } }

docker挂载

ubuntu宿主机安装了gm,docker容器使用需要挂载gm的安装地址和共享库

查找gm地址使用 which gm,会输出例如/usr/bin/gm

查询/usr/bin/gm的共享库输入ldd /usr/bin/gm

以下是我使用后可以实现容器执行gm命令的挂载信息

-v /usr/bin/gm:/usr/bin/gm -v /usr/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu -v /usr/lib/libGraphicsMagick-Q16.so.3:/usr/lib/libGraphicsMagick-Q16.so.3

也可以修改dockerFile的命令,将gm安装在容器中

可能遇到的问题

windows安装gm后,java项目中使用提示Cannot run program "gm": CreateProcess error=2, 系统找不到指定的文件。 你需要重启一下电脑

本文作者:Weee

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.14.8