1.定义接口:
[ServiceContract]
publicinterface IUploadService
{
[OperationContract]
[WebInvoke(Method ="POST", UriTemplate ="/Create")]
string Create(Stream stream);
}
2.实现服务:
publicclass UploadService: IUploadService
{
publicstring Create(Stream stream)
{
//TODO: manipulate the stream whatever you want
}
}
3.Host Service:
新建一个WCF Service Application项目,在Web.Config进行如下配置:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="webBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true">
<serviceActivations>
<add relativeAddress="UploadService.svc" service="Service.UploadService"/>
</serviceActivations>
</serviceHostingEnvironment>
<services>
<service name="Service.UploadService">
<endpoint address="" behaviorConfiguration="webBehavior" binding="webHttpBinding" contract="Contract.IUploadService"/>
</service>
</services>
</system.serviceModel>
4.扔到IIS里,结束
5.测试上传(用WebClient有点问题没有来得及找出原因——原因是WebClient在内部生成的Request中添加了一些信息,导致生成的MessageBodyStream是这些信息跟所上传文件的并集,造成无法直接变成文件的MemoryStream;要的话需要去除这些信息,所以直接使用WebRequest):
using (var fileStream =new FileStream("F:\\abcdefg.zip", FileMode.Open))
{
var request = (HttpWebRequest)WebRequest.Create("http://localhost:8800/UploadService.svc/Create");
request.Method ="POST";
request.ContentType ="application/x-zip-compressed";
request.ContentLength = fileStream.Length;
var requestStream = request.GetRequestStream();
// The buffer size is set to 2kb
constint buffLength =2048;
var buff =newbyte[buffLength];
fileStream.Position =0;
var contentLength = fileStream.Read(buff, 0, buffLength);
while (contentLength !=0)
{
requestStream.Write(buff, 0, contentLength);
contentLength = fileStream.Read(buff, 0, buffLength);
}
requestStream.Close();
var response = (HttpWebResponse)request.GetResponse();
var reader =new StreamReader(response.GetResponseStream());
var temp = reader.ReadToEnd();
Console.WriteLine(temp);
reader.Close();
}
============================ 分割线 ============================
以上代码未经测试,是由当前实现中抽出,可能隐含bug。
其实以上都不是我在这篇文章要说的东西,因为这类代码相信到处都是。今天花了大半下午解决的一个问题(对WCF不熟)才是我想说的。
之前针对Service写了个测试代码(采用XUnit框架):
[Fact]
publicvoid Create_Should_Success()
{
using (var zip =new FileStream("F:\\abcdefg.zip", FileMode.Open))
{
var msg = _service.Create(zip);
Assert.Equal("Success!", msg);
}
}
用这个方法悲剧的地方就是避开了上传文件这一步骤,让我认为在IIS环境里UploadService.Create()读取到的Stream就是FileStream或者是MemoryStream。结果下午部署到IIS(之前在本机上用IIS Express)一看,问题来了:stream.Length无法读取。仔细看看stream的类型,成了MessageBodyStream。呵,这下傻眼了,原本对IO这块不甚了解,现在还蹦出个让人挠头的东西。
花了好多时间,终于找到原因了:通过REST上传的文件,一定是MessageBodyStream类型的,它本身可以直接用,但如果有一些特殊的应用场景——譬如读取Length——则必须将它转换成别的类型(MemoryStream..)。
写完回家。