客户端和服务端通讯的N种方式(二)

来源:互联网 发布:程序员自由职业平台 编辑:程序博客网 时间:2024/05/02 02:21
原文出自: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 plaincopy to clipboardprint?
  1. package service;  
  2.   
  3. import java.io.ByteArrayOutputStream;  
  4. import java.io.FileInputStream;  
  5. import java.io.FileOutputStream;  
  6.   
  7. public class MyImage  
  8. {  
  9.     //  上传文件  
  10.     public boolean updateImage(String image)  
  11.     {  
  12.         try  
  13.         {  
  14.             byte[] buffer = new sun.misc.BASE64Decoder().decodeBuffer(image);  
  15.             FileOutputStream fos = new FileOutputStream("d:\\my.jpg");  
  16.             fos.write(buffer);  
  17.             fos.close();  
  18.             return true;  
  19.         }  
  20.         catch (Exception e)  
  21.         {  
  22.             // TODO: handle exception  
  23.         }  
  24.         return false;  
  25.   
  26.     }  
  27.     //  下载文件  
  28.     public String downloadImage()  
  29.     {  
  30.         String base64 = "";  
  31.         try  
  32.         {  
  33.             FileInputStream fis = new FileInputStream("e:\\my.jpg");  
  34.             ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  35.   
  36.             byte[] buffer = new byte[8192];  
  37.             int count = 0;  
  38.             while ((count = fis.read(buffer)) >= 0)  
  39.             {  
  40.                 baos.write(buffer, 0, count);  
  41.             }  
  42.   
  43.             base64 = new sun.misc.BASE64Encoder().encode(baos.toByteArray());  
  44.   
  45.             fis.close();  
  46.         }  
  47.         catch (Exception e)  
  48.         {  
  49.             // TODO: handle exception  
  50.         }  
  51.         return base64;  
  52.     }  
  53. }  


在上面的代码中使用了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组件,用来显示从服务端下载的图像。增加的布局代码如下:

 

view plaincopy to clipboardprint?
  1. <LinearLayout android:orientation="horizontal"  
  2.     android:layout_width="fill_parent" android:layout_height="wrap_content">  
  3.     <Button android:id="@+id/btnUploadImage" android:layout_width="wrap_content"  
  4.         android:layout_height="wrap_content" android:text="上传图像"  
  5.         android:onClick="onClick_UploadImage" />  
  6.     <Button android:id="@+id/btnDownloadImage"  
  7.         android:layout_width="wrap_content" android:layout_height="wrap_content"  
  8.         android:text="下载图像" android:onClick="onClick_DownloadImage" />  
  9. </LinearLayout>  
  10. <ImageView android:id="@+id/imageview" android:layout_width="fill_parent"  
  11.     android:layout_height="fill_parent" />  

 

其中onClick_UploadImage和onClick_DownloadImage方法分别用来处理上传和下载按钮的单击事件。设计完的界面如图1所示。

图1  增加了上传和下载按钮的界面


下面先来看看onClick_UploadImage方法的实现,代码如下:

 

view plaincopy to clipboardprint?
  1. public void onClick_UploadImage(View view)  
  2. {  
  3.     try  
  4.     {  
  5.         FileInputStream fis = new FileInputStream("/sdcard/test.jpg");  
  6.         ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  7.   
  8.         byte[] buffer = new byte[8192];  
  9.         int count = 0;  
  10.         while ((count = fis.read(buffer)) >= 0)  
  11.         {  
  12.   
  13.             baos.write(buffer, 0, count);  
  14.         }  
  15.   
  16.         String imageBase64 = new String(Base64.encodeBase64(baos  
  17.                 .toByteArray()));  
  18.   
  19.         fis.close();  
  20.   
  21.         String serviceUrl = "http://192.168.17.82:8080/axis2/services/MyImageService?wsdl";  
  22.         String methodName = "updateImage";  
  23.         SoapObject request = new SoapObject("http://service", methodName);  
  24.         request.addProperty("image", imageBase64);  
  25.         SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(  
  26.                 SoapEnvelope.VER11);  
  27.         envelope.bodyOut = request;  
  28.         HttpTransportSE ht = new HttpTransportSE(serviceUrl);  
  29.         ht.call(null, envelope);  
  30.   
  31.         if (envelope.getResponse() != null)  
  32.         {  
  33.             org.ksoap2.serialization.SoapPrimitive soapPrimitive = (SoapPrimitive) envelope  
  34.                     .getResponse();  
  35.             boolean result = Boolean.parseBoolean(soapPrimitive.toString());  
  36.             if (result)  
  37.                 Toast.makeText(this"成功上传图像.", Toast.LENGTH_LONG).show();  
  38.             else  
  39.                 Toast.makeText(this"上传图像失败.", Toast.LENGTH_LONG).show();  
  40.         }  
  41.   
  42.     }  
  43.     catch (Exception e)  
  44.     {  
  45.         Log.d("updateImage_exception", String.valueOf(e));  
  46.     }  
  47. }  

