使用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. 后ARM时代,嵌入式工程师的自我修养

    1 嵌入式学习的一些概念理解误区 很多嵌入式初学者认为,学嵌入式,就是学习ARM,就是学习开发板.买一块开发板,然后在上面“移植”u-boot.Linux内核,再使用busybox制作一个根文件系统, ...

  2. FAQ and discussed with adam

    1. About permuter index. url:  https://www.youtube.com/watch?v=j789k96g5aQ&list=PL0ZVw5-GryEkGAQ ...

  3. oracle中row_number()的用法

    公司系统升级的时候需要数据迁移,遇到一个问题:新表的数据结构和旧表异构,旧表是流水号,新表是联合主键(业务号码+业务号码序号) 最后发现用窗口函数 row_number() + partition b ...

  4. REDIS scan与sunionstore合并多集合数据

    实际业务场景: 现需求要将多个KEY的set集合数据合并到一个总集合中,思路:通过scan分批扫描满足条件的KEY,然后用sunionstore分批合并. 注意闭坑:此种解决方案只适用于待合并的集合K ...

  5. 下载GDB调试工具peda

    命令: 1.git clone https://github.com/longld/peda.git ~/peda 2.echo "source ~/peda/peda.py" & ...

  6. k8s-部署策略

    在Kubernetes中有几种不同的方式发布应用,所以为了让应用在升级期间依然平稳提供服务,选择一个正确的发布策略就非常重要了. 选择正确的部署策略是要依赖于我们的业务需求的,下面我们列出了一些可能会 ...

  7. java——多态例题

    class A { public String show(D obj) { return ("A and D"); } public String show(A obj) { re ...

  8. vsftp多个用户公享同一个文件,但是权限不同

    如标题:vsftp多个用户公享同一个文件,但是权限不同.如何做到呢.首先创建多个用户,并且指定同一个目录

  9. java 获取json字符串中key对应的值

    用到了Gson的JsonParser maven项目引入 <dependency> <groupId>com.google.code.gson</groupId> ...

  10. C# Enum操作

    枚举定义 /// <summary> /// 节点类型 /// </summary> public enum NodeTypeEnum { 企业 = , 人员 = , 人员地址 ...