WebService支持多平台上传文件的实现
要使用网站上传文件,在ASP.NET的范畴,我基本上能想到的有两类,一类是通过HTTP POST请求获得文件信息,另外一类是通过WebService或者WCF之类的技术对外发布服务。
以前做3G摄像头项目的时候,做过使用HTTP POST的方式获得照片,方式很简单,就是一个aspx文件(其实用asp也行,最开始是用asp实现的,用aspx的时候还出了点幺蛾子),只是客户端(摄像头)那边需要了解POST请求,将图片做成数据传输上来。这个方式也没什么不好的,就是无奈在集成到系统中的时候总是出点问题,想着反正还要做多种客户端,于是乎改成第二类方式。
需求是要能够实现多个平台(PC,Android,iOS)的文件传输。
我说用WCF吧,之前我用来着,容易上手,但是其他人觉得WCF可能不太容易与非.NET平台互通(没深入探究,但是网上看到过有人用android调用WCF的,以后有时间再探讨),于是乎老老实实的用WebService。
Android调用webservice好像也有两种方式,我们实现的是用ksoap2调用,首先我需要编写WEBSERVICE。
文件用什么参数传递呢?
-
简单暴力byte[],使用File类或者FileStream类可以很容易将文件流汇入byte数组,直接传递。
-
不简单但是暴力string,使用某种方式将文件变成string,再进行传输。
-
其他方法,类似object?或者别的什么。
这里有序列化的知识,我还没深入体会,说说自己的看法吧:
首先序列化个人看来就是把对象什么的变得可以存储和传输,有了这个就能够很方便的实现一些网络应用。然后就是说KSOAP2对基本的数据类型都能够序列化,我看到string了,但是也不知道支持还是不支持byte[]型。
回到正题,干脆2个都来吧,反正也不差多少事。
由于自己有点疑虑,所以先实现了第二种,通过某种方式---base64编码解码。通过这个编码可以将BYTE变成能够直接网络传输的string,server端收到数据之后解码就能够得到原始byte[]。以下列出web端函数。
[WebMethod(MessageName = "UploadSmallString")] public bool UploadSmallString(string fileName, string serializedData) { try { byte[] receivedBytes = Convert.FromBase64String(serializedData); using (FileStream fs = new FileStream(HttpContext.Current.Server.MapPath(fileName), FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite)) { fs.Write(receivedBytes, 0, receivedBytes.Length); } return true; } catch { return false; } }
客户端用base64编码就可以了,网上找找很多。细心的客官可能看到函数名SmallString,对的,这个适用于文件不太大的时候传输,传输很大的文件呢?
分块!分块传输的好处在于能够支持断点续传,说说客户端的实现思路:
1.判断文件大小
2.是否适用于分块传输
3.调用服务进行操作
然后服务器端呢,写个重载吧,可是试了试不成功,参见http://www.cnblogs.com/menglin2010/archive/2012/03/29/2421445.html中说不太支持,要改,用[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]如果改成None用以支持重载的话,又怕出现兼容性问题,老老实实的吧还。
/// <summary> /// 使用BASE64编码接收分块传输的大文件 /// </summary> /// <param name="fileName">文件名</param> /// <param name="serializedData">BASE64数据</param> /// <param name="blockSerial">用以识别文件块的ID</param> /// <returns>对应成功文件块ID,不成功便成-1</returns> [WebMethod(MessageName = "UploadBlobString", Description = "支持大文件传输的方法,blockserial为0将创建新文件,这也是默认行为")] public int UploadBlobString(string fileName, string serializedData, int blockSerial = 0) { try { byte[] receivedBytes = Convert.FromBase64String(serializedData); if (blockSerial == 0) { using (FileStream fs = new FileStream(HttpContext.Current.Server.MapPath(fileName), FileMode.Create)) { fs.Write(receivedBytes, 0, receivedBytes.Length); } } else { using (FileStream fs = new FileStream(HttpContext.Current.Server.MapPath(fileName), FileMode.Append)) { fs.Write(receivedBytes, 0, receivedBytes.Length); } } return blockSerial; } catch { return -1; } }
琢磨了一下,好像这个完全可以替代前一个方法,是用.NET默认参数的特性,指定blockSerial默认为0。如果客户端中断了,续传的时候从断点blockSerial开始就OK了。
接下来说说第一种,使用bye[]的方式。
[WebMethod(MessageName = "UploadSmallByte", Description = "直接发送BYTE数组存储")] public bool UploadSmallByte(string fileName, byte[] fileBytes) { try { using (FileStream fs = new FileStream(HttpContext.Current.Server.MapPath(fileName), FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite)) { fs.Write(fileBytes, 0, fileBytes.Length); } return true; } catch { return false; } }
这种方式我没怎么测试过,但是有一个有意思的事情,就是如果ANDROID编码BASE64后的string直接传递给byte[]参数,这个函数依然可以正常工作,上传的图片还是可以正常显示,很是诡异呀,想在.net下试一试,直接由于类型不同,不能编译,以后有机会再琢磨琢磨。
P.S. BASE64方式的代码经过测试能够在ANDROID平台和PC平台下通过,iOS等待BASE64实现(貌似没有内部的BASE64编码方法),从原理上应该没问题,欢迎大家讨论。