最新消息:

Java环境下Imagemagick中文图片处理问题非完美解决方案

技术 admin 3613浏览 1评论

在处理服务器与客户端交互时候,基于性能考虑(参考手机客户端网络加速技术方案实现思考),对软件介绍中的图片采用缩略图进行展现,这样涉及了实际图片缩略图的动态处理问题,采用Java的Runtime.getRuntime().exec直接来调用Imagemagick的命令来完成缩略图的动态生成。原本是一个很简单的问题,只不过由于在图片的目录路径及图片名称中存在中文,因此在处理时候又出现了FileNotFoundException,记录一下非完美解决方案。

1、系统环境:

操作系统:Red Hat Enterprise Linux AS release 4 (Nahant Update 4)

操作系统用户环境变量(参考Linux环境下资源下载中文目录及中文文件名称问题):

export LC_ALL=zh_CN.GB18030

export LANG= zh_CN.GB18030

JVM、Tomcat、数据库等相关的字符集(参考Struts2中Datetimepicker控件的中文问题):UTF-8

2、需求场景

手机客户端对于图片的请求分为两大类:图片的下载请求、图片的预览请求。

图片的下载请求:客户端请求下载实际的图片内容,此时候服务器端不需要对图片进行任何加工处理,只需要正确返回实际的图片内容。

图片的预览请求:客户端并不需要下载完整的图片内容,只需要在客户端能够预览图片,因此在分辨率及图片质量上可以稍稍降低,以节省带宽资源。在手机客户端发起图片预览请求时候,服务器端响应的所有的图片都采用缩略图(降低图片大小)及低分辨率(降低图片质量)的方式返回。

备注:通过手机浏览器或手机客户端下载1M左右以上的资源时候,如果采用Struts2的Stream Result,存在服务器端无响应的现象,具体原因没有查明,有空看看Struts2代码再说,采用传统的response写流的方式实现没有此问题。

3、实现机制

采用Runtime.getRuntime().exec(“/usr/bin/convert  -sample 75%x75% /test.jpg /test_thumb.jpg”)来动态生成缩略图。

备注:

a、考虑到jmagick并不能完整支持Imagemagick所有命令集且使用上并不是很方便,因此没有采用jmagick,直接使用Runtime.getRuntime().exec(“/usr/bin/convert  -sample 75%x75% /test.jpg /test_thumb.jpg”)的方案,使用方法具体可以参考在线主题制作技术实现方案

b、这里使用的imagemagick的命令只供测试使用,不一定是最佳的命令,具体查一下imagemagick的手册

c、由于Imagemagick生成图片相对较慢,因此并不需要每一次都生成缩略图,可以将缩略图存放在与原有图片相同的目录路径下,这样在客户端发起图片预览请求时候,服务器端先查看一下请求图片的预览图是否存在,如果存在直接返回以前生成的预览图,如果不存在则动态生成后再返回。

4、问题

在使用Runtime.getRuntime().exec调用convert来动态生成缩略图时候,由于资源在Linux服务器上存放目录及文件名称都存在中文(例如/products/material/img/101×80 /人物肖像101×8/测试.jpg),因此在调用时候会报FileNotFoundException错误。

5、方案测试

测试命令:

/usr/bin/convert -sample 75%x75%   /img/101×80/人物肖像101×8/测试.jpg  /img/101×80/人物肖像101×8/测试_thumb.jpg

试验1:

操作:操作系统编码为zh_CN.GB18030,因此在shell命令行执行convert,如果传入的字符串参数为正常的zh_CN.GBK(比zh_CN.GB18030范围小)的编码,那么不会乱码。

结果:在命令行执行,已验证。

试验2:

操作:JVM环境变量为UTF-8,Java内部本身也是采用UTF-8编码,Runtime.getRuntime().exec调用convert时候,如果将参数进行转码成zh_CN.GBK,则应该能够正常处理

private static final String CMD=”/usr/bin/convert “;
private static final String PRAM=” -sample 75%x75%  “;

inputPath=new String(inputPath.getBytes(“UTF-8″),”GBK”);
outPutPath=new String(outPutPath.getBytes(“UTF-8″),”GBK”);
Process pc=Runtime.getRuntime().exec(CMD+PRAM+inputPath+” “+outPutPath);

结果:以上代码不行,将getBytes的编码换成GBK、ISO8859-1,输出编码换成UTF-8、ISO8859-1等也一样

试验3:

操作:将JVM环境变量修改为GBK

结果:似乎也不行

试验4:

操作:在java代码中,将中文路径/img/101×80/人物肖像101×8/测试.jpg  /img/101×80/人物肖像101×8/测试_thumb.jpg写入普通文本文件/home/client.aouu.com/img/img2.txt,然后在命令行调用convert来生成

Java代码(示例,不规范):

FileWriter os = new FileWriter(“/home/client.aouu.com/img/img1.txt”);
String imgPath=inputPath+” “+outPutPath;
os.write(imgPath);
os.close();
Process pc=Runtime.getRuntime().exec(“/home/client.aouu.com/img/img.sh”);
pc.waitFor();

img.sh:

#!/bin/sh

cat /home/client.aouu.com/img/img1.txt |xargs /usr/bin/convert  -sample 75%x75%

结果:不行,报告-bash: ./img.sh: cannot execute binary file

试验5:

操作:与试验4操作相同

img.sh:

#!/bin/sh

iconv -f UTF-8 -t GBK -c /home/client.aouu.com/img/img1.txt -o /home/client.aouu.com/img/img2.txt
cat /home/client.aouu.com/img/img2.txt |xargs /usr/bin/convert  -sample 75%x75%

结果:OK

结果分析:

尽管在java代码中将中文路径、中文文件名称的编码转码成GBK,但java内部本身是UTF-8编码,因此JVM通过Runtime.getRuntime().exec传给/usr/bin/convert的参数实际上仍然为UTF-8编码(?是什么编码,尚待验证),并不是GBK编码,因此convert获取输入参数后无法在操作系统找到对应的文件。

通过FileWriter写入文件的字符集也为UTF-8,通过iconv强制将UTF-8编码转化为GBK,这样convert能够正常处理。

具体的原因及机理尚待进一步考察验证,现在尚无好的办法,姑且凑合使用此种方案再说。

转载请注明:出家如初,成佛有余 » Java环境下Imagemagick中文图片处理问题非完美解决方案

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

网友最新评论 (1)