如何让你的图片服务也有类似OSS的图片处理功能

原文链接

前言

有自己机房的公司一般都有一套存储系统用于存储公司的图片、视频、音频、文件等数据,常见的存储系统有以NAS、FASTDFS为代表的传统文件存储,和以Minio为代表的对象存储系统,随着云服务的兴起很多公司逐渐将数据迁移到以阿里云OSS为代表的云对象存储,OSS的好处是不但解决了数据的存储还自带的很多文件的处理功能,如图片的缩放、打水印、裁剪等功能,例如我们要获获取一张宽为200大小的图片只需要在原图后面增加?x-oss-process=image/resize,w_200这个参数就可以了,处理图片确实非常方便。
https://oss-console-img-demo-cn-hangzhou.oss-cn-hangzhou.aliyuncs.com/example.jpg?x-oss-process=image/resize,w_200

传统的NAS、FASTDFS只有存储功能,没有文件处理能力的,我们可以使用图片处理软件给它加上类似的图片处理功能。常用的图片处理软件有ImagemagickGraphicsMagickOpenCV对应的JAVA操作库为im4javaJMagickJavacv。用上这些图片处理软件再配置Nginx+Lua或者OpenResty就可以让你的传统NAS存储也也有像OSS一样的图片处理能力。

ImageMagick、GraphicsMagick的安装

ImageMagick官网下载

官网下载地址:https://imagemagick.org/script/download.php

以windows为例,下载ImageMagick-7.1.1-15-Q16-HDRI-x64-dll.exe ,然后下一步、下一步安装就好了,安装完成后输入magick -version检查是否安装成功:

shell 复制代码
PS C:\Users\Administrator> magick -version
Version: ImageMagick 7.1.1-15 Q16-HDRI x64 a0a5f3d:20230730 https://imagemagick.org
Copyright: (C) 1999 ImageMagick Studio LLC
License: https://imagemagick.org/script/license.php
Features: Cipher DPC HDRI Modules OpenCL OpenMP(2.0)
Delegates (built-in): bzlib cairo flif freetype gslib heic jng jp2 jpeg jxl lcms lqr lzma openexr pangocairo png ps raqm raw rsvg tiff webp xml zip zlib
Compiler: Visual Studio 2022 (193532217)

GraphicsMagick官网下载

GraphicsMagick最初源于ImageMagick5.5.2(2002年11月),但从那时起就完全独立于ImageMagick项目。自从ImageMagick的fork以来,许多作者使用开放的开发模型进行了许多改进(参见新闻),但没有破坏API或实用程序操作。下载地址:
http://www.graphicsmagick.org/download.html

安装完成后输出gm检测是否安装成功。

js 复制代码
C:\Users\Administrator>gm
GraphicsMagick 1.3.40 2023-01-14 Q16 http://www.GraphicsMagick.org/
Copyright (C) 2002-2023 GraphicsMagick Group.
Additional copyrights and licenses apply to this software.
See http://www.GraphicsMagick.org/www/Copyright.html for details.
Usage: gm command [options ...]

JAVA操作

JAVA操作GraphicsMagick可以使用im4java

引入POM依赖im4java

js 复制代码
        <dependency>
            <groupId>org.im4java</groupId>
            <artifactId>im4java</artifactId>
            <version>1.4.0</version>
        </dependency>

im4java官网地址:
https://im4java.sourceforge.net

im4java源码地址:im4java download | SourceForge.net

JAVA操作ImageMagick可以使用JMagick,引入POM依赖:

xml 复制代码
<dependency>
    <groupId>jmagick</groupId>
    <artifactId>jmagick</artifactId>
    <version>6.6.9</version>
</dependency>

JMagick官网地址:www.jmagick.org

JMagick源码地址:JMagick download | SourceForge.net

图片信息获取

命令获取图片信息

js 复制代码
 magick identify .\1.jpg
.\1.jpg PNG 1920x1080 1920x1080+0+0 8-bit sRGB 1.31707MiB 0.000u 0:00.000

//格式化输出
magick identify -format '%W,%H,%B,%f,%m'  .\1.jpg
1920,1080,1381050,1.jpg,PNG

JAVA获取图片信息