在上面的代码中使用了一个第三方的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。因此,客户端需要获得服务端的返回结果。代码如下:

 

view plaincopy to clipboardprint?
  1. org.ksoap2.serialization.SoapPrimitive soapPrimitive = (SoapPrimitive) envelope  
  2.                         .getResponse();  

由于服务端返回的是简单类型(boolean),因此,在这里需要将返回值转换成SoapPrimitive对象,而不是SoapObject对象。服务端的updateImage方法如果上传成功,返回true,否则返回false。如果成功上传图像,会显示如图2所示的提示信息。我们会在D盘根目录看到一个my.jpg文件。

图2  成功上传图像文件后的提示信息


下面来看看如何下载图像文件,代码如下:

 

view plaincopy to clipboardprint?
  1. public void onClick_DownloadImage(View view)  
  2. {  
  3.     try  
  4.     {  
  5.   
  6.         String serviceUrl = "http://192.168.17.82:8080/axis2/services/MyImageService?wsdl";  
  7.         String methodName = "downloadImage";  
  8.         SoapObject request = new SoapObject("http://service", methodName);  
  9.   
  10.         SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(  
  11.                 SoapEnvelope.VER11);  
  12.         envelope.bodyOut = request;  
  13.         HttpTransportSE ht = new HttpTransportSE(serviceUrl);  
  14.         ht.call(null, envelope);  
  15.   
  16.         if (envelope.getResponse() != null)  
  17.         {  
  18.             SoapPrimitive soapPrimitive = (SoapPrimitive) envelope  
  19.                     .getResponse();  
  20.   
  21.             byte[] buffer = org.kobjects.base64.Base64.decode(soapPrimitive  
  22.                     .toString());  
  23.   
  24.             Bitmap bitmap = BitmapFactory.decodeByteArray(buffer, 0,  
  25.                     buffer.length);  
  26.   
  27.             ImageView imageView = (ImageView) findViewById(R.id.imageview);  
  28.             imageView.setImageBitmap(bitmap);  
  29.   
  30.   
  31.         }  
  32.   
  33.     }  
  34.     catch (Exception e)  
  35.     {  
  36.         Log.d("downloadImage_exception", String.valueOf(e));  
  37.     }  
  38. }  

在上面的代码中首先从调用了WebService的downloadImage方法,并返回了一个Base64格式的字符串。然后利用decode方法将字符串还原成byte数组。最后在ImageView中显示了这个下载的图像。如图3所示。
 

图3  下载并显示图像


总结


本文介绍了如何利用WebService上传和下载图像。利用本节的技术,不光能上传图像文件,也可以上传任意的文件。但如果文件非常大的话,并不建议采用这种方式来传递。对于大文件可以采用后面几篇文章所介绍的方式与服务端进行交互。

相关文章:客户端和服务端通讯的N种方式(一)

作者介绍


李宁,东北大学计算机专业硕士,拥有超过10年的软件开发经验。曾任国内某知名企业项目经理;目前担任eoeandroid和ophonesdn版主;中国移动开发者社区OPhone专家;51CTO客作专家;CSDN博客专家。曾领导并参与开发了多个大中型项目。目前主要从事Android及其相关产品的研发。从2005年进入写作领域以来,为《程序员》、《电脑编程技巧与维护》、《电脑报》、IT168、天极网等平面媒体和网络媒体撰写了一百多篇原创技术和评论文章。并在个人blog(http://nokiaguy.blogjava.net)上发表了大量的原创技术文章。2007年获《电脑编程技巧与维护》优秀作者。2009年获得OPhone征文大赛二等奖。个人著作:《Android/OPhone开发完全讲义》、《人人都玩开心网:Ext JS+Android+SSH整合开发Web与移动SNS》、《Java Web开发速学宝典》。

(声明:本网的新闻及文章版权均属OPhone SDN网站所有,如需转载请与我们编辑团队联系。任何媒体、网站或个人未经本网书面协议授权,不得进行任何形式的转载。已经取得本网协议授权的媒体、网站,在转载使用时请注明稿件来源。)

原创粉丝点击