锁定老帖子 主题:手把手教你做基于web的文件扫描并上传
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2010-05-14
最后修改:2011-01-04
最近碰到的问题,客户端调用本地的扫描仪,将扫描的文件上传。 使用到的技术:applet,twain,HttpClient 当然也碰到很多问题,因为在这周之前我都不知道什么是applet 一.Applet操作本地资源 基于安全方面的原因,applet是不允许操作本地资源的。但是java提供了相应的为jar包签名的机制来提升applet的权限。相信很多人都碰到过这种对话框: 让用户来决定是否给applet提升权限,如果用户信任这个资源,applet将能操作本地资源。
1.为applet依赖的jar包签名 java提供了两个工具; keytool用于生成存放key的库 jarsigner用于为jar进行签名
首先建立一个keystore(这是在当前路径上操作): keytool -genkey -alias zengge -keystore zengge.keystore keytool -genkey表示建库 -alias zengge是为要建立的key取一个别名 -keystore zengge.keystore是建立一个名字叫zengge.keystore的key库,key就存在里面. 如下图:
有了keystore之后jarsigner就可以利用存放在keystore中的key来为jar签名
包里面的META-INFO里面只有一个文件,且内容为上图
接下来为jar包签名: jarsigner -keystore zengge.keystore HelloWorld.jar zengge 密码为建立keystore时的密码
再来看一下签过名的jar里面的情况有什么变化: 可以看到META-INFO里面现在是三个文件,.DSAG与.SF我想肯定是用来加解密用的,大家注意现在的MANIFEST.MF, 可以看出为每个类添加了一个SHA签名,用它来保证,这个jar里面的内容不会被其它人修改,用户可以相信这个jar。
2.将applet嵌入html applet是通过浏览器来运行了,可能你会问,java的东西浏览器怎么能运行呢,难道客户端也要安装java? 实际上applet是通过嵌在浏览器中的jvm在运行,但是这个jvm是从那里来的呢? 对于IE,大家可以看一下,下图中的java如果选中,就表示会调用本地的jre来运行applet。 但是绝大部分情况下,客户端是不会安装java的啊,还有firefox下面没有类似的这种选项。那么要如何来解决这种问题呢? 早期的applet都是用applet标签来嵌入html的(当然现在也可以),例如: <APPLET CODE = "HelloWorld" archive ="HelloWorld.jar" JAVA_CODEBASE = "." WIDTH = "320" HEIGHT = "240" NAME = "HelloWorld"></APPLET> 这里的CODE表示类名(类名后可以加上.class),archvie表示类所在的jar包,如果你有多个jar包,可以全加在archive里面,用,号分开(archive="a,jar,b.jar,c.jar",当然这些jar要签名的还得签名)。 如果用这种标签,在IE下,如果没选中用本地jre运行applet的话,是运行不了的,没安装插件的firefox也是不能运行的。
面对这种情况,升级版的标签出现了,java提供了一个工具名字叫HTMLconverter,通过它,能将html中的applet标签转换成标准的标签,如下 <object classid = "clsid:8AD9C840-044E-11D1-B3E9-00805F499D93" codebase = "http://java.sun.com/update/1.6.0/jinstall-6u14-windows-i586.cab#Version=6,0,0,8" WIDTH = "320" HEIGHT = "240" NAME = "HelloWorld" > <PARAM NAME = CODE VALUE = "HelloWorld" > <PARAM NAME = CODEBASE VALUE = "." > <PARAM NAME = ARCHIVE VALUE = "applet_test.jar" > <PARAM NAME = NAME VALUE = "HelloWorld" > <param name = "type" value = "application/x-java-applet;version=1.6"> <param name = "scriptable" value = "false"> //上面是针对IE //下面的embed是针对firefox <comment> <embed type = "application/x-java-applet;version=1.6" \ CODE = "HelloWorld" \ JAVA_CODEBASE = "." \ ARCHIVE = "applet_test.jar" \ NAME = "HelloWorld" \ WIDTH = "320" \ HEIGHT = "240" scriptable = false pluginspage = "http://java.sun.com/products/plugin/index.html#download"> <noembed> </noembed> </embed> </comment> </object> <!-- <APPLET CODE = "HelloWorld" JAVA_CODEBASE = "." WIDTH = "320" HEIGHT = "240" NAME = "HelloWorld"> </APPLET> --> <!--"END_CONVERTED_APPLET"--> 其中Object部分是针对IE的,embed是针对firefox的.classid与codebase都是表示相应的plugin的下载地址,如果codebase的版本高于classid将下载codebase版本的plugin。这样不管本地有没有安装java,applet都能正常运行了。
现在,在applet里面己经能调用本地的资源了,那么如何驱动扫描仪呢?
二.applet调用TWain驱动本地扫描仪 TWain是一个标准,用于获取扫描仪等设备的信息,它有很多实现(基本都是收费的),这里选择的是一个开源twain产品,mmsc twain(官网 http://www.mms-computing.co.uk/例子很丰富)。 只要本地安装有扫描仪驱动,twain就能找到并运行且获取到扫描的数据。 它里面有个Scanner类,Scanner scanner = Scanner.getDevice()能获取到相应的设备。然后为scanner添加一个监听器,ScannerListener,它里面有个方法public void update(ScannerIOMetadata.Type type, ScannerIOMetadata metadata){},第二个参数即为扫描得到的数据,而且这个方法是在扫描述的状态发生变变就会解发。具体可以看一些mmsc里面的例子。
三.applet与服务器通信 得到了扫描的数据,得把它上传到服务器。我这里用的是HttpClient(需要的jar包commons-codec-1.4.jar,commons-httpclient-3.0.jar,commons-io-1.4.jar,commons-logging-1.0.2.jar)。在使用时最好对这几个jar包都签名(我没有测试这种方式,我是将这几个jar全给解压了,最后连同我的类一起打成了一个jar,最后签名),上传代码位于update方法内即可。 因为扫描仪有多种状态,所以要进行判断,那次才是拿到了扫描数据。 public void update(ScannerIOMetadata.Type type, ScannerIOMetadata metadata){ if(type.equals(ScannerIOMetadata.ACQUIRED)){ BufferedImage image=metadata.getImage(); System.out.println("Have an image now!"); HttpClient httpClient = new HttpClient(); MultipartPostMethod mpm = new MultipartPostMethod("http://localhost:8086/ReiyenDMS/TestUploadServlet"); // MultipartPostMethod mpm = new MultipartPostMethod("http://localhost:8086/applet_study/servlet/AppletServlet"); File file = new File("c:/upload/abc"+index+".jpg"); try{ ImageIO.write(image, "jpg", file); index++; mpm.addParameter("aFile", "haha.pdf", file); httpClient.executeMethod(mpm); }catch(Exception e){ e.printStackTrace(); } }else if(type.equals(ScannerIOMetadata.NEGOTIATE)){ ScannerDevice device=metadata.getDevice(); /* try{ device.setResolution(100); // device.setRegionOfInterest(0.0,0.0,40.0,50.0); // top-left corner 40x50 mm device.setRegionOfInterest(0,0,400,500); // top-left corner 400x500 pixels device.setShowUserInterface(false); device.setShowProgressBar(false); }catch(Exception e){ e.printStackTrace(); } */ }else if(type.equals(ScannerIOMetadata.STATECHANGE)){ System.err.println(metadata.getStateStr()); }else if(type.equals(ScannerIOMetadata.EXCEPTION)){ metadata.getException().printStackTrace(); } }
完工 中间碰到最多的问题就是,applet签名及applet布署,开始老以为applet根平时的类布署是一样的,后来发现就应该把它当成一个独立的应用来对等。
中间可能有错,有问题请在家指出,谢谢
效果图(点击中间的acquire就能扫描了):
有童鞋反映提供的twain站点不能用了,特加上一个twain的源码,详见符件twian.rar 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-05-14
看来这种需求很少啊,我自己支持一下
|
|
返回顶楼 | |
发表时间:2010-05-15
客户有很大几率会提
值得收藏 |
|
返回顶楼 | |
发表时间:2010-05-15
支持下,原来还可以这样用
|
|
返回顶楼 | |
发表时间:2010-05-15
印象系统这样需求很多
我们公司有很多这样鬼东西 代码写不好维护烦 其实也可以考虑bs解决 只是浏览器兼容本地存储是个考虑的问题 |
|
返回顶楼 | |
发表时间:2010-05-15
applet 这个好吗 不装jre用不了的 好多客户反正不喜欢用介个 没别的办法做到这功能了吗
|
|
返回顶楼 | |
发表时间:2010-05-15
whaosoft 写道 applet 这个好吗 不装jre用不了的 好多客户反正不喜欢用介个 没别的办法做到这功能了吗
客户端不用装jre的,它会去下载相应的插件..... |
|
返回顶楼 | |
发表时间:2010-05-15
网络不好的话,下载都要下班天
|
|
返回顶楼 | |
发表时间:2010-05-15
最后修改:2010-05-15
tsxm 写道 网络不好的话,下载都要下班天
那你怎么不说人家禁用js的情况啊,什么技术都有它的局限性,我只是发出来让大家参考下嘛,如果有更好的解决方式当然更好啊. 况且你不是每次都要下啊,一次而己嘛 |
|
返回顶楼 | |
发表时间:2010-05-15
whaosoft 写道 applet 这个好吗 不装jre用不了的 好多客户反正不喜欢用介个 没别的办法做到这功能了吗
其实我觉得我们自己自寻烦恼,做开发也有几年了,我们总是被教导jre太大,下载安装麻烦,applet已经过时了 我们是b/s时代的典范 这几年接触客户,客户从来没有为要安装jre而提出,我们不需要这样的jre,你要客户安装啥,他们就安装什么,他们要的是系统可以运行,想要的结果可以看到 往往我们花了大力气从applet中解脱,老实说离开applet做很多东西还是不方便的,数据呈现(图表 放大 缩小。。。)用js或svg麻烦多了,用applet成熟还稳定。悲哀阿 flash:每一次重装系统,需要安装最新的flash那个升级速度没有个1、2分钟搞不定阿,其实现在jre的下载安装也只需要1、2分钟;在linux下每次flash运行那个cpu和浏览器假死郁闷阿 只想说明:没必要太在意中间过程,只要简单的和达到要求的即可。对于开发和维护越简单的实现越好 |
|
返回顶楼 | |