js 复制代码
    @Test
    public void info() throws IOException, InterruptedException, IM4JavaException {
        String iImageDir = "C:\\Users\\Administrator\\Desktop\\img\\1.jpg";
        IMOperation operation = new IMOperation();
        //格式化输出
        //operation.format("%W,%H,%B,%f,%m");
        operation.addImage(iImageDir);
        IdentifyCmd indentity = new IdentifyCmd();
        ArrayListOutputConsumer output = new ArrayListOutputConsumer();
        indentity.setOutputConsumer(output);
        indentity.run(operation);
        ArrayList<String> cmdOutput = output.getOutput();
        String line = cmdOutput.get(0);
        System.out.println(line);
        //C:\Users\Administrator\Desktop\img\1.jpg PNG 1920x1080 1920x1080+0+0 8-bit sRGB 1.31707MiB 0.000u 0:00.000
    }

注意事项

  1. 如果运行代码报org.im4java.core.CommandException: java.io.IOException: Cannot run program "identify": CreateProcess error=2, 系统找不到指定的文件。是因为刚安装软件,系统没找到命令,重启电脑后就可以解决。

  2. 可以使用-format '%W,%H,%B,%f,%m'来格式化图片信息的输出 ,具体的格式化参数有很多,详细可以参考官方文档,你想要的图片信息应该都是有的。https://imagemagick.org/script/escape.phps

图片缩放

命令图片缩放

js 复制代码
magick .\1.jpg -resize 200x100 1_w200h100.jpg
magick identify -format '%W,%H,%B,%f,%m'  .\1_w200h100.jpg
输出:
178,100,8003,1_w200h100.jpg,JPEG

JAVA图片缩放

js 复制代码
    @Test
    public void resizeImg() throws IOException, InterruptedException, IM4JavaException {
        String srcImagePath = "C:\\Users\\Administrator\\Desktop\\img\\1.jpg";
        Integer width = 200;
        Integer height = 100;
        String newImagePath = "C:\\Users\\Administrator\\Desktop\\img\\1_w200h100.jpg";
        IMOperation op = new IMOperation();
        op.addImage(srcImagePath);
        op.resize(width, height);
        op.addImage(newImagePath);
        ImageCommand convert = new ConvertCmd();
        convert.run(op);
    }

注意事项

这里我们使用 -resize 200x100 想生成一张200x100的图片,结果输出图片是178x100,因为原始图片为1920x1080为了保持图片宽高比例,做了等比例缩放,防止图片变形。

图片打水印

命令图片打水印

js 复制代码
 magick composite  -geometry  '200x200+100+50'  -gravity  'center'  .\avatar.png .\1.jpg  1_avater.jpg

JAVA图片打水印

js 复制代码
    @Test
    public void testaddImgWatermark() throws Exception {
        String srcImagePath="D://img/1.jpg";
        String destImagePath="D://img/1_avater.jpg";
        String waterImgPath="D://img/avatar.png";
        IMOperation op = new IMOperation();
        //水印大小
        op.geometry(1000,1000,1000,500);
        // 水印图片位置NorthWest, North, NorthEast, West, Center, East, SouthWest, South, SouthEast
        op.gravity("NorthEast");
        // 水印透明度
        op.dissolve(100);
        // 水印
        op.addImage(waterImgPath);
        // 原图
        op.addImage(srcImagePath);
        // 目标
        op.addImage(destImagePath);
        ImageCommand cmd = new CompositeCmd();
        cmd.run(op);
    }

注意事项

  1. -geometry '200x200+100+50' 中的200x200是设置水印图片的大小,+200+50设置水印图片相对于gravity的位置
  2. -gravity 'center'是给定水印的相对原图的位置,支持NorthWest, North, NorthEast, West, Center, East, SouthWest, South, SouthEast,这个参数和阿里OSS加水印的也是一样的。

最终打出水印的效果如下:

图片裁剪

命令图片裁剪

js 复制代码
magick convert -gravity  'center'   .\1_avater.jpg -crop  200x200+100+50 1_cut.jpg

JAVA图片裁剪

js 复制代码
    @Test
    public  void testCrop()  throws Exception {
        String srcImagePath="D://img//1.jpg";
        String destImagePath="D://img/1_cut.jpg";
        IMOperation op = new IMOperation();
        op.gravity("center");
        op.addImage(srcImagePath);
        op.crop( 200,200,100,50);
        op.addImage(destImagePath);
        ImageCommand cmd  = new ConvertCmd();
        cmd.run(op, srcImagePath, destImagePath);
    }

