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 ...
随机推荐
- 【计算机视觉】Selective Search for Object Recognition论文阅读2
Selective Search for Object Recognition 是J.R.R. Uijlings发表在2012 IJCV上的一篇文章.主要介绍了选择性搜索(Selective Sear ...
- linux 安装了Linux generic mysql 出错
在一次安装 Linux generic cmake 出错 提示:CMake Error: The source directory "/usr/local/src" does no ...
- 为何我的网站http总是跳转https?能不能让http不跳转https?谈谈我遇到的坑和解决方案。
如题,突然想给我的个人网站加一个ssl,让它能够通过https访问. 但一顿操作猛如虎后,发现只能https访问了,手动输入http也会无限跳转到https. 现在说下我的排查思路和解决方案: 1. ...
- AI新生代“教父”崛起,或成就迈向具有类人意识机器的一大步
<麻省理工科技评论>公布了 2018 年全球十大突破性技术,“对抗性神经网络”即“生成对抗网络”作为突破性人工智能技术赫然上榜.这家全球最顶级科技杂志编辑部对这项革命性技术给出的评价是:它 ...
- linux系统中RAID5磁盘冗余阵列配置(5块磁盘)
RAID5:需要至少三块(含)硬盘,兼顾存储性能.数据安全和储存成本. 如图所示”parity”块中保存的是其他硬盘数据的奇偶校验信息(并非其他硬盘的数据),以数据的奇偶校验信息来保证数据的安全,RA ...
- pytorch中F.softmax(x1,dim = -1) dim 取值测试及验证
# -*- coding: utf-8 -*- """ Created on Mon May 27 11:09:52 2019 @author: jiangshan &q ...
- Solr综合案例深入练习
1. 综合案例 1.1. 需求 使用Solr实现电商网站中商品信息搜索功能,可以根据关键字.分类.价格搜索商品信息,也可以根据价格进行排序,并且实现分页功能. 界面如下: 1.2. 分析 开发人员需要 ...
- 学习嵌入式为什么要有uboot(深度解析)
ref:http://www.elecfans.com/d/617674.html 为什么要有uboot 1.1.计算机系统的主要部件 (1)计算机系统就是以CPU为核心来运行的系统. 典型的 ...
- LaTeX技巧96:LaTeX 图片控制命令,位置控制
LaTeX技巧96:LaTeX 图片控制命令,位置控制 2012-04-05 17:25:44 zd0303 阅读数 28512更多 分类专栏: Latex LaTeX 控制图片的位置,就是加感叹 ...
- Bloom过滤器
提出一个问题 在我们细述Bloom过滤器之前,我们先抛出一个问题:给你一个巨大的数据集(百万级.亿级......),怎么判断一个元素是否在此数据集中?或者怎么判断一个元素不在此数据集中? 思考这个问题 ...