原文出自:http://www.ophonesdn.com/article/show/304
在上一篇文章中我们介绍了WebService的简单调用方式。通过这种调用方式可以传递一般的数据。例如,服务端可以返回一个对象,并在客户端取出对象中的属性值。也可以向服务端传递一个简单的值(String、int、boolean等)作为WebService方法的参数值。但如果某些读者对WebService提出了更高的要求,那么本文以及下一篇文章中的内容正好可满足这类读者的需求。
在本文先来提出对WebService的第一个苛刻要求。有很多情况下,需要在数据库或以文件形式将图像保存在服务端。虽然上传图像可以通过FTP方式很容易解决。但这就需要将数据分成两部分或更多部分上传,在处理上可能会消耗更多的服务端资源,程序也不易维护。OK,那么我们期望的最佳结果是在使用WebService上传数据的同时就把图像传到服务端。好,现在我们提出了关于WebService的第一个复杂问题,利用WebService上传和下载图像。
上传和下载图像的原理
不管是什么格式的图像(当然,其他类型的文件也是一样)都是由二进制数据组成的,因此,我们很自然地想到通过byte数组进行数据传递。但由于服务端的WebService可能由非Java语言来编写,对于byte数组的处理可能和Java不同。因此,我们可以通过编码的方式将byte数组转换成字符串。一般会选择Base64编码。
也就是说,在上传图像时会从本地将图像文件以字节的形式读到内存中,然后再通过相应的API将byte数组转换成Base64字符串。当下载图像时,过程正好相反。我们下载的是被转换成base64编码的byte数组。在客户端需要对其进行解码,还原成byte数组,然后再做进一步处理。在接下来的内容中读者将会看到具体的实现过程。
可以上传和下载的服务端WebService
为了完成本文的例子,首先需要编写一个可以上传和下载的图像文件的WebService。为了方便起见,在本程序中使用了固定的图像位置。将原图像放到了E盘根目录(my.jpg),服务端从该位置下载文件。上传文件后将文件保存在D盘根目录的my.jpg文件中。
下面看一下上传和下载文件的完整的WebService代码。
view plain
copy to clipboard
print
?
- package?service;??
- ??
- import?java.io.ByteArrayOutputStream;??
- import?java.io.FileInputStream;??
- import?java.io.FileOutputStream;??
- public?class?MyImage??
- {??
- ??????
- ????boolean?updateImage(String?image)??
- ????{??
- ????????try??
- ????????{??
- ????????????byte[]?buffer?=?new?sun.misc.BASE64Decoder().decodeBuffer(image);??
- ????????????FileOutputStream?fos?=?new?FileOutputStream("d:my.jpg");??
- ????????????fos.write(buffer);??
- ????????????fos.close();??
- return?true;??
- ????????}??
- catch?(Exception?e)??
- ??????????????
- false;??
- ??
- ????}??
- //??下载文件??
- public?String?downloadImage()??
- ????????String?base64?=?"";??
- ????????try??
- ????????{??
- ????????????FileInputStream?fis?=?new?FileInputStream("e:my.jpg");??
- ????????????ByteArrayOutputStream?baos?=?new?ByteArrayOutputStream();??
- new?byte[8192];??
- ????????????int?count?=?0;??
- while?((count?=?fis.read(buffer))?>=?0)??
- ????????????{??
- ????????????????baos.write(buffer,?0,?count);??
- ????????????}??
- ????????????base64?=?new?sun.misc.BASE64Encoder().encode(baos.toByteArray());??
- ????????????fis.close();??
- ????????}??
- catch?(Exception?e)??
- ??????????????
- return?base64;??
- ????}??
- }??
在上面的代码中使用了sun.misc.BASE64Decoder类中的相应方法对表示图像的字节数组进行Base64编码和解码。
updateImage方法复制上传图像,通过image参数传递一个被编码成Base64字符串的字节数组。该字节数组就是客户端上传的图像数据。然后通过decodeBuffer方法将其还原成byte数组。并保存在D:my.jpg文件中。
downloadImage是updateImage方法的逆过程。从E:my.jpg文件中读取数据,并将这些数据保存在byte数组中,最后使用encode方法将byte数组转换成Base64格式的字符串,并返回这个字符串。
现在将这个WebService部署到Tomcat中,并启动Tomcat,下面将编写调用这个WebService的OPhone客户端。
在OPhone 2.0中上传和下载图像
本文的例子接着上一篇文章的例子来实现。首先需要在main.xml布局文件中增加两个按钮和一个ImageView组件,用来显示从服务端下载的图像。增加的布局代码如下:
?
<LinearLayout?android:orientation="horizontal"??
????android:layout_width="fill_parent"?android:layout_height="wrap_content">??
????<Button?android:id="@+id/btnUploadImage"?android:layout_width="wrap_content"??
????????android:layout_height="wrap_content"?android:text="上传图像"??
????????android:onClick="onClick_UploadImage"?/>??
????<Button?android:id="@+id/btnDownloadImage"??
????????android:layout_width="wrap_content"?android:layout_height="wrap_content"??
????????android:text="下载图像"?android:onClick="onClick_DownloadImage"?/>??
</LinearLayout>??
<ImageView?android:id="@+id/imageview"?android:layout_width="fill_parent"??
????android:layout_height="fill_parent"?/>??
?
其中onClick_UploadImage和onClick_DownloadImage方法分别用来处理上传和下载按钮的单击事件。设计完的界面如图1所示。

