提示
由于我需要读取多个图片生成word,之前都是使用thumbnailator进行压缩,但是这个工具如果输入的图片很大会很容易导致内存溢出,因为thumbnailator的每个图片的大小都是当前图片的四倍,每次都是小图片可以使用这个工具
GraphicsMagick 是一套功能强大、稳定且高效的图像处理软件套件,它支持超过88种主要的图像文件格式(包括重要的如DIR、TIFF、JPEG、PNG、PDF、PS等),并提供了丰富的图像处理操作。它是从ImageMagick项目分叉出来的,旨在提供一个更稳定、更快捷、更易维护的分支。 官网
GraphicsMagick适合需要高效处理大量图像的应用场景,如Web服务器端图片处理、批处理作业、自动化工作流中的图像优化等。由于其高性能和可靠性,很多互联网公司和服务提供商都选择使用GraphicsMagick来进行日常的图像处理工作。如果你正在寻找一个稳健且高效的图像处理解决方案,GraphicsMagick无疑是一个值得考虑的选择。
选择合适的版本下载 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
执行以下安装命令
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
javaimport 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;
}
}
xml <dependency>
<groupId>org.im4java</groupId>
<artifactId>im4java</artifactId>
<version>1.4.0</version>
</dependency>
javaimport 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;
}
}
}
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 许可协议。转载请注明出处!
预览: