在处理服务器与客户端交互时候,基于性能考虑(参考手机客户端网络加速技术方案实现思考 ), 对软件介绍中的图片采用缩略图进行展现,这样涉及了实际图片缩略图的动态处理问题,采用Java的Runtime.getRuntime().exec直 接来调用Imagemagick的命令来完成缩略图的动态生成。原本是一个很简单的问题,只不过由于在图片的目录路径及图片名称中存在中文,因此在处理时 候又出现了FileNotFoundException,记录一下非完美解决方案。
操作系统: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
手机客户端对于图片的请求分为两大类:图片的下载请求、图片的预览请求。
图片的下载请求 :客户端请求下载实际的图片内容,此时候服务器端不需要对图片进行任何加工处理,只需要正确返回实际的图片内容。
图片的预览请求 :客户端并不需要下载完整的图片内容,只需要在客户端能够预览图片,因此在分辨率及图片质量上可以稍稍降低,以节省带宽资源。在手机客户端发起图片预览请求时候,服务器端响应的所有的图片都采用缩略图(降低图片大小)及低分辨率(降低图片质量)的方式返回。
备注:通过手机浏览器或手机客户端下载1M左右以上的资源时候,如果采用Struts2的Stream Result,存在服务器端无响应的现象,具体原因没有查明,有空看看Struts2代码再说,采用传统的response写流的方式实现没有此问题。
采用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生成图片相对较慢,因此并不需要每一次都生成缩略图,可以将缩略图存放在与原有图片相同的目录路径下,这样在客户端 发起图片预览请求时候,服务器端先查看一下请求图片的预览图是否存在,如果存在直接返回以前生成的预览图,如果不存在则动态生成后再返回。
在使用Runtime.getRuntime().exec调用convert来动态生成缩略图时候,由于资源在Linux服务器上存放目录及文件 名称都存在中文(例如/products/material/img/101×80 /人物肖像101×8/测试.jpg),因此在调用时候会报FileNotFoundException错误。
测试命令:
/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能够正常处理。
具体的原因及机理尚待进一步考察验证,现在尚无好的办法,姑且凑合使用此种方案再说。