最张裁剪效果:

注意事项

  1. -crop 200x200+100+50 结合 -gravity 'center'刚好可以把我们打上的水印裁剪出来;
  2. 裁剪出的图片在左右两边还是有一点红色边框,这个可能是计算有一两个像素误差。

图片加参数自动处理

有了以上的图片处理命令之后想要实现OSS ?x-oss-process=image/resize,w_200这样加参数处理图片可以使用Nginx+Lua来实现,集成了Lua模块的Nginx项目OpenResty

例如我们原始图片地址:

http://127.0.0.1/img/1.jpg

自动缩放图片尺寸:

http://127.0.0.1/img/1_400x400.jpg

  1. 下载安装 OpenResty - Download
  2. 下载安装LUA Release Lua for Windows v5.1.5-52 Released · rjpcomputing/luaforwindows · GitHub

Nginx配置引入Lua:

js 复制代码
http {
    lua_package_path 'D:\software\openresty\lualib\\?.lua;;';
    lua_package_cpath 'D:\software\openresty\lualib\\?.so;;';
}

Nginx配置文件中可以写成这样:

js 复制代码
 location ~ '/img/(\d+)_(\d+)x(\d+).jpg$' {
	            root D:/img;
		    set $img_root  "D:/img/img";
		    set $fileName  ngx.arg[1];
		    set $width  ngx.arg[2];
		    set $height  ngx.arg[3];
		    set $origin "${img_root}/${fileName}.jpg" ;
		    set $file  "${img_root}/${fileName}_${width}x${height}.jpg";
		    if (!-f $file) {
			rewrite_by_lua '
			    local command = "magick  "..ngx.var.origin.." -resize "..ngx.var.width.."x" ..ngx.var.height.." "..ngx.var.file;
			    os.execute(command);
			 ';
		 }

	 }

当访问(http://127.0.0.1/img/1_400x400.jpg时可动态生成对应尺寸图片,类似OSS的功能:

生成的文件,当然我们还可以使用Lua+Redis缓存这里生成的文件,如果有CDN还可以配置CDN缓存这些文件,下次就可以从CDN缓存直接取对应尺寸的图片了,减少服务器处理图片的性能消耗。很多大厂不将这种图片处理下沉到CDN边缘节点,利用离用户最近CDN节点完成图片数据的处理,减少数据回源,从而减少中心服务器的性能消耗。

当然这个只是使用Nginx+Lua+GraphicsMagick来实现简单的图片裁剪功能,如果要实现阿里OSS图片处理要比这个复杂的多,不仅要解决大量文件存储高可要和自动扩容问题,还要解决高并发下图片裁剪的性能问题,们只是通过这个案例了解图片自动缩放的基本原理,原理看起来比较简单,想要做的好而且还要给全国那么多企业用,要保持高可用、高性能就比较有难度了。

总结

本文主要介绍了常用图片处理软件ImageMagick的使用,并通过命令和JAVA代码演示图片信息的获取、缩放、打水印、裁剪功能,在最后通过OpenResty+Lua实现类似OSS的自动图片缩放功能。

相关推荐
冷琴19966 分钟前
基于java+springboot的酒店预定网站、酒店客房管理系统
java·开发语言·spring boot
daiyang123...32 分钟前
IT 行业的就业情况
java
爬山算法1 小时前
Maven(6)如何使用Maven进行项目构建?
java·maven
.生产的驴1 小时前
Electron Vue框架环境搭建 Vue3环境搭建
java·前端·vue.js·spring boot·后端·electron·ecmascript
爱学的小涛1 小时前
【NIO基础】基于 NIO 中的组件实现对文件的操作(文件编程),FileChannel 详解
java·开发语言·笔记·后端·nio
吹老师个人app编程教学1 小时前
详解Java中的BIO、NIO、AIO
java·开发语言·nio
爱学的小涛1 小时前
【NIO基础】NIO(非阻塞 I/O)和 IO(传统 I/O)的区别,以及 NIO 的三大组件详解
java·开发语言·笔记·后端·nio
北极无雪1 小时前
Spring源码学习:SpringMVC(4)DispatcherServlet请求入口分析
java·开发语言·后端·学习·spring
琴智冰1 小时前
SpringBoot
java·数据库·spring boot
binqian1 小时前
【SpringSecurity】基本流程
java·spring