利用WCF与Android实现图片上传并传参
利用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; } } |
五:运行效果:
如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】按钮。 因为,我的写作热情也离不开您的肯定支持。
感谢您的阅读
利用WCF与Android实现图片上传并传参的更多相关文章
- 基于Http原理实现Android的图片上传和表单提交
版权声明:本文由张坤 原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/794875001483009140 来源:腾云阁 ...
- android批量文件上传(android批量图片上传)
项目中多处用到文件批量上传功能,今天正好解决了此问题,在此写出来,以便日后借鉴. 首先,以下架构下的批量文件上传可能会失败或者不会成功: 1.android客户端+springMVC服务端:服务端 ...
- Base64实现android端图片上传到server端
首先要下载Base64.java文件http://iharder.sourceforge.net/current/java/base64/ 将代码复制到project中. 然后上代码: android ...
- iOS利用AFNetworking(AFN) 实现图片上传
1.上传图片以二进制流的形式上传 1 #pragma mark - 文件上传 2 - (IBAction)uploadImage 3 { 4 /* 5 此段代码如果需要修改, ...
- 利用HTML5和css3 选中图片上传到服务器,插件地址如下
https://yusi123.com/3349.html 分三步,需要将js文件和css文件拷贝到项目目录下,在需要选择的图片的文件中引入,然后将HTML代码复制 <!--选择图片模块star ...
- android之使用GridView+仿微信图片上传功能
由于工作要求最近在使用GridView完成图片的批量上传功能,我的例子当中包含仿微信图片上传.拍照.本地选择.相片裁剪等功能,如果有需要的朋友可以看一下,希望我的实际经验能对您有所帮助. 直接上图,下 ...
- Android开发之使用GridView+仿微信图片上传功能(附源代码)
前言:如果转载文章请声明转载自:https://i.cnblogs.com/EditPosts.aspx?postid=7419021 .另外针对有些网站转载本人的文章结果源码链接不对的问题,本人在 ...
- Android 使用GridView+仿微信图片上传功能(附源代码)
由于工作要求最近在使用GridView完成图片的批量上传功能,我的例子当中包含仿微信图片上传.拍照.本地选择.相片裁剪等功能,如果有需要的朋友可以看一下,希望我的实际经验能对您有所帮助. 直接上图,下 ...
- C#设计模式总结 C#设计模式(22)——访问者模式(Vistor Pattern) C#设计模式总结 .NET Core launch.json 简介 利用Bootstrap Paginator插件和knockout.js完成分页功能 图片在线裁剪和图片上传总结 循序渐进学.Net Core Web Api开发系列【2】:利用Swagger调试WebApi
C#设计模式总结 一. 设计原则 使用设计模式的根本原因是适应变化,提高代码复用率,使软件更具有可维护性和可扩展性.并且,在进行设计的时候,也需要遵循以下几个原则:单一职责原则.开放封闭原则.里氏代替 ...
随机推荐
- Windows Phone 8.1 应用生命周期
原文:Windows Phone 8.1 应用生命周期 一.“后退键”不会终止应用 关于 Windows Phone 8.1 的应用生命周期,第一个要知道的关键就是:“后退键”不会终止应用! 在 8. ...
- 使用myeclipse创建带注解的model实体类
1.先新建JPA项目: 如果没有就点击左下角的Show All Wizards. 点两次Next后,点击Finish即可,中间不用任何操作 (点第二次Next后会出现连接到所在数据库,先不管) ...
- Swift构造函数(Initializer)和析构函数(Deinitializer)
要初始化结构和类和其他类型的实例的属性. 默认的构造函数 struct Fahrenheit { var temperature: Doubleinit(){ temperature = 32.0 } ...
- UVALive 6472 Powers of Pascal
标题手段: 他给了一个无限Pascal阵,定义了powers,然后询问power为P的pascal矩阵里面的第R行C列的元素是多少. 最開始读错题意了...然后 就成了一个神得不得了的题了.后来请教的 ...
- dojo/request
dojo/request模块整体架构解析 总体说明 做前端当然少不了ajax的使用,使用dojo的童鞋都知道dojo是基于模块化管理的前端框架,其中对ajax的处理位于dojo/request模块 ...
- c#数据库操作大全
原文:c#数据库操作大全 1.提取单条记录 //using System.Data; //using System.Data.SqlClient; using (SqlConnection cn = ...
- MFC中的HOOK编程
HOOK,n.钩, 吊钩,通常称钩子. 在计算机中,是Windows消息处理机制的一个平台,应用程序能够在上面设置子程以监视指定窗体的某种消息,并且所监视的窗体能够是其它进程所创建的.当消息到达后,在 ...
- 【淡墨Unity3D Shader计划】五 圣诞用品: Unity在Shader三种形式的控制&混合操作编译
本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/42060963 作者:毛星云(浅墨) ...
- ORA-00265: instance recovery required, cannot set ARCHIVELOG mode
症状: 我打开后归档命令报告运行错误ORA-00265 SQL> alter database archivelog; alter database archivelog * ERROR at ...
- Lyx输入中文与代码高亮
如果您看了我的这个随笔:<OpenSUSE 13.2安装Texlive2014+Texmaker+Lyx> (一)LyX中文 打开Lyx直接新建开始使用,那么输入的中文会是编译失败的,疑? ...