編輯: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>
:編寫完上面的代碼後就可以服務器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;
- }
- }
五:運行效果:
登錄應用程序的屏幕,詢問憑據登錄到一些特定的應用。可能需要登錄到Facebook,微博等本章介紹了,如何創建一個登錄界面,以及如何管理安全問題和錯誤嘗試。首先,必須定義兩
Android應用程序可以在許多不同地區的許多設備上運行。為了使應用程序更具交互性,應用程序應該處理以適合應用程序將要使用的語言環境方面的文字,數字,文件等。在本章中,我
Toast英文含義是吐司,在Android中,它就像烘烤機裡做好的吐司彈出來,並持續一小段時間後慢慢消失Toast也是一個容器,可以包含各種View,並承載著它們
本文實例講述了Android編程實現監聽EditText變化的方法。分享給大家供大家參考,具體如下: 監聽EditText中的內容的變化。在EditText沒有找