How to covert HEIF to JPG with Java

How to covert HEIF to JPG with Java

问题描述

最近在项目中遇到个问题,业务须收集客户上传的图片格式证明材料,偶然会发现客户上传成功的图片,无法预览。 开发同学第一时间找运维同学协助排查组件问题,经查实问题是我们所使用的zimg组件作为图片服务器,图片文件存储正常,但是当预览时要生成一个临时文件时失败,导致浏览器客户端无法展示图片。后找客户要了原图,通过微信传输后重新传到zimg服务器,发现展示正常。

HEIC格式图片手动改后缀为JPG,上传后的zimg日志

##-----------download 下载日志---------------------------
2022/07/29 11:27:11:231772 [DEBUG] Method: 0
2022/07/29 11:27:11:232173 [DEBUG] Got a GET request for </c2b1b40cb1546c7f8e63597b05f941f7>
2022/07/29 11:27:11:232282 [DEBUG] md5 of request is <c2b1b40cb1546c7f8e63597b05f941f7>
2022/07/29 11:27:11:232320 [DEBUG] get_img() start processing zimg request...
2022/07/29 11:27:11:232346 [DEBUG] whole_path: /root/zimg7/zimg/bin/img/778/109/c2b1b40cb1546c7f8e63597b05f941f7
2022/07/29 11:27:11:232371 [DEBUG] Path[/root/zimg7/zimg/bin/img/778/109/c2b1b40cb1546c7f8e63597b05f941f7] is A Dir.
2022/07/29 11:27:11:232396 [DEBUG] key: c2b1b40cb1546c7f8e63597b05f941f7:0:0:1:0:-1:-1:0:75:jpeg
2022/07/29 11:27:11:232550 [DEBUG] Cache Result: SERVER HAS FAILED AND IS DISABLED UNTIL TIMED RETRY
2022/07/29 11:27:11:232580 [DEBUG] Start to Find the Image...
2022/07/29 11:27:11:232603 [DEBUG] 0rig File Path: /root/zimg7/zimg/bin/img/778/109/c2b1b40cb1546c7f8e63597b05f941f7/0*0
2022/07/29 11:27:11:232626 [DEBUG] Got the rsp_path: /root/zimg7/zimg/bin/img/778/109/c2b1b40cb1546c7f8e63597b05f941f7/0*0_p1_g0_-1*-1_r0_q75.jpeg
2022/07/29 11:27:11:232685 [DEBUG] Cache Result: SERVER HAS FAILED AND IS DISABLED UNTIL TIMED RETRY
2022/07/29 11:27:11:232711 [DEBUG] Not Hit Original Image Cache. Begin to Open it.
2022/07/29 11:27:11:233388 [DEBUG] Open Original Image From Disk Failed! 0 != 1  # 这里是报错的问题
2022/07/29 11:27:11:233423 [DEBUG] Open Original Image From Disk Failed!
2022/07/29 11:27:11:233452 [DEBUG] zimg Requset Get Image[MD5: c2b1b40cb1546c7f8e63597b05f941f7] Failed!
2022/07/29 11:27:11:233475 [ERROR] 172.16.167.1 fail pic:c2b1b40cb1546c7f8e63597b05f941f7 w:0 h:0 p:1 g:0 x:-1 y:-1 r:0 q:75 f:jpeg
2022/07/29 11:27:11:233507 [DEBUG] ============get_request_cb() ERROR!===============

正常图片上传后 0*0为源文件, 下载时会生成 0 * 0_p1_xxx.jpeg 真实文件, 而HEIC 类型的则无法生成 jpeg 文件, 具体见下方对比图:

解决方案

方案一:升级zimg依赖库

目前我们使用的zimg版本是:v3.1.0, ImageMagick版本是:6.9.10-68,系统版本:CentOS 7.9 内核版本:3.10.0-1160.el7.x86_64

# Linux版本
[root@localhost ~]# convert -version
Version: ImageMagick 6.9.10-68 Q16 x86_64 2021-10-14 https://imagemagick.org
Copyright: © 1999-2019 ImageMagick Studio LLC
License: https://imagemagick.org/script/license.php
Features: Cipher DPC Modules OpenMP(3.1) 
Delegates (built-in): bzlib cairo fontconfig freetype gslib jng jp2 jpeg lcms ltdl lzma openexr pangocairo png ps rsvg tiff wmf x xml zlib
# Mac版本7.1.0-16
 ~ convert -version
Version: ImageMagick 7.1.0-16 Q16-HDRI x86_64 2021-11-21 https://imagemagick.org
Copyright: (C) 1999-2021 ImageMagick Studio LLC
License: https://imagemagick.org/script/license.php
Features: Cipher DPC HDRI Modules OpenMP(5.0)
Delegates (built-in): bzlib fontconfig freetype gslib heic jng jp2 jpeg lcms lqr ltdl lzma openexr png ps tiff webp xml zlib
Compiler: gcc (4.2)

通过更新 ImageMagick 7.1.0-44,依然无法支持HEIC格式下载:

[root@localhost c2b1b40cb1546c7f8e63597b05f941f7]# convert -version
Version: ImageMagick 7.1.0-44 Q16-HDRI x86_64 20294 https://imagemagick.org
Copyright: (C) 1999 ImageMagick Studio LLC
License: https://imagemagick.org/script/license.php
Features: Cipher DPC HDRI OpenMP(3.1) 
Delegates (built-in): bzlib freetype jng jpeg lzma png tiff x xml zlib
Compiler: gcc (4.8)

卸载ImageMagick, 重新编译安装:无论怎么改,–with-heic的值都为no
How to covert HEIF to JPG with Java_第1张图片

后从GitHub获取7.1.0-16 版本,编译安装后,通过指定–with-heic=yes ,可以支持heic
How to covert HEIF to JPG with Java_第2张图片

