转:wcf大文件传输解决之道(2)
此篇文章主要是基于http协议应用于大文件传输中的应用,现在我们先解析下wcf中编码器的定义,编码器实现了类的编码,并负责将Message内存中消息转变为网络发送的字节流或者字节缓冲区(对于发送方而言)。在接收方,编码会将一系列字节转变为内存中的消息
在wcf中有三个编码器
1、TextMessageEncodingBindingElement
文本消息编码器是所有的基于Http的绑定的默认编码器,并且是最关注互操作性的所有的自定义绑定的正确选择。即为请求/应答模式,此编码器读取和编码标准为SOAP1.1/SOAP1.2的文本消息,而不会对二进制数据进行任何特殊处理,如果消息的MessageVersion设置为None,则SOAP信封包装从输出中省略,只有正文内容进行序列化。
2、MtomMessageEncodingBindingElement
MTOM消息编码器是也是一个文本编码器,实现对二进制数据的特殊处理,默认情况下在任何标准绑定中都不会使用,也就是说需要我们自己定义(一般定义在wsHttpBinding中),因为它是一个严格按具体情况进行优化的实用工具,只有当二进制数据的量不超过某个阀值时,MTOM编码才具有优势,如果信息包含的二进制数据超过某个阀值,则这些数据会外部化消息信封之后的MIME部分
3、BinaryMessageEncodingBindingElement
二进制消息编码器是Net*绑定的默认编码器,当通信双方都基于WCF时,此编码器始终是正确的选择。二进制消息编码使用.NET二进制XML格式,该格式是XML信息集(Information Sets,Infosets)的Microsoft特定二进制表示法,与等效的xml1.0表示法相比产生的需求量通常较小,并将二进制数据编码为字节流
每个标准绑定都包括一个预配置编码器,因此默认情况下Net*前缀的绑定使用二进制编码(通过包括BinaryMessageEncodingBindingElement类),而BasicHttpBinding和WSHttpBing类则使用文本信息编码器(通过TextMessageEncodingBindingElement类)
通常,文本信息编码是要求互操作性的任意通信路径的最佳选择,也就是通用性比较高,而二进制消息编码则是其他任意通信路径的最佳选择。通常,对于当个消息而言,二进制编码生成的消息要小于文本编码,并且在通信会话期间消息大小会逐渐变得更小。于文本编码不同的是,二进制编码不需要对数据进行特殊处理(例如,使用Base64),当会字节表示为字节
如果数据无法分段,消息必须及时的方式传递或者当传输启动时数据为完全就绪,则应考虑启用流模式,而且只能对大型消息(带文本或者二进制)启用流模式
无法执行信息正文的数字签名,因为他们需要整个消息内容进行哈希算法。采用流模式的情况下,当构造和发送消息头时,内容尚未完全就绪,因此无法计算数字签名。
加密依赖于数字签名验证是否已经正确的重新构造数据
如果消息在传输过程中丢失,可靠的会话必须在客户端上缓冲已发送的消息以便可以重新传递,并且在将消息传递给服务实现之前必须在服务上保留信息以保证信息顺序。以备在未按顺序接受消息时可以按照正确的顺寻重新排列消息
下面我们通过一个上传文件的简单程序实现流文件的上传:
有几点我们需要注意:
1、在我们流文件上传的时候,需要定义文件的一些属性,这样我们就需要用消息契约代替数据契约方式
2、流文件上传的时候我们定义方法的时候只能保持一个参数,即消息契约。
第一步、新建文件消息契约
- using System;
- using System.Collections.Generic;
- using System.ServiceModel;
- using System.IO;
- namespace streamFileUp
- {
- /// <summary>
- /// 消息契约(定义与SOAP消息相对应的强类型)
- /// 因为我们用流传输,所以用消息契约代替传统的数据契约
- ///
- /// </summary>
- [MessageContract]
- public class FileWrapper
- {
- /// <summary>
- ///SOAP的消息头这里即为标记文件的路径
- /// </summary>
- [MessageHeader]
- public string FilePath;
- /// <summary>
- /// SOAP消息的内容,指定成员序列化正文中的元素
- /// </summary>
- [MessageBodyMember]
- public Stream FileData;
- }
- /// <summary>
- /// 返回结果
- /// </summary>
- [MessageContract]
- public class result
- {
- [MessageBodyMember]
- public bool returnresult;
- }
- }
第二步,定义服务契约
- using System;
- using System.ServiceModel;
- namespace streamFileUp
- {
- [ServiceContract]
- public interface IStreamed
- {
- /// <summary>
- /// 上传文件
- /// </summary>
- /// 1、支持数据流传输的绑定有:BasicHttpBding、NetTcpBinding和NetNamedPipeBinding
- /// 2、数据流类型必须是可序列化的sream或MemorySream
- /// 3、传递时消息体(Message Body)中不可能包含其他数据,即参数只能有一个streamFileUp.FileWrapper
- /// <param name="fileWrapper">信息载体</param>
- [OperationContract]
- result UploadFile(FileWrapper fileWrapper);
- }
- }
第三步、实现类,注释很全,就不解释了
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.ServiceModel;
- namespace streamFileUp
- {
- public class Streamed : IStreamed
- {
- /// <summary>
- /// 上传文件
- /// </summary>
- /// <param name="fileWrapper">streamFileUp.FileWrapper</param>
- public result UploadFile(FileWrapper fileWrapper)
- {
- try
- {
- var sourceSream = fileWrapper.FileData;
- var targetSream = new FileStream(fileWrapper.FilePath,
- FileMode.Create,
- FileAccess.Write,
- FileShare.None);
- var buffer = new byte[];
- var count = ;
- while ((count = sourceSream.Read(buffer, , buffer.Length)) > )
- {
- targetSream.Write(buffer, , count);
- }
- targetSream.Close();
- sourceSream.Close();
- }
- catch (Exception)
- {
- return new result { returnresult = false };
- }
- return new result { returnresult = true };
- }
- }
- }
第四步、我们采取自托管,新建服务
- using System;
- using System.ServiceModel;
- namespace streamFileUp
- {
- class Program
- {
- static void Main(string[] args)
- {
- using (ServiceHost host = new ServiceHost(typeof(Streamed)))
- {
- host.Opened += delegate
- {
- Console.WriteLine("服务已经启动");
- };
- host.Open();
- foreach (var endpoint in host.Description.Endpoints)
- {
- Console.WriteLine(endpoint.Address.ToString());
- }
- Console.ReadLine();
- }
- }
- }
- }
第五步,这里是我们的配置文件,晒一下,注释很详细,记住是配置流传输
- <?xml version="1.0"?>
- <configuration>
- <startup>
- <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
- </startup>
- <system.serviceModel>
- <services>
- <!--name 提供服务的类名-->
- <!--behaviorConfiguraron 指定相关的行为配置 -->
- <service name="streamFileUp.Streamed"
- behaviorConfiguration="MessageBehavior">
- <!--address - 服务地址-->
- <!--binding - 通信方式-->
- <!--contract - 服务契约-->
- <!--bindingConfiguration - 指定相关的绑定配置-->
- <endpoint
- address="Message/Streamed"
- binding="netTcpBinding"
- contract="streamFileUp.IStreamed"
- bindingConfiguration="StreamedBindingConfiguration" />
- <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
- <host>
- <baseAddresses>
- <add baseAddress="http://localhost:12345/Message/Streamed/"/>
- <add baseAddress="net.tcp://localhost:54321/"/>
- </baseAddresses>
- </host>
- </service>
- </services>
- <behaviors>
- <serviceBehaviors>
- <behavior name="MessageBehavior">
- <!--httpGetEnabled - 使用get方式提供服务-->
- <serviceMetadata httpGetEnabled="true" />
- <serviceDebug includeExceptionDetailInFaults="true"/>
- </behavior>
- </serviceBehaviors>
- </behaviors>
- <bindings>
- <netTcpBinding>
- <!--transferMode - 指示通道是使用流处理模式还是缓冲模式来传输请求和响应消息-->
- <!--maxReceivedMessageSize - 在采用此绑定配置的通道上可接收的最大消息大小(单位:字节)-->
- <!--receiveTimeout - 在传输引发异常之前可用于完成读取操作的时间间隔-->
- <binding
- name="StreamedBindingConfiguration"
- transferMode="Streamed"
- maxReceivedMessageSize=""
- receiveTimeout="00:10:00" />
- </netTcpBinding>
- </bindings>
- </system.serviceModel>
- </configuration>
到此我们的服务端已经建立成功了,跑一下试试...
可以看到我们公开了两个服务地址,一个是net.tcp、一个是http...其实这里面http地址是引用服务的基地址,其传递方式还是采用
tcp方式的,一会我们通过客户端验证来下我们的推测。
下面我们开始新建客户端,来连接该服务:
第一步、新建类库,引用该服务,这里面有几点注意,在流的传输下我们客户端生成代码的时候服务地址是不能用上面的net.tcp...
我们需要引用http:......基地址生成:
第三步、实现客户端上传文件:
- using System;
- using System.Collections.Generic;
- using System.ServiceModel;
- namespace client
- {
- class Program
- {
- static void Main(string[] args)
- {
- ///自定义绑定
- string strAddress = "net.tcp://localhost:54321/Message/Streamed";
- ChannelFactory<ServiceFileUp.IStreamed> factory = new ChannelFactory<ServiceFileUp.IStreamed>("NetTcpBinding_IStreamed", new EndpointAddress(strAddress));
- ServiceFileUp.IStreamed service = factory.CreateChannel();
- string filePath = @"G:\wcf学习测试案例\wcf大型数据传输\1.jpg";
- string newFilePath = @"G:\wcf学习测试案例\wcf大型数据传输\2.jpg";
- System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch(); //定义观察者
- watch.Start();
- ServiceFileUp.result returnResult = service.UploadFile(getSreamFromFile(filePath, newFilePath));
- watch.Stop();
- if (returnResult.returnresult)
- {
- Console.WriteLine("上传成功,上传时间为:" + watch.ElapsedMilliseconds);
- Console.ReadLine();
- }
- else
- {
- Console.WriteLine("上传失败");
- Console.ReadLine();
- }
- }
- /// <summary>
- /// 流数据上传文件
- /// </summary>
- /// <param name="file">源文件地址</param>
- public static ServiceFileUp.FileWrapper getSreamFromFile(string file, string newFilePath)
- {
- ServiceFileUp.FileWrapper myFileFw = null;
- try
- {
- var sr = new System.IO.FileStream(
- file, System.IO.FileMode.Open);
- ServiceFileUp.FileWrapper oneFW = new ServiceFileUp.FileWrapper()
- {
- FilePath = newFilePath,
- FileData = sr
- };
- myFileFw = oneFW;
- Console.WriteLine("文件大小为:"+sr.Length.ToString());
- //sr.Close();
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex.ToString());
- }
- return myFileFw;
- }
- }
- }
我们在G盘下面放了一个jpg文件,然后通过上传同样的在该目录下存入我们刚才上传的文件,这里我们顺便监听了一下上传该文件需要的时间
下面晒一下结果:
说明文件上传是成功的,下面我们变换一种方式,以上方式我们是自定义了net.tcp地址的方式实现了文件的上传,我们改变一下直接用
http方式看看文件上传时候会变慢:
看运行结果:
呵呵...同样能实现上传的功能,但是性能的落后于基地址赋值地址,当然本身client方式就是通过ChannelFactory工厂创建,性能有所耗损是必然的,这里同样告诫我们:
在大文件传输的时候我们最好是通过自定义地址实现客户端配置,当然我们现在只是传递了一个简单的图片,对于大文件的上传我们需要更多的配置和性能优化。
来自:http://www.cnblogs.com/zhijianliutang/archive/2011/11/28/2265989.html
转:wcf大文件传输解决之道(2)的更多相关文章
- 转:wcf大文件传输解决之道(1)
首先声明,文章思路源于MSDN中徐长龙老师的课程整理,加上自己的一些心得体会,先总结如下: 在应对与大文件传输的情况下,因为wcf默认采用的是缓存加载对象,也就是说将文件包一次性接受至缓存中,然后生成 ...
- WCF大文件传输【转】
http://www.cnblogs.com/happygx/archive/2013/10/29/3393973.html WCF大文件传输 WCF传输文件的时候可以设置每次文件的传输大小,如果是小 ...
- WCF大文件传输服务
由于项目需要,自己写一个基于WCF的大文件传输服务雏形.觉得有一定的参考价值,因此放在网上分享. 目前版本为v1.1特点如下: 1.文件传输端口为18650 2.上传和下载文件 3.支持获取文件传输状 ...
- WCF大文件传输
WCF传输文件的时候可以设置每次文件的传输大小,如果是小文件的时候,可以很方便的将文件传递到服务端,但是如果文件比较大的话,就不可取了 遇到大文件的话可以采取分段传输的方式进行文件传输 思路: 1.客 ...
- WCF 大文件传输配置
<bindings> <webHttpBinding> <!--这个是接收大数据加的,设置WCF服务器端数据接收上限参数,此处单位字节,故2147483647字节==2G ...
- Nginx集群之WCF大文件上传及下载(支持6G传输)
目录 1 大概思路... 1 2 Nginx集群之WCF大文件上传及下载... 1 3 BasicHttpBinding相关配置解析... 2 4 编写 ...
- 大文件传输 分片上传 上传id 分片号 授权给第三方上传
https://www.zhihu.com/question/39593108 作者:ZeroOne链接:https://www.zhihu.com/question/39593108/answer/ ...
- 【转】Windows2008上传大文件的解决方法(iis7解决上传大容量文件)
2008上传大文件的解决方法:http://wenku.it168.com/d_000091739.shtml 2003上传大文件的解决方法:http://tech.v01.cn/windowsxit ...
- 利用Socket进行大文件传输
分类: WINDOWS 最近接触到利用socket进行大文件传输的技术,有些心得,与大家分享.首先看看这个过程是怎么进行的(如下图): 所以,我们需要三个socket在窗体加载的时候初始化: ...
随机推荐
- XtraBackup之踩过的坑
xtrabackup相信目前使用已经非常广泛了,备份innodb表的首选工具,但是其中还是有点小坑,虽然发生的概率不大,但是我还是踩坑了.关于xtrabackup的详细参考请查阅官方文档http:// ...
- MySQL crash-safe replication【转载】
本文来自david大神的博客,innodb技术内幕的作者. http://insidemysql.blog.163.com/blog/static/202834042201385190333/ MyS ...
- 限制SSH用户访问Linux中指定的目录
限制SSH用户访问Linux中指定的目录 http://os.51cto.com/art/201703/534895.htm#topx http://www.cnblogs.com/lykyl/arc ...
- 线上MYSQL同步报错故障处理方法总结
前言 在发生故障切换后,经常遇到的问题就是同步报错,下面是最近收集的报错信息. 记录删除失败 在master上删除一条记录,而slave上找不到 Last_SQL_Error: Could not e ...
- Python3学习之路~5.7 Json & pickle 模块
用于序列化的两个模块 json,用于字符串 和 python数据类型间进行转换 pickle,用于python特有的类型 和 python的数据类型间进行转换 Json模块提供了四个功能:dumps. ...
- Linux系统启动和内核管理
Linux组成 由 kernel 和 rootfs 组成 单内核:(进程管理,内存管理,网络管理, 驱动程序,文件系统, 安全功能) /boot/vmlinuz-VERSION-release 辅助的 ...
- 011-docker-安装-rabbitmq-management:3.7.13
1.搜索镜像 docker search rabbitmq 2.拉取合适镜像 选择合适tag:https://hub.docker.com/,下载3.7.13 带web管理界面版本 docker pu ...
- C 表達式及返回值
以下程序的输出结果是__A____. #include<stdio.h> main() { ,j=; printf("%d,%d\n",++i,j--); } A., ...
- 使用autoconf与automake自动生成MakeFile文件
automake主要通过编辑Makefile.am来控制它的行为,下面就常用的三个Makefile.am配置做出说明. 1.1. autotools的工作原理 autotools最终是为了生成Make ...
- OpenShift Origin 基本命令
用户管理 $ oc login #登陆$ oc logout #注销$ oc login -u system:admin -n default #以系统管理身份登陆并指定项目$ oc login ht ...