【转载】wcf 利用二进制流 异步上传文件
3 MTOM消息优化传输
MTOM 全称Message Transmission Optimization Mechanism,即消息传输优化机制。它提出的模型适用于大量数据的交互情况。针对Base64编码情况带来的开销提出的解决方案。当数据量小的时候,SOAP依然使用XML进行消息的传递。
但是在大量数据情况下,如果数据依然进行Base64编码,会带来33%的额外开销,这样的情况对于大量数据交换的情况是无法容忍的。MTOM 就是针对SOAP 消息传输的基础上提出的改进办法。对于大量数据的传递,不会进行进行Base64编码,而是直接以附件的二进制原始数据的形式封装在SOAP消息的MIME 部分,进行传输。SOAP 消息通过指向随其发送的 MIME 部分来引用二进制内容,另外包括SOAP基本的XML 数据,这些还是Base64编码。因为此模型与简单邮件协议SMTP 模型基本一致。
MTOM通过简化大量数据的编码过程,从而提高数据的处理效率。因为SOAP消息等必要的信息,MTOM 也有一些必要的开销。MTOM仅在二进制数据元素的大小超过大约 1 KB 时,才能体现出其优势
如何在WCF中使用MTOM
其实很简单,只要设置Binding的MessageEncoding为MTOM即可。无语了吧
<bindings> <wsHttpBinding> <binding name="mybinding" messageEncoding="Mtom">binding> wsHttpBinding> bindings>
WCF中MTOM编码方式例子
此示例演示如何将消息传输优化机制 (MTOM) 消息编码与 WSHttpBinding 一起使用。MTOM 是一种机制,用来以原始字节形式传输包含 SOAP 消息的较大二进制附件,从而使所传输的消息较小。
默认情况下,WSHttpBinding 以正常文本 XML 形式发送和接收消息。若要允许发送和接收 MTOM 消息,请在绑定的配置中设置 messageEncoding 属性 (Attribute)(如下面的示例代码中所示),或者使用 MessageEncoding 属性 (Property) 直接在绑定中进行设置。服务或客户端现在可以发送和接收 MTOM 消息了。
<binding name="WSHttpBinding_IUpload" messageEncoding="Mtom"/>
</wsHttpBinding>
MTOM 编码器可以优化字节和流的数组。在下面的示例中,操作使用 Stream 参数,因此可以进行优化。
public interface IUpload
{
[OperationContract]
int Upload(Stream data);
}
为该示例选择的协定会将二进制数据传输到服务,并将上载的字节数作为返回值接收。在安装服务之后运行客户端时,服务会显示数字 1000,这表示收到了全部 1000 个字节。剩下的输出列出了在各种负载情况下经过优化和未经优化的消息大小。
1000
Text encoding with a 100 byte payload: 433
MTOM encoding with a 100 byte payload: 912
Text encoding with a 1000 byte payload: 1633
MTOM encoding with a 1000 byte payload: 2080
Text encoding with a 10000 byte payload: 13633
MTOM encoding with a 10000 byte payload: 11080
Text encoding with a 100000 byte payload: 133633
MTOM encoding with a 100000 byte payload: 101080
Text encoding with a 1000000 byte payload: 1333633
MTOM encoding with a 1000000 byte payload: 1001080
Press <ENTER> to terminate client.
--------------------------------------------------------------
[OperationContract]
public void DoUpload(string fileName, byte[] context, bool append)
{
//上传目录
string folder = System.Web.Hosting.HostingEnvironment.MapPath("~/upload");
if (!System.IO.Directory.Exists(folder))
{
//如果上传目录不存在则新建一个
System.IO.Directory.CreateDirectory(folder);
}
//文件读写模式
FileMode m = FileMode.Create;
if (append)
{
//如果参数为true则表示续传,以追加模式操作文件
m = FileMode.Append;
}
//写文件操作
using (FileStream fs = new FileStream(folder + @"\" + fileName, m, FileAccess.Write))
{
fs.Write(context, 0, context.Length);
}
return;
}
void uploader_DoUploadCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
if (e.Error == null)
{
//上一个包上传成功
uploadfile file = e.UserState as uploadfile;
//修改已上传大小(显示作用)
file.sent += file.context[0].Length / 1000;
//删除已上传内容
file.context.RemoveAt(0);
bt.Content = file.sent.ToString() + "/" + file.size.ToString();
{
bt.Content = "upload";
MessageBox.Show("upload OK");
}
else
{
//如果上传内容不是空,则继续剩余下一段内容上传
(sender as ServiceReference1.uploadServiceClient).DoUploadAsync(file.name, file.context[0], true, file);
}
}
}
}
public class uploadfile
{
//文件名
public string name { get; set; }
//文件大小
public double size { get; set; }
//已上传
public double sent { get; set; }
//上传内容
public List<byte[]> context { get; set; }
}
//点击事件
void bt_Click(object sender, RoutedEventArgs e)
{
//选择本地文件对话框
OpenFileDialog d = new OpenFileDialog();
//文件过滤
d.Filter = "(*.*)|*.*";
//只能选单个文件
d.Multiselect = false;
//选择完成
if (d.ShowDialog() == true)
{
//文件信消
FileInfo f = d.File;
//新实例化一个文件从uploadfile类
uploadfile file = new uploadfile();
//文件名
file.name = f.Name;
//文件流内容
Stream s = f.OpenRead();
//文件大小(/1000是为了方便查看,成为k为单位)
file.size = s.Length / 1000;
//实例file的内容
file.context = new List<byte[]>();
//这里读取指定大小的文件流内容到file.context准备上传
int b;
while (s.Position > -1 && s.Position < s.Length)
{
if (s.Length - s.Position >= 3000)
{
b = 3000;
}
else
{
b = (int)(s.Length - s.Position);
}
byte[] filebyte = new byte[b];
s.Read(filebyte, 0, b);
file.context.Add(filebyte);
}
s.Close();
//实例化wcf客户端
ServiceReference1.uploadServiceClient uploader = new uploadFile.ServiceReference1.uploadServiceClient();
//注册DoUpload完成事件
uploader.DoUploadCompleted += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(uploader_DoUploadCompleted);
//开始第一个包的上传
uploader.DoUploadAsync(file.name, file.context[0], false, file);
}
}
邮箱:steven9801@163.com
QQ: 48039387