测试png正常图片

2022/08/01 00:04:18:079828 [DEBUG] Method: 2
2022/08/01 00:04:18:079903 [DEBUG] evblen = 1363658
2022/08/01 00:04:18:080774 [DEBUG] rmblen = 1363658
2022/08/01 00:04:18:080816 [DEBUG] boundary Find. boundary = ----WebKitFormBoundaryxVla4BXhmubWrIuU
2022/08/01 00:04:18:080839 [DEBUG] boundaryPattern = ------WebKitFormBoundaryxVla4BXhmubWrIuU, strlen = 40
2022/08/01 00:04:18:080863 [DEBUG] File[wizard.png]
2022/08/01 00:04:18:080883 [DEBUG] fileType[png]
2022/08/01 00:04:18:080909 [DEBUG] header_value 49 form-data; name="userfile"; filename="wizard.png
2022/08/01 00:04:18:080931 [DEBUG] header_value 9 image/png
2022/08/01 00:04:18:083845 [DEBUG] Begin to Caculate MD5...
2022/08/01 00:04:18:086854 [DEBUG] md5: 951b967ec917a397d5e5111a65cb6501
2022/08/01 00:04:18:086956 [DEBUG] save_path: /root/zimg-3.1.0/bin/img/596/741/951b967ec917a397d5e5111a65cb6501
2022/08/01 00:04:18:086994 [DEBUG] Path[/root/zimg-3.1.0/bin/img/596/741/951b967ec917a397d5e5111a65cb6501] is Not Existed!
2022/08/01 00:04:18:087370 [DEBUG] save_path[/root/zimg-3.1.0/bin/img/596/741/951b967ec917a397d5e5111a65cb6501] Create Finish.
2022/08/01 00:04:18:087404 [DEBUG] save_name-->: /root/zimg-3.1.0/bin/img/596/741/951b967ec917a397d5e5111a65cb6501/0*0
2022/08/01 00:04:18:087427 [DEBUG] Start to Storage the New Image...
2022/08/01 00:04:18:088403 [DEBUG] Image [/root/zimg-3.1.0/bin/img/596/741/951b967ec917a397d5e5111a65cb6501/0*0] Write Successfully!
2022/08/01 00:04:18:088447 [INFO] 172.16.167.1 succ post pic:951b967ec917a397d5e5111a65cb6501 size:1363471
2022/08/01 00:04:18:088489 [DEBUG] ============post_request_cb() DONE!===============
2022/08/01 00:04:22:219329 [DEBUG] Method: 0
2022/08/01 00:04:22:219403 [DEBUG] Got a GET request for </951b967ec917a397d5e5111a65cb6501>
2022/08/01 00:04:22:219442 [DEBUG] md5 of request is <951b967ec917a397d5e5111a65cb6501>
2022/08/01 00:04:22:219501 [DEBUG] get_img() start processing zimg request...
2022/08/01 00:04:22:219561 [DEBUG] whole_path: /root/zimg-3.1.0/bin/img/596/741/951b967ec917a397d5e5111a65cb6501
2022/08/01 00:04:22:219592 [DEBUG] Path[/root/zimg-3.1.0/bin/img/596/741/951b967ec917a397d5e5111a65cb6501] is A Dir.
2022/08/01 00:04:22:219618 [DEBUG] key: 951b967ec917a397d5e5111a65cb6501:0:0:1:0:-1:-1:0:75:none
2022/08/01 00:04:22:219808 [DEBUG] Cache Result: SERVER HAS FAILED AND IS DISABLED UNTIL TIMED RETRY
2022/08/01 00:04:22:219838 [DEBUG] Start to Find the Image...
2022/08/01 00:04:22:219860 [DEBUG] 0rig File Path: /root/zimg-3.1.0/bin/img/596/741/951b967ec917a397d5e5111a65cb6501/0*0
2022/08/01 00:04:22:219889 [DEBUG] Got the rsp_path: /root/zimg-3.1.0/bin/img/596/741/951b967ec917a397d5e5111a65cb6501/0*0_p1_g0_-1*-1_r0_q75.none
2022/08/01 00:04:22:219942 [DEBUG] Cache Result: SERVER HAS FAILED AND IS DISABLED UNTIL TIMED RETRY
2022/08/01 00:04:22:219967 [DEBUG] Not Hit Original Image Cache. Begin to Open it.
2022/08/01 00:04:22:270994 [DEBUG] image size = 1363471
2022/08/01 00:04:22:271066 [DEBUG] wi_set_quality(im, 75)
2022/08/01 00:04:22:271094 [DEBUG] convert(im, req) 1
2022/08/01 00:04:22:579246 [DEBUG] Image[/root/zimg-3.1.0/bin/img/596/741/951b967ec917a397d5e5111a65cb6501/0*0_p1_g0_-1*-1_r0_q75.none] is Not Existed. Begin to Save it.
2022/08/01 00:04:22:579311 [DEBUG] Start to Storage the New Image...
2022/08/01 00:04:22:580189 [DEBUG] Image [/root/zimg-3.1.0/bin/img/596/741/951b967ec917a397d5e5111a65cb6501/0*0_p1_g0_-1*-1_r0_q75.none] Write Successfully!
2022/08/01 00:04:22:580646 [DEBUG] Begin to Caculate MD5...
2022/08/01 00:04:22:583114 [DEBUG] md5: 4ad1b7cc217480b97fd08a71d80369bf
2022/08/01 00:04:22:583210 [DEBUG] If-None-Match: (null)
2022/08/01 00:04:22:585089 [DEBUG] get buffer length: 1363477
2022/08/01 00:04:22:585128 [DEBUG] Got the File!
2022/08/01 00:04:22:585163 [DEBUG] headers: 1
2022/08/01 00:04:22:585257 [INFO] 172.16.167.1 succ pic:951b967ec917a397d5e5111a65cb6501 w:0 h:0 p:1 g:0 x:-1 y:-1 r:0 q:75 f:none size:1363477
2022/08/01 00:04:22:585316 [DEBUG] ============get_request_cb() DONE!===============

