使用Stream上传文件

文件上传功能是web程序/服务上常用和必须的功能,WCF也不例外。在4.0版本之前,WCF仅仅提供了buffered模式上传文件。从4.0版本之后,WCF开始提供了Streaming模式。在buffered模式中,实体文件需要在WCF服务可以访问之前上传到服务。在Streaming模式中,服务在文件上传完成之前就可以访问了。当你需要通过服务对文件进行处理或者由于文件太大无法将文件进行Buffered的时候,Stream模式就非常有用了。

在本节,我们来看看如何实现和配置一个服务,上其支持Streaming模式进行文件上传。

如何做

  1. 启动Visual Studio2012,创建一个WCF服务库,命名为WcfFileUploadService
  2. 添加一个新的类,名称为UploadDetails
  3. 打开类文件,用[MessageContract]修饰这个类
  4. 将类声明为public
  5. 给类添加下面的属性

Name

Data type

FileName

String

Data

Stream

  1. 用[MessageHeader]修饰FileName属性
  2. 用[MessageBodyMember]修饰data属性
  3. 修改之后,UploadDetails类应该看起来是这样的

[MessageContract]

public class UploadDetails

{

[MessageHeader]

public string FileName { get; set; }

[MessageBodyMember]

public Stream Data { get; set; }

}

  1. 将IService重命名为IUploadService,将Service类重命名为UploadService
  2. 打开IUploadService类,移除已经存在的代码
  3. 添加一个方法接受一个UploadDetails类型的参数,返回类型是void,方法名为Upload,它的方法前面如下:

void Upload(UploadDetails details);

  1. 用[OperationContract]修改这个方法,这个接口现在是这样

[ServiceContract]

public interface IUploadService

{

[OperationContract]

void Upload(UploadDetails details);

}

  1. 下一步,打开UploadService类,实现IUploadService接口。移除这个类现有的代码
  2. 实现IUploadService接口的Upload方法
  3. 添加下面的代码到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();

}

}

  1. 在前面的代码当中的C:\Downloads可以替换为你自己允许保存文件的目录(当要有确保文件目录存在,且有读写权限)
  2. 现在打开App.config,在<services>节前面加入下面的binding

<bindings>

<basicHttpBinding>

<binding

name="UploadServiceBinding"

messageEncoding="Text"

transferMode="Streamed"

maxBufferSize="65536"

maxReceivedMessageSize="5242880">

</binding>

</basicHttpBinding>

</bindings>

  1. 将<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>

  1. 添加一个Windows Form Appliction命名为UploadServiceTestApp
  2. 将Form1.cpp重命名为UploadTestForm.cs
  3. 切换到设计界面,将界面按一下截图进行设置
  4. 将控件按下表进行命名

Control

Name

Description

Textbox

txtFile

显示选择文件的路径

Button

btnBrowse

调用文件上传服务

  1. 添加对UploadService服务的引用,将其命名为UploadServiceReference
  2. 双击btnBrowse按钮,添加事件处理器
  3. 在事件处理过程中添加以下代码

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");

}

}

  1. 在主菜单中点击 调试|运行新实例,你会看到以下截图
  2. 点击Browse and Upload按钮,选择一个文件上传。
  3. 如果上传成功,你会看到下面的消息框
  1. 如果不成功,会看到错误提示

实现原理

