WCF 使用Stream模式进行文件上传 --节选自Packt.Net.Framework.4.5.Expert.Programming.Cookbook
使用Stream上传文件
文件上传功能是web程序/服务上常用和必须的功能,WCF也不例外。在4.0版本之前,WCF仅仅提供了buffered模式上传文件。从4.0版本之后,WCF开始提供了Streaming模式。在buffered模式中,实体文件需要在WCF服务可以访问之前上传到服务。在Streaming模式中,服务在文件上传完成之前就可以访问了。当你需要通过服务对文件进行处理或者由于文件太大无法将文件进行Buffered的时候,Stream模式就非常有用了。
在本节,我们来看看如何实现和配置一个服务,上其支持Streaming模式进行文件上传。
如何做
- 启动Visual Studio2012,创建一个WCF服务库,命名为WcfFileUploadService
- 添加一个新的类,名称为UploadDetails
- 打开类文件,用[MessageContract]修饰这个类
- 将类声明为public
- 给类添加下面的属性
Name |
Data type |
FileName |
String |
Data |
Stream |
- 用[MessageHeader]修饰FileName属性
- 用[MessageBodyMember]修饰data属性
- 修改之后,UploadDetails类应该看起来是这样的
[MessageContract] public class UploadDetails { [MessageHeader] public string FileName { get; set; } [MessageBodyMember] public Stream Data { get; set; } } |
- 将IService重命名为IUploadService,将Service类重命名为UploadService
- 打开IUploadService类,移除已经存在的代码
- 添加一个方法接受一个UploadDetails类型的参数,返回类型是void,方法名为Upload,它的方法前面如下:
void Upload(UploadDetails details); |
- 用[OperationContract]修改这个方法,这个接口现在是这样
[ServiceContract] public interface IUploadService { [OperationContract] void Upload(UploadDetails details); } |
- 下一步,打开UploadService类,实现IUploadService接口。移除这个类现有的代码
- 实现IUploadService接口的Upload方法
- 添加下面的代码到Upload方法:
using (FileStream fs = new FileStream(@"C:\Downloads\"+details. FileName, FileMode.Create)) { int bufferSize = 1 * 1024 * 1024; byte[] buffer = new byte[bufferSize]; int bytes; while ((bytes = details.Data.Read(buffer, 0, bufferSize)) > 0) { fs.Write(buffer, 0, bytes); fs.Flush(); } } |
- 在前面的代码当中的C:\Downloads可以替换为你自己允许保存文件的目录(当要有确保文件目录存在,且有读写权限)
- 现在打开App.config,在<services>节前面加入下面的binding
<bindings> <basicHttpBinding> <binding name="UploadServiceBinding" messageEncoding="Text" transferMode="Streamed" maxBufferSize="65536" maxReceivedMessageSize="5242880"> </binding> </basicHttpBinding> </bindings> |
- 将<service>节点修改为下面的样子
<service name="WcfFileUploadService.UploadService"> <endpoint address="" binding="basicHttpBinding" bindingConfiguration="UploadServiceBinding" contract="WcfFileUploadService.IUploadService"> <identity> <dns value="localhost" /> </identity> </endpoint> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> <host> <baseAddresses> <add baseAddress="http://localhost:8733/Design_Time_ Addresses/WcfFileUploadService/Service1/" /> </baseAddresses> </host> </service> |
- 添加一个Windows Form Appliction命名为UploadServiceTestApp
- 将Form1.cpp重命名为UploadTestForm.cs
- 切换到设计界面,将界面按一下截图进行设置
- 将控件按下表进行命名
Control |
Name |
Description |
Textbox |
txtFile |
显示选择文件的路径 |
Button |
btnBrowse |
调用文件上传服务 |
- 添加对UploadService服务的引用,将其命名为UploadServiceReference
- 双击btnBrowse按钮,添加事件处理器
- 在事件处理过程中添加以下代码
OpenFileDialog diagOpen = new OpenFileDialog(); if (diagOpen.ShowDialog() == System.Windows.Forms.DialogResult.OK) { try { txtFile.Text = diagOpen.FileName; UploadServiceReference.UploadServiceClient client = new UploadServiceReference.UploadServiceClient(); client.Upload(Path.GetFileName(txtFile.Text), File. Open(txtFile.Text, FileMode.Open)); MessageBox.Show("Upload successful"); } catch (Exception) { MessageBox.Show("Upload failed"); } } |
- 在主菜单中点击 调试|运行新实例,你会看到以下截图
- 点击Browse and Upload按钮,选择一个文件上传。
- 如果上传成功,你会看到下面的消息框
- 如果不成功,会看到错误提示
实现原理
为了使用了Streaming模式上传文件,服务应该提供一个方法访问Stream类型的参数,有一点需要记住如果方法有一个Stream参数,就不允许有其他的参数或其他的返回值,否则服务就不能运行。这就是我们为什么要要将UploadDetails配置成为MessageContract的原因。
[MessageContract] public class UploadDetails { [MessageHeader] public string FileName { get; set; } [MessageBodyMember] public Stream Data { get; set; } } |
在前面的代码中,我们给FileName添加[MessageHeader]修饰,而不是[MessageBodyMember],这是因为Data是一个Stream类型成员。如果MessageContract包含了一个Stream类型的MessageBodyMember,其他属性只能做作为MessageHeader,换句话说,如果有一个Stream类型的属性,那么就只能有一个MessageBodyMember。其他的属性都必须是MessageHeader (那么Stream类型的成员只能有一个)。
在UploadService类的Upload方法中,我们首先打开了一个FileStream往文件夹中写文件。然后,然后我将UploadDetails实例中的Stream类型成员的数据写入到文件中。
using (FileStream fs = new FileStream(@"C:\Downloads"+details. FileName, FileMode.Create)) { int bufferSize = 1 * 1024 * 1024; byte[] buffer = new byte[bufferSize]; int bytes; while ((bytes = details.Data.Read(buffer, 0, bufferSize)) > 0) { fs.Write(buffer, 0, bytes); fs.Flush(); } } |
为了告诉.NET Runtime我们需要使用Stream模式,我们需要添加一个新的binding命名为UploadServiceBinding。在binding中,我设置messageEncoding为Text,transferMode为Streamed,然后我们设置最大的buffer长度,以及消息大小的上限,消息的大小决定了可以上传的最大尺寸。
<bindings> <basicHttpBinding> <binding name="UploadServiceBinding" messageEncoding="Text" transferMode="Streamed" maxBufferSize="65536" maxReceivedMessageSize="5242880"> </binding> </basicHttpBinding> </bindings> |
因为我们想使用HTTP自己来传输,我们使用了<basicHttpBinding>。在<service>节点中,我们设置了bindingConfiguration为UploadServiceBinding,看下面高亮的代码
<service name="WcfFileUploadService.UploadService"> <endpoint address="" binding="basicHttpBinding" bindingConfiguration="UploadServiceBinding" contract="WcfFileUploadService.IUploadService"> <identity> <dns value="localhost" /> </identity> </endpoint> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> <host> <baseAddresses> <add baseAddress="http://localhost:8733/Design_Time_Addresses/ WcfFileUploadService/Service1/" /> </baseAddresses> </host> </service> |
在UploadTestForm中,我们通过选择的文件并用文件的内容传教的FileStream实例调用服务的Upload方法。
txtFile.Text = diagOpen.FileName; UploadServiceReference.UploadServiceClient client = new UploadServiceReference.UploadServiceClient(); client.Upload(Path.GetFileName(txtFile.Text), File.Open(txtFile.Text, FileMode.Open)); |
在接口和实现中,我们使用MessageContract约束的类型作为Upload方法的参数。但是我们在客户端调用的时候,分别传递了文件名和Stream的实例,(也就是说客户端调用时方法传的参数和WCF接口声明不一致),将参数封装为MessageContract,是由.NET Runtime完成的,将其进行类型转换是在服务端完成的。
你不能在binding中同时将传输模式设置为Streamed,而将编码设置为MOTM,如果同时使用这两项设置会在上传文件的时候引发问题,问题以及引发问题的原因超出了本文论述的范围。 |
WCF 使用Stream模式进行文件上传 --节选自Packt.Net.Framework.4.5.Expert.Programming.Cookbook的更多相关文章
- Facade模式实现文件上传(Flash+HTML5)
一.前言 确定了渐进式增强的上传方式,接下来我们需要将上传功能从具体的业务逻辑中剥离出来,作为公共组件供业务层调用.这就要求我们必须对业务层隐藏上传细节,只暴露统一的上传API.这时候大家是不是跟我一 ...
- MVC+WCF框架下广告位管理——文件上传
广告位是站点中不可缺少的内容之中的一个.也是能直接给我们站点带来经济收益的内容之中的一个. 好的广告位不仅不会强宾压主,而会为我们的站点锦上添花.起到画龙点睛的作用.因此设计好广告位也是开发过程中一大 ...
- koa2基于stream(流)进行文件上传和下载
阅读目录 一:上传文件(包括单个文件或多个文件上传) 二:下载文件 回到顶部 一:上传文件(包括单个文件或多个文件上传) 在之前一篇文章,我们了解到nodejs中的流的概念,也了解到了使用流的优点,具 ...
- PHP fastcgi模式大文件上传500错误
最近在项目中中上传图片时,大约有300多K,结果报了个服务器错误,以前从未遇到过,错误的内容如下: mod_fcgid: www.111cn.net HTTP request length 13229 ...
- ADODB.Stream在进行文件上传时报错
最近在做web项目,有个控件是上传材料文件和文件夹,本地运行正常,放到服务器上,一直报错:AutoRuntime服务器无法创建..... 解决方法: 1.配置ie浏览器的安全级别 2.修改ie浏览器对 ...
- 完整的多文件上传实例(java版)
昨天刚刚做了一个文件列表上传,后端很简单,用 MultipartFile[] files 获取文件流数组,后端就当IO流操作就可以,似乎好像没啥好写的,但是!!!!!前端是真的糙单.要是自己写一个前端 ...
- TZ_06_SpringMVC_传统文件上传和SpringMVC文件上传方式
1.传统文件上传方式 <!-- 文件上传需要的jar --> <dependency> <groupId>commons-fileupload</groupI ...
- Spring3文件上传,提速你的Web开发
Spring1 推出的时候可以说是不小的颠覆了J2EE 的开发,彻底把EJB打败,将J2EE开发进行简化,Spring2 推出以后完美的与多种开源框架与服务器的结合,让你对其拥抱的更紧,Spring变 ...
- Wcf 文件上传下载
wcf 文件上传的例子网上很多,我也是借鉴别人的示例.wcf 文件下载的示例网上就很少了,不知道是不是因为两者的处理方式比较类似,别人就没有再上传了.在此本人做下记录备忘. UploadFile.sv ...
随机推荐
- MySQL安装时出现的问题
mysql正常安装结束之后需要连接你所安装的数据库的时候出现下面的错误: Client does not support authentication protocol requested by se ...
- C语言位操作中指定的某一位数置0、置1、取反
一.指定的某一位数置1 宏 #define setbit(x,y) x|=(1<<y) 二.指定的某一位数置0 宏 #define clrbit(x,y) x&=~(1< ...
- 在Ubuntu中安装了MongoDB后无法启动mongod的问题
今天准备学习MongoDB,没想到下载之后服务器端启动不了,记录一下问题和处理过程 一.安装 在Ubuntu中安装还是很简单,直接:sudo apt install mongodb 二.启动 启动Mo ...
- javaIO -- InputStream和OutStream
一.简介 InputStream 和 OutputStream 对于字节流的输入和输出是作为协议的存在 所以有必要了解下这两个类提供出来的基本约定,这两个类是抽象类,而且基本上没什么实现,都是依赖于子 ...
- JSON文件转为Excel
前言 今天在帮老师做年终党统的时候,发现管理平台上没有将正在发展的同志的信息导出功能,只能一个一个点击进去查看,操作起来步骤很多很麻烦,所以我就想到了"扒"一下这个网页,扒下来发现 ...
- WUSTOJ 1247: 递增或递减排序(Java)
1247: 递增或递减排序 题目 有n个整数,求它的递增排序序列或递减排序序列.更多内容点击标题. 分析 统一升序排序,输出的时候做区分. 先区分是升序还是降序,调用库函数. 代码 方法1,将 ...
- SAS学习笔记3 输入输出格式(format、informat函数)
format函数:定义输出格式 informat函数:定义输入格式 proc format:定义输出格式 从外部读取文件 proc format过程步
- 基于Task的多线程
/// <summary> /// 基于Task的多线程 /// </summary> public class Tasks { public static void Task ...
- .net core下对于Excel的一些操作及使用
原文:.net core下对于Excel的一些操作及使用 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.cs ...
- JS OOP 概述
JS面向对象,大致内容 1.面向对象的基础 2.深入认识JS的函数 3.JS类的实现 4JS中共有成员,私有成员和静态成员 5.JS的反射 6.JS的继承 7.JS实现抽象类 8.JS事件设计模式 9 ...