ok , 上传下载PNG没问题!

测试上传把HEIC转换成JPG的图片

curl -F "[email protected];type=image/jpg" "http://172.16.167.132:9998/upload"

<html>
<head>
<title>Upload Resulttitle>
head>
<body>
<h1>MD5: 8f77425b750361a53fec7c83b441ab4fh1>
Image upload successfully! You can get this image via this address:<br/><br/>
<a href="/8f77425b750361a53fec7c83b441ab4f">http://yourhostname:9998/8f77425b750361a53fec7c83b441ab4fa>?w=width&h=height&g=isgray&x=position_x&y=position_y&r=rotate&q=quality&f=format
body>
html>

测试下载:wget http://172.16.167.133:9998/8f77425b750361a53fec7c83b441ab4f

客户端直接崩溃:

zimg: magick/semaphore.c:347: LockSemaphoreInfo: Assertion `semaphore_info != (SemaphoreInfo *) ((void *)0)' failed.

zimg日志

2022/08/01 00:07:29:771755 [DEBUG] Method: 0
2022/08/01 00:07:29:771843 [DEBUG] Got a GET request for </3bb6ef7ba803f003d9609958bc720f36>
2022/08/01 00:07:29:771871 [DEBUG] md5 of request is <3bb6ef7ba803f003d9609958bc720f36>
2022/08/01 00:07:29:771893 [DEBUG] get_img() start processing zimg request...
2022/08/01 00:07:29:771915 [DEBUG] whole_path: /root/zimg-3.1.0/bin/img/238/443/3bb6ef7ba803f003d9609958bc720f36
2022/08/01 00:07:29:771940 [DEBUG] Path[/root/zimg-3.1.0/bin/img/238/443/3bb6ef7ba803f003d9609958bc720f36] is A Dir.
2022/08/01 00:07:29:771963 [DEBUG] key: 3bb6ef7ba803f003d9609958bc720f36:0:0:1:0:-1:-1:0:75:none
2022/08/01 00:07:29:772199 [DEBUG] Cache Result: SERVER HAS FAILED AND IS DISABLED UNTIL TIMED RETRY
2022/08/01 00:07:29:772229 [DEBUG] Start to Find the Image...
2022/08/01 00:07:29:772251 [DEBUG] 0rig File Path: /root/zimg-3.1.0/bin/img/238/443/3bb6ef7ba803f003d9609958bc720f36/0*0
2022/08/01 00:07:29:772272 [DEBUG] Got the rsp_path: /root/zimg-3.1.0/bin/img/238/443/3bb6ef7ba803f003d9609958bc720f36/0*0_p1_g0_-1*-1_r0_q75.none
2022/08/01 00:07:29:772324 [DEBUG] Cache Result: SERVER HAS FAILED AND IS DISABLED UNTIL TIMED RETRY
2022/08/01 00:07:29:772349 [DEBUG] Not Hit Original Image Cache. Begin to Open it.
2022/08/01 00:07:29:999538 [DEBUG] image size = 3852068
2022/08/01 00:07:29:999628 [DEBUG] wi_set_quality(im, 75)
2022/08/01 00:07:29:999645 [DEBUG] convert(im, req) 1

测试HEIC原图

上传/下载

[root@localhost ~]# curl -F "blob=@img_20220727_154323.heic;type=image/heic" "http://172.16.167.133:9998/upload"
<html>
<head>
<title>Upload Result</title>
</head>
<body>
<h1>MD5: a7a19c0d70dba023356a92ddfb31c3bf</h1>
Image upload successfully! You can get this image via this address:<br/><br/>
<a href="/a7a19c0d70dba023356a92ddfb31c3bf">http://yourhostname:9998/a7a19c0d70dba023356a92ddfb31c3bf</a>?w=width&h=height&g=isgray&x=position_x&y=position_y&r=rotate&q=quality&f=format
</body>
</html>
[root@localhost ~]# wget http://172.16.167.133:9998/a7a19c0d70dba023356a92ddfb31c3bf
--2022-08-01 00:01:45--  http://172.16.167.133:9998/a7a19c0d70dba023356a92ddfb31c3bf
Connecting to 172.16.167.133:9998... connected.
HTTP request sent, awaiting response... 404 Not Found
2022-08-01 00:01:45 ERROR 404: Not Found.

下载404日志:

2022/08/01 00:01:45:661914 [DEBUG] Method: 0
2022/08/01 00:01:45:661974 [DEBUG] Got a GET request for </a7a19c0d70dba023356a92ddfb31c3bf>
2022/08/01 00:01:45:662000 [DEBUG] md5 of request is <a7a19c0d70dba023356a92ddfb31c3bf>
2022/08/01 00:01:45:662023 [DEBUG] get_img() start processing zimg request...
2022/08/01 00:01:45:662047 [DEBUG] whole_path: /root/zimg-3.1.0/bin/img/670/103/a7a19c0d70dba023356a92ddfb31c3bf
2022/08/01 00:01:45:662072 [DEBUG] Path[/root/zimg-3.1.0/bin/img/670/103/a7a19c0d70dba023356a92ddfb31c3bf] is A Dir.
2022/08/01 00:01:45:662142 [DEBUG] key: a7a19c0d70dba023356a92ddfb31c3bf:0:0:1:0:-1:-1:0:75:none
2022/08/01 00:01:45:662301 [DEBUG] Cache Conn Failed!
2022/08/01 00:01:45:662329 [DEBUG] Start to Find the Image...
2022/08/01 00:01:45:662351 [DEBUG] 0rig File Path: /root/zimg-3.1.0/bin/img/670/103/a7a19c0d70dba023356a92ddfb31c3bf/0*0
2022/08/01 00:01:45:662374 [DEBUG] Got the rsp_path: /root/zimg-3.1.0/bin/img/670/103/a7a19c0d70dba023356a92ddfb31c3bf/0*0_p1_g0_-1*-1_r0_q75.none
2022/08/01 00:01:45:662491 [DEBUG] Cache Result: SERVER HAS FAILED AND IS DISABLED UNTIL TIMED RETRY
2022/08/01 00:01:45:662517 [DEBUG] Not Hit Original Image Cache. Begin to Open it.
2022/08/01 00:01:45:664733 [DEBUG] Open Original Image From Disk Failed! 0 != 1
2022/08/01 00:01:45:664770 [DEBUG] Open Original Image From Disk Failed!
2022/08/01 00:01:45:664843 [DEBUG] zimg Requset Get Image[MD5: a7a19c0d70dba023356a92ddfb31c3bf] Failed!
2022/08/01 00:01:45:664873 [ERROR] 172.16.167.133 fail pic:a7a19c0d70dba023356a92ddfb31c3bf w:0 h:0 p:1 g:0 x:-1 y:-1 r:0 q:75 f:none
2022/08/01 00:01:45:664912 [DEBUG] ============get_request_cb() ERROR!===============

方案二:图片压缩

这个是通过微信传输后得来的灵感,通过 thumbnailator 来对原图压缩,来测试压缩后的图片是否被zimg兼容。

<dependency>
    <groupId>net.coobirdgroupId>
    <artifactId>thumbnailatorartifactId>
    <version>0.4.8version>
dependency>

java代码:

//import org.apache.commons.io.FileUtils
/**
 * 图片压缩方式转换
 * @param source
 * @param dest
 * @throws IOException
 */
private static void thumbnailsImg(File source, File dest) throws IOException {
    byte[] sourceByte = FileUtils.readFileToByteArray(source);//读取源文件
    ByteArrayInputStream inputStream = new ByteArrayInputStream(sourceByte);
    //ByteArrayOutputStream outputStream = new ByteArrayOutputStream(sourceByte.length);
    Thumbnails.of(inputStream)
            .scale(0.75)
            .outputQuality(0.5).toFile(dest);//生成压缩后的新文件
    //byte[] bytes = outputStream.toByteArray();
    //FileUtils.writeByteArrayToFile(dest, bytes);
}
//TODO 抛出异常:
//Exception in thread "main" net.coobird.thumbnailator.tasks.UnsupportedFormatException: No suitable ImageReader found for source data.
	//at net.coobird.thumbnailator.tasks.io.InputStreamImageSource.read(Unknown Source)
	//at net.coobird.thumbnailator.tasks.SourceSinkThumbnailTask.read(Unknown Source)
	//at net.coobird.thumbnailator.Thumbnailator.createThumbnail(Unknown Source)

这个网上说要把文件先放到ByteArrayInputStream中再操作,经测试无效,方案失败。

方案三:读取原图二进制流生成新文件

通过读取原图片二进制流,copy生成临时文件后再上传到图片服务器。

//import javax.imageio.ImageIO
/**
 * 仅读取二进制信息, 不读取元数据
 * @param source
 * @param dest
 * @param formatName
 * @throws IOException
 */
private static void onlyReadByteInfo(File source, File dest, String formatName) throws IOException {
    BufferedImage srcImg = ImageIO.read(source);
    ImageIO.write(srcImg,formatName,dest);
}
// TODO 执行抛出异常
//Exception in thread "main" java.lang.IllegalArgumentException: image == null!

方案失败。

方案四:修改图片元数据信息

既然要修改图片元数据信息,得先读取元数据,开搞!

读取图片元数据信息

使用drewnoakes来读取图片元数据

<dependency>
    <groupId>com.drewnoakesgroupId>
    <artifactId>metadata-extractorartifactId>
    <version>2.18.0version>
dependency>

java代码:

//import com.drew.imaging.ImageMetadataReader;
//import com.drew.imaging.ImageProcessingException;
//import com.drew.imaging.heif.HeifMetadataReader;
//import com.drew.imaging.jpeg.JpegMetadataReader;
//import com.drew.imaging.jpeg.JpegProcessingException;
//import com.drew.metadata.Directory;
//import com.drew.metadata.Metadata;
//import com.drew.metadata.Tag;
Metadata metadata = ImageMetadataReader.readMetadata(file);
for (Directory directory : metadata.getDirectories()) {
    for (Tag tag : directory.getTags()) {
        System.out.format("[%s] - %s = %s",
            directory.getName(), tag.getTagName(), tag.getDescription());
    }
}

执行结果:

[HEIF]-Major Brand = heic[HEIF]-Minor Version = 0[HEIF]-Compatible Brands = mif1 MiHE miaf MiHB heic[HEIF]-Width = 512 pixels[HEIF]-Height = 512 pixels[HEIF]-Rotation = 0 degrees[HEIF]-Bits Per Channel = 8 8 8[ICC Profile]-Profile Size = 548[ICC Profile]-CMM Type = appl[ICC Profile]-Version = 4.0.0[ICC Profile]-Class = Display
.....省略敏感信息.......
Make = Apple[Exif SubIFD]-Lens Model = iPhone 12 Pro Max back triple camera 5.1mm f/1.6[Exif SubIFD]-Unknown tag (0xa460) = 3[Exif SubIFD]-Unknown tag (0xa461) = 10 0[Apple Makernote]-Unknown tag (0x0001) = 12[Apple Makernote]-Unknown tag (0x0002) = [558 values][Apple Makernote]-Run Time = [104 values][Apple Makernote]-Unknown tag (0x0004) = 1[Apple Makernote]-Unknown tag (0x0005) = 87[Apple Makernote]-Unknown tag (0x0006) = 81[Apple Makernote]-Unknown tag (0x0007) = 1[Apple Makernote]-Acceleration Vector = 1.00g right, 0.05g up, 0.06g backward[Apple Makernote]-Unknown tag (0x000c) = 533/256 137/128[Apple Makernote]-Unknown tag (0x000d) = 0[Apple Makernote]-Unknown tag (0x000e) = 0[Apple Makernote]-Unknown tag (0x000f) 
.....省略敏感信息.......
Makernote]-Unknown tag (0x003b) = 0[Apple Makernote]-Unknown tag (0x003c) = 4[Apple Makernote]-Unknown tag (0x003d) = 0[Apple Run Time]-Flags = Valid[Apple Run Time]-Epoch = 0[Apple Run Time]-Scale = 1000000000[Apple Run Time]-Value = 604736 seconds[File Type]-Detected File Type Name = HEIF[File Type]-Detected File Type Long Name = High Efficiency Image File Format[File Type]-Detected MIME Type = image/heif[File Type]-Expected File Name Extension = heif[File]-File Name = IMG_0905.heic[File]-File Size = 1308495 bytes[File]-File Modified Date = 周三 727 15:42:30 +08:00 2022

图片元数据信息读取成功,搞定第一步!

要小心照片泄露隐私信息,客户上传的原图留存,copy新图做业务。

修改图片元数据信息

利用 Apache sanselan,引入maven依赖:

<dependency>
    <groupId>org.apache.sanselangroupId>
    <artifactId>sanselanartifactId>
    <version>0.97-incubatorversion>
dependency>

Java代码:

//import org.apache.sanselan.formats.jpeg.exifRewrite.ExifRewriter
/**
 * Apache sanselan 方式
 * JPEG 删除图片元信息
 * @param file
 * @param dst
 * @throws IOException
 * @throws ImageReadException
 * @throws ImageWriteException
 */
public static void removeExifMetadata(final File file, final File dst) throws IOException, ImageReadException, ImageWriteException {
    OutputStream os = null;
    try {
        os = new FileOutputStream(dst);
        os = new BufferedOutputStream(os);
        new ExifRewriter().removeExifMetadata(file, os);
    } finally {
        IOUtils.closeQuietly(os);
    }
}
//TODO 抛出异常 ,仅支持 JPEG。。。
//Exception in thread "main" org.apache.sanselan.ImageReadException: Not a Valid JPEG File: doesn't begin with 0xffd8

仅支持JEPG格式, 再通过Google大佬提供的工具类试试

<dependency>
    <groupId>com.google.appenginegroupId>
    <artifactId>mediautilartifactId>
    <version>0.2version>
dependency>

Java代码:

private static void changeMetaInfo(File source) throws Exception{
    InputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(source));
    LLJTran lljTran = new LLJTran(bufferedInputStream);
    lljTran.read(LLJTran.READ_INFO, true);
    //lljTran.read(true);
    Exif exif = (Exif)lljTran.getImageInfo();
    Entry entry = new Entry(Exif.ASCII);
    entry.setValue(0,'N');
    exif.setTagValue(Exif.GPSINFO, -1, entry, true);
    //设置经度
    entry = new Entry(Exif.RATIONAL);
    entry.setValue(0, new Rational(31,1));
    entry.setValue(1, new Rational(21,1));
    entry.setValue(2, new Rational(111,1));
    exif.setTagValue(Exif.GPSINFO, -1, entry, true);
    //刷新
    lljTran.refreshAppx();
    //改写后的⽂件,⽂件必须存在
    OutputStream out = new BufferedOutputStream(new FileOutputStream("/Users/xxxx/Desktop/123.jpg"));
    lljTran.xferInfo(null, out, LLJTran.REPLACE, LLJTran.REPLACE);
}
//TODO 异常:Exception in thread "main" mediautil.image.jpeg.LLJTranException: Not a Jpeg File
//  at mediautil.image.jpeg.LLJTran.read(LLJTran.java:2080)

还是。。。仅支持Jpeg文件,HEIC无法处理

方案五:格式转换

因为通过读取客户提供的原图meta信息发现,原图是通过iPhone拍摄的照片,默认格式为HEIF,客户手动将.heif后缀的图片改为JPG(本质上还是HEIF格式)而zimg组件下载时无法兼容此格式。

那思路就是 判断图片格式,如果是这种HEIF图片格式,通过程序给__转换为JPG__进行存储,这样客户端展示时就OK了,基本流程如下:

upload
Yes
No
源图片
is HEIF?
convert to JPG
zimg服务器

如何利用Java将HEIF图片格式转换成JPG??? 这个就是我们要研究的课题了。

HEIF是High Efficiency Image File Coding的缩写,意为高效率的图像文件格式,文件名通常为.heif或者.heic后缀。

从iOS11和Android 9开始,苹果和谷歌都加入了HEIF文件格式的支持,而iOS甚至将HEIF作为默认的照片存储格式。

和JPEG不太一样的是,HEIF是一种封装格式,画质相同时HEIF容量只有不到JPG的一半,它不仅可以存储静态图像和EXIF信息元数据等,还可以存储动画、图像序列甚至视频、音频等等,存储的数据种类要丰富得多,这点有点类似于视频网站常见的mkv封装,而HEIF的静态图像格式特指的是以HEVC编码器进行压缩的图像数据和文件[HEVC Image File Format]。

ImageMagick

基本介绍

ImageMagick 是使用Apache 2.0许可证一款免费开源数字图像处理软件,以二进制发行版或者以源代码的形式发布,您可以在开放应用程序和专有应用程序中使用、复制、修改和发布。

使用ImageMagick ,您可以调整图像大小、翻转、镜像、旋转、扭曲、剪切和变换图像,调整图像颜色,应用各种特殊效果,或绘制文本、线条、多边形、椭圆和Bézier曲线。ImageMagick利用多个计算线程来提高性能,可以读取、处理或写入兆像素、千兆像素或万亿像素大小的图像。它可以读取和写入超过200种格式的图像,包括常见的PNG, JPEG, GIF, WebP, HEIC, SVG, PDF, DPX, EXR和TIFF等格式。ImageMagick可以在Linux、Windows、Mac Os X、iOS、Android Os等平台上运行。

当前最新版本是ImageMagick 7.1.0-44。官方权威代码仓库: https://github.com/ImageMagick

ImageMagick的功能通常是通过命令行使用的,或者您也可以使用用您喜欢的语言编写的程序的特性。从这些接口中选择: G2F (Ada), MagickCore ©, MagickWand ©, ChMagick (Ch), Magick ©, JMagick (Java), WASM-ImageMagick (Javascript/Typescript), JuliaIO (Julia), KMagick (Kotlin), L-Magick (Lisp), Lua (LuaJIT), NMagick (Neko/haXe), Magick。.NET (.NET)、Pascal magick (Pascal)、PerlMagick (Perl)、IMagick (PHP)、PythonMagick (Python)、magick ®、RMagick (Ruby)、TclMagick (Tcl/TK)、WASM。通过语言界面,使用ImageMagic。

支持的编程语言

语言 IimageMagick接口实现 备注
C 使用线程安全的 MagickWand c语言库来转换, 构建, 和编辑图像. There is also the low-level thread-safe MagickCore library for wizard-level developers. The MagickCache API is a development library to utilize an efficient image cache.
Ch ChMagick is a Ch binding to the MagickCore and MagickWand API. Ch is an embeddable C/C++ interpreter for cross-platform scripting.
C++ Magick++ provides a thread-safe object-oriented C++ interface to ImageMagick. See A Gentle Introduction to Magick++ for an introductory tutorial to Magick++. We include the source if you want to correct, enhance, or expand the tutorial.
GO GoImagick is a set of Go bindings to ImageMagick’s MagickWand and MagickCore C APIs.
Java JMagick 是ImageMagick提供一个面向对象的Java接口. Im4java 是ImageMagick命令行的纯java接口。
JavaScript/TypeScript WASM-ImageMagick Webassembly compiliation of ImageMagick that allows serverless clientside bindings for Typescript and Javascript. Works in Progressive Web Apps.
Lua Lua bindings to ImageMagick for LuaJIT using FFI.
Lua bindings to ImageMagick for Lua using pure-C.
.NET Use Magick.NET to convert, compose, and edit images from Windows .NET.
ImageMagickApp is a .NET application written in C# that utilizes the ImageMagick command line to allow conversion of multiple image formats to different formats.
PHP IMagick is a native PHP extension to create and modify images using the ImageMagick API. Documentation for the extension is available here, and a site showing examples is available at PHPImagick.com.
phMagick is a wrapper class for ImageMagick, wrapping the most common web image manipulation actions in easy to use functions, but allowing full access to ImageMagick’s power by issuing system calls to it’s command-line programs.
Python Wand is a ctypes-based ImagedMagick binding library for Python.
PythonMagick is an object-oriented Python interface to ImageMagick.
PythonMagickWand is an object-oriented Python interface to MagickWand based on ctypes.
Scilab Image Processing toolbox utilizes ImageMagick to do imaging tasks such as filtering, blurring, edge detection, thresholding, histogram manipulation, segmentation, mathematical morphology, color image processing, etc…
R The magick package wraps the Magick++ STL to provide vectorized image processing in R. Get started with using the package vignette.
Ruby RMagick is an interface between the Ruby programming language and the MagickCore image processing libraries. Get started with RMagick by perusing the documentation.
MagickWand for Ruby is an interface between the Ruby programming language and the MagickWand image processing libraries. Get started with MagickWand for PHP by perusing the documentation.
MiniMagick is a Ruby wrapper for ImageMagick command line. MiniMagick gives you convenient access to all the command line options ImageMagick supports.
QuickMagick is a gem for easily accessing ImageMagick command line tools from Ruby programs.
Rust MagickRust is a MagickWand bindings for the Rust language.
XML RPC RemoteMagick is an XML-RPC web service that creates image thumbnails.

(PS: 仅列举了常用编程语言,要查询支持全部语言请参阅官网:https://imagemagick.org/script/develop.php)

镜像

如果想获取ImageMagick源代码和二进制发行版可以从下面列出的世界各地的各种FTP和Web镜像中获得。也可以从Github获取 ImageMagick的稳定版和开发版。

The latest release of ImageMagick is version 7.1.0-44.

镜像仓库

区域 镜像仓库
德国 https://mirror.dogado.de/imagemagick/
http://mirror.checkdomain.de/imagemagick/
ftp://mirror.checkdomain.de/imagemagick/
波兰 https://ftp.icm.edu.pl/pub/unix/graphics/ImageMagick/
rsync://ftp.icm.edu.pl/pub/unix/graphics/ImageMagick/
瑞典 https://ftp.acc.umu.se/mirror/imagemagick.org/ftp/
rsync://ftp.acc.umu.se/mirror/imagemagick.org/ftp/
俄罗斯 https://mirror.surf/imagemagick/
美国 https://imagemagick.org/archive
https://github.com/ImageMagick (Git)

ftp://sunsite.icm.edu.pl/packages/ImageMagick/

ftp://sunsite.icm.edu.pl/packages/ImageMagick/binaries/

二进制方式

http://www.macports.org/ports.php?by=name&substr=imagemagick (Mac OS X)

http://hpux.connect.org.uk/hppd/hpux/X11/Viewers/ (HP-UX 10.20 and 11.00)

Rsync 镜像

rsync://mirror.surf/imagemagick//

rsync://rsync.is.co.za/IS-Mirror/mirror.imagemagick.org/

rsync://mirror.imagemagick.org/magick_html/ (Web site mirror)

rsync://mirror.imagemagick.org/magick_ftp/ (FTP mirror)

安装

MacOS安装

我们使用homebrew方式来进行安装:

brew install imagemagick

PS: 官网推荐安装 brew install ghostscript 经过测试(MacOs下), 这个可以不执行,在安装imagemagick的时候会自动安装依赖库,依赖如下:

Installing dependencies for imagemagick: libpng, freetype, fontconfig, jbig2dec, jpeg, libidn, libtiff, little-cms2, openjpeg, ghostscript, giflib, imath, openexr, webp, jpeg-xl, libvmaf, aom, libde265, libffi, pcre, glib, docbook, docbook-xsl, gnu-getopt, xmlto, shared-mime-info, x265, libheif, liblqr, libomp, m4 and libtool

验证是否安装成功

➜  ~ convert -version
Version: ImageMagick 7.1.0-16 Q16-HDRI x86_64 2021-11-21 https://imagemagick.org
Copyright: (C) 1999-2021 ImageMagick Studio LLC
License: https://imagemagick.org/script/license.php
Features: Cipher DPC HDRI Modules OpenMP(5.0)
Delegates (built-in): bzlib fontconfig freetype gslib heic jng jp2 jpeg lcms lqr ltdl lzma openexr png ps tiff webp xml zlib
Compiler: gcc (4.2)

使用命令转换图片格式

# magick <原图> <目标图片名称及格式>
magick IMG_20220727_154323.HEIC  jqk.jpg

Linux安装

安装支持HEIC的依赖包

yum localinstall -y https://download1.rpmfusion.org/free/el/rpmfusion-free-release-7.noarch.rpm
yum install tcl-devel.x86_64 libpng-devel.x86_64 libjpeg-devel.x86_64 ghostscript-devel.x86_64 bzip2-devel.x86_64 freetype-devel.x86_64 libtiff-devel.x86_64 libde265  libwebp-devel libde265  libheif*

安装imageMagick

必须是7.1.0-16版本,–with-heic=yes生效,之前一直用最新版7.1.0-44,无论怎么设置–with-heic=yes,结果还是–with-heic=no

wget https://hub.fastgit.xyz/ImageMagick/ImageMagick/archive/refs/tags/7.1.0-16.tar.gz
tar zxvf ImageMagick-7.1.0-16.tar.gz
cd ImageMagick-7.1.0-16
./configure --with-heic=yes
make && make install

./configure 后一定要检查,是否支持HEIC格式!!!
How to covert HEIF to JPG with Java_第3张图片

Windows安装方式

直接运行exe文件进行安装。

ImageMagick支持Windows版本
Windows 11/10 (x86 , x64 & arm64)
Windows 8/7 (x86 & x64)
Windows Vista with Service Pack 2 (x86 & x64)
Windows Server 2019/2016/2012 (x86 & x64)
Windows Server 2008 with Service Pack 2 (x86 & x64)
Windows Server 2008 R2 (x64)

建议windows至少有512 MB的RAM,但是RAM越多越好。尽管ImageMagick在单核计算机上运行良好,但它可以在多核系统上自动并行运行,大大减少了运行时间。可用发行版列表:

Version Description
ImageMagick-7.1.0-44-Q16-x64-dll.exe Win64 dynamic at 16 bits-per-pixel component
ImageMagick-7.1.0-44-Q16-x64-static.exe Win64 static at 16 bits-per-pixel component
ImageMagick-7.1.0-44-Q8-x64-dll.exe Win64 dynamic at 8 bits-per-pixel component
ImageMagick-7.1.0-44-Q8-x64-static.exe Win64 static at 8 bits-per-pixel component
ImageMagick-7.1.0-44-Q16-HDRI-x64-static.exe Win64 static at 16 bits-per-pixel component with high dynamic-range imaging enabled
ImageMagick-7.1.0-44-Q8-arm64-dll.exe ARM64 dynamic at 8 bits-per-pixel component
ImageMagick-7.1.0-44-Q8-arm64-static.exe ARM64 static at 8 bits-per-pixel component
ImageMagick-7.1.0-44-Q16-arm64-dll.exe ARM64 dynamic at 16 bits-per-pixel component
ImageMagick-7.1.0-44-Q16-arm64-static.exe ARM64 dynamic at 16 bits-per-pixel component
ImageMagick-7.1.0-44-Q16-HDRI-arm64-dll.exe ARM64 dynamic at 16 bits-per-pixel component with high dynamic-range imaging enabled
ImageMagick-7.1.0-44-Q16-HDRI-arm64-static.exe ARM64 static at 16 bits-per-pixel component with high dynamic-range imaging enabled
ImageMagick-7.1.0-44-Q16-x86-dll.exe Win32 dynamic at 16 bits-per-pixel component
ImageMagick-7.1.0-44-Q16-x86-static.exe Win32 static at 16 bits-per-pixel component
ImageMagick-7.1.0-44-Q8-x86-dll.exe Win32 dynamic at 8 bits-per-pixel component
ImageMagick-7.1.0-44-Q8-x86-static.exe Win32 static at 8 bits-per-pixel component
ImageMagick-7.1.0-44-Q16-HDRI-x86-dll.exe Win32 dynamic at 16 bits-per-pixel component with high dynamic-range imaging enabled
ImageMagick-7.1.0-44-Q16-HDRI-x86-static.exe Win32 static at 16 bits-per-pixel component with high dynamic-range imaging enabled
ImageMagick-7.1.0-portable-Q16-x64.zip Portable Win64 static at 16 bits-per-pixel component. Just copy to your host and run (no installer, no Windows registry entries).
ImageMagick-7.1.0-portable-Q16-arm64.zip Portable ARM64 static at 16 bits-per-pixel component. Just copy to your host and run (no installer, no Windows registry entries).
ImageMagick-7.1.0-portable-Q16-x86.zip Portable Win32 static at 16 bits-per-pixel component. Just copy to your host and run (no installer, no Windows registry entries).
ImageMagick-7.1.0-portable-Q8-x64.zip Portable Win64 static at 8 bits-per-pixel component. Just copy to your host and run (no installer, no Windows registry entries).
ImageMagick-7.1.0-portable-Q8-arm64.zip Portable ARM64 static at 8 bits-per-pixel component. Just copy to your host and run (no installer, no Windows registry entries).
ImageMagick-7.1.0-portable-Q8-x86.zip Portable Win32 static at 8 bits-per-pixel component. Just copy to your host and run (no installer, no Windows registry entries).
ImageMagick-7.1.0-portable-Q16-HDRI-x64.zip Portable Win64 static at 16 bits-per-pixel component with high dynamic-range imaging enabled. Just copy to your host and run (no installer, no Windows registry entries).
ImageMagick-7.1.0-portable-Q16-HDRI-arm64.zip Portable ARM64 static at 16 bits-per-pixel component with high dynamic-range imaging enabled. Just copy to your host and run (no installer, no Windows registry entries).
ImageMagick-7.1.0-portable-Q16-HDRI-x86.zip Portable Win32 static at 16 bits-per-pixel component with high dynamic-range imaging enabled. Just copy to your host and run (no installer, no Windows registry entries).

如果报缺少 vcomp120.dll 文件,那你需要安装 Visual C++ Redistributable Package.

注意,在Windows下,ImageMagick命令行使用双引号(")而不是单引号('):

magick "e:/myimages/image.png" "e:/myimages/image.jpg"

对VBScript脚本使用两个双引号:

Set objShell = wscript.createobject("wscript.shell")
objShell.Exec("magick ""e:/myimages/image.png"" ""e:/myimages/image.jpg""")

GraphicsMagick

官网:http://www.graphicsmagick.org/

GraphicsMagick是从ImageMagick5.x的版本迁出的,与ImageMagick相比,GraphicsMagick有很多优点,最突出的是它卓越的性能。由于fork ImageMagick提高了命令行语法的表达能力,因此,ImageMagick命令行不一定与GraphicsMagick兼容。但对于大多数单操作转换来说,它仍然是支持的。在im4java中,如果你想使用GraphicsMagick,你有三个选项:

  • 显式地使用GraphicsMagick,在创建对象时传递命令:GraphicsMagickCmd cmd = new GraphicsMagickCmd("convert");
  • 使用GraphicsMagick显式,使用包装器类:ConvertCmd cmd = new ConvertCmd(true);
  • 在运行时决定: 设置im4java的system-properties, 将属性设置为true,(im4java.useGM=true)将在运行时选择GraphicsMagick。

Java客户端

JMagick为ImageMagick提供了一个面向对象的Java接口。 http://www.jmagick.org/ (PS:目前官网打不开2022-07-28 09:59:03)

Im4java是ImageMagick命令行的纯java接口。http://im4java.sourceforge.net/

im4java

im4java 是ImageMagick的第二个Java接口实现,它不是 JMagick的替代品,而是作为一个补充。JMagick是ImageMagick C-API之上的一个轻量级的JNI层,相反,im4java只生成ImageMagick命令的命令行,并将生成的行传递给选中的IM命令(使用java.lang.ProcessBuilder.start()方法)

1.导入im4java的maven仓库

<dependency>
    <groupId>org.im4javagroupId>
    <artifactId>im4javaartifactId>
    <version>1.4.0version>
dependency>

历史版本: https://sourceforge.net/projects/im4java/files/

2.简单的Java案例

2.1图片格式转换

import org.im4java.core.ConvertCmd;
import org.im4java.core.IMOperation;

/**
 * 官方文档:http://im4java.sourceforge.net/docs/dev-guide.html
 */
public class Im4java {

    public static void main(String[] args) throws Exception{
        ConvertCmd cmd = new ConvertCmd();
        //windows下才需要设置imageMagick安装地址
        //cmd.setSearchPath("/usr/local/lib/ImageMagick");
        //创建图片操作对象
        IMOperation op = new IMOperation();
        //指定原图片全路径, 文件需要在同一文件夹下
        op.addImage("/Users/xxxx/Desktop/rB4CFmLffe2AdwnoADDqDe3zFYw144.heic");
        //指定目标图片全路径, 文件需要在同一文件夹下
        op.addImage("/Users/xxxx/Desktop/xxxx123123.jpg");
        cmd.run(op);
    }
}

图片转换成功,经过测试,从HEIC转换后的JPG图片可以被 zimg组件兼容!

总结

最终验证成功的案例各系统版本:

图片转换服务器:MacOs Monterey12.4或CentOS 7.9 ImageMagick 7.1.0-16 Q16-HDRI x86_64

图片服务器:CentOS 7.9 ImageMagick 6.9.10-68

整体流程方案

JPG
源HEIC
转换器
JPG
图片服务器
下载

具体描述:

1.通过im4java提供Java版本的转换器, 通过drewnoakes判断图片真实类型。

注意转换器需要:安装ImageMagick 7.1.0-16 Q16-HDRI x86_64 (编译安装, 指定–with-heic=yes)

2.如果是heic格式,先进行转换,然后再存储到zimg服务器

3.直接下载jpg格式图片。

你可能感兴趣的:(Java,Linux,java,服务器,前端)