为了使用了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的更多相关文章

  1. Facade模式实现文件上传(Flash+HTML5)

    一.前言 确定了渐进式增强的上传方式,接下来我们需要将上传功能从具体的业务逻辑中剥离出来,作为公共组件供业务层调用.这就要求我们必须对业务层隐藏上传细节,只暴露统一的上传API.这时候大家是不是跟我一 ...

  2. MVC+WCF框架下广告位管理——文件上传

    广告位是站点中不可缺少的内容之中的一个.也是能直接给我们站点带来经济收益的内容之中的一个. 好的广告位不仅不会强宾压主,而会为我们的站点锦上添花.起到画龙点睛的作用.因此设计好广告位也是开发过程中一大 ...

  3. koa2基于stream(流)进行文件上传和下载

    阅读目录 一:上传文件(包括单个文件或多个文件上传) 二:下载文件 回到顶部 一:上传文件(包括单个文件或多个文件上传) 在之前一篇文章,我们了解到nodejs中的流的概念,也了解到了使用流的优点,具 ...

  4. PHP fastcgi模式大文件上传500错误

    最近在项目中中上传图片时,大约有300多K,结果报了个服务器错误,以前从未遇到过,错误的内容如下: mod_fcgid: www.111cn.net HTTP request length 13229 ...

  5. ADODB.Stream在进行文件上传时报错

    最近在做web项目,有个控件是上传材料文件和文件夹,本地运行正常,放到服务器上,一直报错:AutoRuntime服务器无法创建..... 解决方法: 1.配置ie浏览器的安全级别 2.修改ie浏览器对 ...

  6. 完整的多文件上传实例(java版)

    昨天刚刚做了一个文件列表上传,后端很简单,用 MultipartFile[] files 获取文件流数组,后端就当IO流操作就可以,似乎好像没啥好写的,但是!!!!!前端是真的糙单.要是自己写一个前端 ...

  7. TZ_06_SpringMVC_传统文件上传和SpringMVC文件上传方式

    1.传统文件上传方式 <!-- 文件上传需要的jar --> <dependency> <groupId>commons-fileupload</groupI ...

  8. Spring3文件上传,提速你的Web开发

    Spring1 推出的时候可以说是不小的颠覆了J2EE 的开发,彻底把EJB打败,将J2EE开发进行简化,Spring2 推出以后完美的与多种开源框架与服务器的结合,让你对其拥抱的更紧,Spring变 ...

  9. Wcf 文件上传下载

    wcf 文件上传的例子网上很多,我也是借鉴别人的示例.wcf 文件下载的示例网上就很少了,不知道是不是因为两者的处理方式比较类似,别人就没有再上传了.在此本人做下记录备忘. UploadFile.sv ...

随机推荐

  1. Leetcode739 - Daily Temperatures

    题目描述 Leetcode 739 本题考察了栈的使用.题目输入是一段温度值列表,然后返回一个列表.这个列表包含了输入列表中每一天还有多少天温度升高.如果未来没有升高的情况,则输入 0. # Exam ...

  2. 工作总结 CTO(张王岩) File构造器

    import java.io.File; /** * 构建File对象 * @author Allen17805272076 * */ public class FileDemo2 { public ...

  3. FastJson反序列化获取不到值

    今天碰到一个问题,使用fastjson反序列化,就是将JSON解析成javaBean时,一个字段值为null.后面经查,是JavaBean中的set方法写错了,fastJson解析的是利用反射通过se ...

  4. hadoop基本文件配置

    [学习笔记] 5)hadoop基本文件配置:hadoop配置文件位于:/etc/hadoop下(etc即:“etcetera”(附加物))core-site.xml:<configuration ...

  5. 剑指offer15:反转链表后,输出新链表的表头。

    1 题目描述 输入一个链表,反转链表后,输出新链表的表头. 2 思路和方法 (1)利用栈作为中间存储,进行链表的反转,将压入的数据按先进后出的顺序弹出依次赋给链表再输出表头pHead. (2)将当前节 ...

  6. I2C读写EEPROM—EEPROM简介

    EEPROM 是一种掉电后数据不丢失的存储器,常用来存储一些配置信息,以便系统重新上电的时候加载之.EEPOM 芯片最常用的通讯方式就是 I 2C 协议,本小节以 EEPROM 的读写实验为大家讲解如 ...

  7. poj 3617 弱鸡贪心

    比赛的时候扣了一道贪心的题目,不会写,,现在补一补一些基础的贪心. 题意:给定一个字符串s,要求按下列操作生成一个新串t--每次从s串中的最前和最后取一个字符给t,要求生成的t字典序最小. 题解:由于 ...

  8. hdu 6153 思维+扩展kmp

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6153 扩展kmp不理解的看下:http://www.cnblogs.com/z1141000271/p ...

  9. java基础知识学习 内存相关

    Java 内存分配策略 静态存储区(方法区):主要存放静态数据.全局 static 数据和常量.这块内存在程序编译时就已经分配好,并且在程序整个运行期间都存在. 栈区 :当方法被执行时,方法体内的局部 ...

  10. (四)springmvc之获取servlet原生对象

    一.使用DI注入的方式 <a href="<%=request.getContextPath()%>/servletObj_1">DI注入的方式</a ...