图1? 增加了上传和下载按钮的界面
下面先来看看onClick_UploadImage方法的实现,代码如下:
void?onClick_UploadImage(View?view)??
???? ????{??
????????FileInputStream?fis?=?new?FileInputStream("/sdcard/test.jpg");??
????????ByteArrayOutputStream?baos?=? ????????????baos.write(buffer,?count);??
????????String?imageBase64?=?new?String(Base64.encodeBase64(baos??
????????????????.toByteArray()));??
????????fis.close();??
????????String?serviceUrl?=?"http://192.168.17.82:8080/axis2/services/MyImageService?wsdl";??
????????String?methodName?=?"updateImage";??
????????SoapObject?request?=?new?SoapObject("http://service",?methodName);??
????????request.addProperty("image",?imageBase64);??
????????SoapSerializationEnvelope?envelope?=?new?SoapSerializationEnvelope(??
????????????????SoapEnvelope.VER11);??
????????envelope.bodyOut?=?request;??
????????HttpTransportSE?ht?=?new?HttpTransportSE(serviceUrl);??
????????ht.call(null,?envelope);??
if?(envelope.getResponse()?!=?null)??
????????????org.ksoap2.serialization.SoapPrimitive?soapPrimitive?=?(SoapPrimitive)?envelope??
????????????????????.getResponse();??
boolean?result?=?Boolean.parseBoolean(soapPrimitive.toString());??
if?(result)??
????????????????Toast.makeText(this,?"成功上传图像.",?Toast.LENGTH_LONG).show();??
else??
"上传图像失败.",108); height:auto; color:inherit; line-height:22px"> ????????Log.d("updateImage_exception",?String.valueOf(e));??
在上面的代码中使用了一个第三方的jar包:commons-codec-1.4.jar,该包可以从www.apacle.org下免费下载。本例主要通过该包中的相应API对字节数组进行Base64编码和解码。
在本例中我们上传了SD卡根目录的一个叫test.jpg的文件。那么onClick_UploadImage方法首先要做的就是读取这个图像文件,并将数据保存在byte数组中。然后对byte数组进行Base64编码。
在接下来的实现过程就需要调用WebService上。WebService的WSDL地址如下:
http://192.168.17.82:8080/axis2/services/MyImageService?wsdl
方法名是updateImage。在这里只需要传递一个image参数,参数值就是经过编码的byte数组(一个字符串)。
服务端的updateImage方法如果上传成功,返回true,否则返回false。因此,客户端需要获得服务端的返回结果。代码如下:
org.ksoap2.serialization.SoapPrimitive?soapPrimitive?=?(SoapPrimitive)?envelope??
????????????????????????.getResponse();??
由于服务端返回的是简单类型(boolean),因此,在这里需要将返回值转换成SoapPrimitive对象,而不是SoapObject对象。服务端的updateImage方法如果上传成功,返回true,否则返回false。如果成功上传图像,会显示如图2所示的提示信息。我们会在D盘根目录看到一个my.jpg文件。