转: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在窗体加载的时候初始化: ...
随机推荐
- 【Selenium】selenium中隐藏元素如何定位?
前言 面试题:selenium 中隐藏元素如何定位?这个是很多面试官喜欢问的一个题,如果单纯的定位的话,隐藏元素和普通不隐藏元素定位没啥区别,用正常定位方法就行了 但是吧~~~很多面试官自己都搞不清楚 ...
- linux 查看磁盘读写:iostat
iostat命令用来查看磁盘IO的读写情况,用法如下: 安装iostat命令 [root@mysql ~]# yum install -y sysstat [root@mysql ~]# iostat ...
- 报错解决——pytesseract.pytesseract.TesseractError: (1,’Error opening data file /usr/local/share/tessdata/eng.traineddata’)
解决方法:(原文地址http://stackoverflow.com/questions/14800730/tesseract-running-error) $ wget https://tesser ...
- Python 全栈开发三 python基础 条件与循环
一. 条件语句 python条件语句是根据一条或多条语句的执行结果的真假(True Or False)来决定代码块的执行. 而执行内容可以多行,以缩进来区分表示同一范围. 1.Python判断条件真假 ...
- tf实现LSTM时rnn.DropoutWrapper
转自:https://blog.csdn.net/abclhq2005/article/details/78683656 作者:abclhq2005 1.概念介绍 所谓dropout,就是指网络中每个 ...
- 【Java】-NO.14.Java.4.Java.1.001-【Java JUnit 5 】-
1.0.0 Summary Tittle:[Java]-NO.14.Java.4.Java.1.001-[Java JUnit 5 ]- Style:Java Series:JUnit Since:2 ...
- IntelliJ IDEA 17 本地LicenseServer激活
注意:此方法适用于Idea v2017.2.x 版本及以前版本. IntelliJ IDEA及破解包下载地址:百度网盘 密码:hlko 一.将IntelliJIDEALicenseServer.e ...
- linux 编译 'aclocal-1.14' is missing on your system
centos编译出现:类似情况: $tar -xvf libpcap-1.0.0.tar.gz $cd libpcap-1.0.0.tar.gz $./configure ...
- CentOS7 开放服务端口
CentOS 7 默认是firewall防火墙 如果你想让一个web服务可以被其它机子访问,就得开放这个服务的端口,不然就会被拦截 1. 开放端口命令 firewall-cmd --add-port= ...
- Mac为python2.7.10安装pip
首先下载get-pip.py https://bootstrap.pypa.io/get-pip.py alias python="/usr/bin/python2.7" pyth ...