• 利用WCF与Android实现图片上传并传参


    最近做一个项目后端使用WCF接收Android手机拍照并带其它参数保存到服务器里;刚好把最近学习的WCF利用上,本以为是个比较简单的功能应该很好实现,没想到其中碰到不少问题,在网上搜索很久一直没有想到的解决方案,最后实现对数据流的分段写入然后后端再来解析流实现的此功能;后端运用WCF中的REST来接收数据;REST还是比较简单的知识,若是不懂可以简单网上了解一下;下面我们先了解一些本次运用到的理论知识:

    一:理论知识

    由于低层协议特性限制,WCF的流模式只支持如下四种:1:BasicHttpBinding 2:NetTcpBinding 3:NetNamedPipeBinding 4:WebHttpBinding

    1.设置TransferMode。它支持四种模式(Buffered、Streamed、StreamedRequest、StreamedResponse),请根据具体情况设置成三种Stream模式之一。

    2.修改MaxReceivedMessageSize。该值默认大小为64k,因此,当传输数据大于64k时,则抛出CommunicationException异常。  

    3.修改receiveTimeout 和sendTimeout。大数据传送时间较长,需要修改这两个值,以免传输超时。

    二:解决问题

    WCF如果使用Stream做为参数时只能唯一一个,不能有其它另外的参数,这个也是本次碰到要重点解决的一个问题;可是我们Android手机除的图片还要有其它的参数,最后决定采用手机端把参数跟图片都一起写入Stream里面,后端WCF再来解析这个参数的流;

    下面就是定义好Stream的格式,传过来的Stream分成三部分: 参数信息长度  参数信息   图片

    1 参数信息长度(1字节):用于存放参数信息的长度(以字节为单位);

    2 参数信息: 除图片以外的参数,以JSON的形式存放如{"type":"jpg","EmployeeID":"12","TaskID":"13"}

    3 图片:图片的字节

    三:WCF编码内容

    1:我们首先定义一个WCF契约,由于我们运用REST(在命名空间ServiceModel.Web下面)契约IAndroidInfo内容如下,采用POST方式进行接收:

    using System.ServiceModel;
    using System.Runtime.Serialization;
    using System.ServiceModel.Web;
    using System.IO;
    
    namespace Coreius.CEIMS.AndroidInterface
    {
        [ServiceContract]
        public interface IAndroidInfo
        {
             [WebInvoke(UriTemplate = "GpsUpFile", Method = "POST", BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json, 
    ResponseFormat = WebMessageFormat.Json)]
            bool GpsUpFile(Stream ImageContext);
        }
    }

    2:根据契约我们定义服务的内容,接收一个流的参数内容,首先把这个Stream转化成字节,然后根据我们先前约定好的内容获得第一个字节的值,再根据此值定义我们另外三个参数的字节长度,再通过JSON转换格式把它里面的三个参数值取出来,最后其它字节是存放一张手机拍的照片,把它存放在于们服务器D盘文件夹下

    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    using System.ServiceModel.Web;
    using System.IO;
    using Newtonsoft.Json;
    
    namespace Coreius.CEIMS.AndroidService
    {
        public class AndroidInfoService:IAndroidInfo
        {
    
          public bool GpsUpFile(Stream ImageContext)
            {
                byte[] m_Bytes = ReadToEnd(ImageContext);
                int len = (int)m_Bytes[0];
    
                byte[] data = m_Bytes.Skip(1).Take(len).ToArray();
                string Jsonstr = System.Text.Encoding.Default.GetString(data);
    
                JsonModel item = JsonConvert.DeserializeObject<JsonModel>(Jsonstr);
                string ImageType=item.type;
                string EmployeeID=item.EmployeeID;
                string TaskID=item.TaskID;
    
                byte[] Imagedata = m_Bytes.Skip(1 + len).ToArray();
    
                string DiskName = "d:";
                string FileAddress = "\UpLoad\";
                string LocationAddress = DiskName + FileAddress;
                if (!DirFileHelper.IsExistDirectory(LocationAddress))
                {
                    DirFileHelper.CreateDirectory(LocationAddress);
                }
    
                string ImageName = DateTime.Now.ToString("yyyyMMddhhmmss.") + ImageType;
                string ImagePath = LocationAddress + ImageName;
                if (!File.Exists(ImagePath))
                {
                    try
                    {
                        System.IO.File.WriteAllBytes(ImagePath, Imagedata);
                        ImageContext.Close();
                        return true;
                    }
                    catch
                    {
                        return false;
                    }
                }
                else
                {
                    return false;
                }
            }
        }
    }

    上面的代码用到几个方法,比如把流转化成字节、把JSON转化成实现等,代码如下:

    public byte[] ReadToEnd(System.IO.Stream stream)
            {
                long originalPosition = 0;
    
                if (stream.CanSeek)
                {
                    originalPosition = stream.Position;
                    stream.Position = 0;
                }
    
                try
                {
                    byte[] readBuffer = new byte[4096];
    
                    int totalBytesRead = 0;
                    int bytesRead;
    
                    while ((bytesRead = stream.Read(readBuffer, totalBytesRead, readBuffer.Length - totalBytesRead)) > 0)
                    {
                        totalBytesRead += bytesRead;
    
                        if (totalBytesRead == readBuffer.Length)
                        {
                            int nextByte = stream.ReadByte();
                            if (nextByte != -1)
                            {
                                byte[] temp = new byte[readBuffer.Length * 2];
                                Buffer.BlockCopy(readBuffer, 0, temp, 0, readBuffer.Length);
                                Buffer.SetByte(temp, totalBytesRead, (byte)nextByte);
                                readBuffer = temp;
                                totalBytesRead++;
                            }
                        }
                    }
    
                    byte[] buffer = readBuffer;
                    if (readBuffer.Length != totalBytesRead)
                    {
                        buffer = new byte[totalBytesRead];
                        Buffer.BlockCopy(readBuffer, 0, buffer, 0, totalBytesRead);
                    }
                    return buffer;
                }
                finally
                {
                    if (stream.CanSeek)
                    {
                        stream.Position = originalPosition;
                    }
                }
            }
    
    
    
        public class JsonModel
        {
            public string type { get; set; }
            public string EmployeeID { get; set; }
            public string TaskID { get; set; }
        }

    3:新建一个文本,然后修改其后缀名为.svc,作为我们发布服务(宿主为IIS)让Android手机调用, 然后把下面的代码写入

    <%@ ServiceHost Language="C#" Debug="true" Service="Coreius.CEIMS.AndroidService.AndroidInfoService" %>

    修改Web.config里面的内容:

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <appSettings>
        <add key="ConnectionString" value="server=127.0.0.1;database=Coreius;uid=sa;pwd=admin"/>
      </appSettings>
      <system.web>
        <compilation debug="true" targetFramework="4.0" />
      </system.web>
      <system.serviceModel>
        <behaviors>
          <endpointBehaviors>
            <behavior name="webHttp">
              <webHttp helpEnabled="true"/>
            </behavior>
          </endpointBehaviors>
          <serviceBehaviors>
            <behavior name="MapConfigBehavior">
              <!-- 为避免泄漏元数据信息,请在部署前将以下值设置为 false 并删除上面的元数据终结点 -->
              <serviceMetadata httpGetEnabled="true"/>
              <!-- 要接收故障异常详细信息以进行调试,请将以下值设置为 true。在部署前设置为 false 以避免泄漏异常信息 -->
              <serviceDebug includeExceptionDetailInFaults="true"/>
              <dataContractSerializer maxItemsInObjectGraph="2147483647"/>
            </behavior>
          </serviceBehaviors>
        </behaviors>
    
        <bindings>
          <webHttpBinding>
            <binding name="webHttpBindConfig" receiveTimeout="00:30:00" sendTimeout="00:30:00" maxReceivedMessageSize="104857600" transferMode="Streamed">
              <readerQuotas maxStringContentLength="2147483647" maxArrayLength="2147483647"/>
              <security mode="None"></security>
            </binding>
          </webHttpBinding>
        </bindings>
        <services>
          <service name="Coreius.CEIMS.AndroidService.AndroidInfoService" behaviorConfiguration="MapConfigBehavior">
            <endpoint binding="webHttpBinding" contract="Coreius.CEIMS.AndroidInterface.IAndroidInfo" bindingConfiguration="webHttpBindConfig" 
    
    behaviorConfiguration="webHttp"/> 
          </service>
        </services>
      </system.serviceModel>
    </configuration>

    此处有些要注意的地方:

    (1):此处采用的是webHttpBinding 所以一定要设置behaviorConfiguration才会有效果,其中helpEnabled="true"则是为实现可以在发布可以查看帮助信息    

            <behavior name="webHttp">
              <webHttp helpEnabled="true"/>
            </behavior>

    (2):为了实现上传大文件所以我们要如下设置最大值,其中security是设置访问服务的认证,此处是把它设置成为不认证,transferMode就是设置运用流的模式

          <webHttpBinding>
            <binding name="webHttpBindConfig" receiveTimeout="00:30:00" sendTimeout="00:30:00" maxReceivedMessageSize="104857600" transferMode="Streamed">
              <readerQuotas maxStringContentLength="2147483647" maxArrayLength="2147483647"/>
              <security mode="None"></security>
            </binding>
          </webHttpBinding>

    4:编写完上面的代码后就可以服务器IIS上部署这个WCF服务:

    四:Android编码

    由于Android手机端的代码是另外一个朋友编写,所以就把大体的代码贴出来,大体的原理就是把参数跟图片写入流(图片进行压缩处理),然后调用部署好的WCF服务

    代码一:因为服务器不是公用的,所以下面的IP我就随便修改的一个;

    private void toUploadFile(File file) throws FileNotFoundException {
    		String result = null;
    		requestTime= 0;
    		int res = 0;
    		long requestTime = System.currentTimeMillis();
    		long responseTime = 0;
    		
    		//封装参数信息
    		JSONObject jsonObject = new JSONObject();
    		try {
    			jsonObject.put("EmployeeID", MainActivity.guid);
    			jsonObject.put("TaskID", "e52df9b4-ee3b-46c5-8387-329b76356641");
    			String[] type = file.getName().split("\.");
    			jsonObject.put("type", type[type.length-1]);
    		} catch (JSONException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		
    		/**上传文件*/
    		HttpParams httpParameters = new BasicHttpParams();
    	    HttpConnectionParams.setConnectionTimeout(httpParameters, 1000*30);
    	    HttpConnectionParams.setSoTimeout(httpParameters, 1000*30);
    	    HttpConnectionParams.setTcpNoDelay(httpParameters, true);
    			
    		String path = PictureUtil.zipNewImage(file);	//压缩文件后返回的文件路径
    		byte[] bytes = null;
    	    InputStream is;
    	    File myfile = new File(path);
    		try {
    			is = new FileInputStream(path);
    			bytes = new byte[(int) myfile.length()];
    			int len = 0;
    			int curLen = 0;
    			while ((len = is.read(bytes)) != -1) {
    				curLen += len;
    				is.read(bytes);
    			}
    			is.close();
    		} catch (FileNotFoundException e1) {
    			// TODO Auto-generated catch block
    			e1.printStackTrace();
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		byte[] updata = GpsImagePackage.getPacket(jsonObject.toString(), bytes);	//参数与文件封装成单个数据包
    		HttpClient httpClient = new DefaultHttpClient(httpParameters);
    		HttpPost httpPost = new HttpPost(MyUrl.upload_file);
    		HttpResponse httpResponse;
    		//单个文件流上传
    		InputStream input = new ByteArrayInputStream( updata );
    		InputStreamEntity reqEntity;
    		reqEntity = new InputStreamEntity(input, -1);
            reqEntity.setContentType("binary/octet-stream");
            reqEntity.setChunked(true);
            httpPost.setEntity(reqEntity);
            try {
    			httpResponse = httpClient.execute(httpPost);
    			responseTime = System.currentTimeMillis();
    			this.requestTime = (int) ((responseTime-requestTime)/1000);
    			res = httpResponse.getStatusLine().getStatusCode();
    			if (httpResponse.getStatusLine().getStatusCode() ==200) {
    				Log.e(TAG, "request success");
    				Log.e(TAG, "result : " + result);
    				return;
    			} else {
    				Log.e(TAG, "request error");
    				sendMessage(UPLOAD_SERVER_ERROR_CODE,"上传失败:code=" + res);
    				return;
    			}
                } catch (ClientProtocolException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			} catch (IOException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    	}
    
    package com.anthony.util;
    /**
     * 服务器端接口
     * @author YWJ
     *
     */
    public class MyUrl {
    	public static String upload_GPS = http://122.199.19.23:8088/AndroidInfoService.svc/GpsUpFile;
    }
    

    代码二:

    package com.anthony.util;
    
    public class GpsImagePackage {
    	public GpsImagePackage() {
    		// TODO Auto-generated constructor stub
    	}
    	
    	//封装字节数组与参数
    	public static byte[] getPacket(String json,byte[] image){
    		
    		byte[] jsonb = json.getBytes();
    		int length = image.length + jsonb.length;
    		System.out.println(image.length +"    "+ jsonb.length);
    		byte[] bytes = new byte[length+1];
    		byte[] lengthb = InttoByteArray(jsonb.length, 1);
    		System.arraycopy(lengthb, 0, bytes, 0, 1);
    		System.arraycopy(jsonb, 0, bytes, 1, jsonb.length);
    		System.arraycopy(image, 0, bytes, 1+jsonb.length, image.length);
    		return bytes;
    		
    	}
    	
    	//将int转换为字节数组
        public static byte[] InttoByteArray(int iSource, int iArrayLen) {
        	
        	byte[] bLocalArr = new byte[iArrayLen];
        	for ( int i = 0; (i < 4) && (i < iArrayLen); i++) {
                 bLocalArr[i] = (byte)( iSource>>8*i & 0xFF );
            }
             return bLocalArr;
        }
    
         // 将byte数组bRefArr转为一个整数,字节数组的低位是整型的低字节位
         public static int BytestoInt(byte[] bRefArr) {
        	 
        	 int iOutcome = 0;
             byte bLoop;
             for ( int i =0; i<bRefArr.length ; i++) {
                bLoop = bRefArr[i];
                iOutcome+= (bLoop & 0xFF) << (8 * i);
             }
            return iOutcome;
         }
    }
    

    五:运行效果:

    如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】按钮。  因为,我的写作热情也离不开您的肯定支持。
     
    感谢您的阅读

    最近有个妹子弄的一个关于扩大眼界跟内含的订阅号,每天都会更新一些深度内容,在这里如果你感兴趣也可以关注一下(嘿对美女跟知识感兴趣),当然可以关注后输入:github 会有我的微信号,如果有问题你也可以在那找到我;当然不感兴趣无视此信息;

  • 相关阅读:
    LibreOJ #507. 「LibreOJ NOI Round #1」接竹竿
    BZOJ 4590: [Shoi2015]自动刷题机
    luogu P3808 【模板】AC自动机(简单版)
    cogs 2569. [東方] 博丽灵梦 梦想妙珠
    codeforces 1C. Ancient Berland Circus
    BZOJ 4570: [Scoi2016]妖怪
    HDU 1392 Surround the Trees
    cogs 999. [東方S2]雾雨魔理沙
    Uva 10652 Board Wrapping
    AC日记——[Sdoi2008]Cave 洞穴勘测 bzoj 2049
  • 原文地址:https://www.cnblogs.com/wujy/p/3297817.html
Copyright © 2020-2023  润新知