WCF 支持流式传输文件---大文件的上传下载实例+源码

项目包含一个 WCF 服务器, 以及一个 Winform 客户端,客户端通过硬编码实现 Binding 调用

源码点击这里查看

1. 添加 WCF service UpLoadService,修改接口文件 IUpLoadService如下

[ServiceContract]
    public interface IUpLoadService
    {
        [OperationContract(Action = "UploadFile", IsOneWay = true)]
        void UploadFile(FileUploadMessage request);

        [OperationContract(Action = "DownLoadFile")]
        DownFileResult DownLoadFile(DownFileRequest fileName);
    }

其中,request model 和 result model 定义如下:

   [MessageContract]
    public class FileUploadMessage
    {
        [MessageHeader(MustUnderstand = true)]
        public string SavePath;

        [MessageHeader(MustUnderstand = true)]
        public string FileName;

        [MessageBodyMember(Order = 1)]
        public Stream FileData;

    }

    [MessageContract]
    public class DownFileRequest
    {
        [MessageHeader]
        public string FileName { get; set; }
    }

    [MessageContract]
    public class DownFileResult
    {
        [MessageHeader]
        public long FileSize { get; set; }
        [MessageHeader]
        public bool IsSuccess { get; set; }
        [MessageHeader]
        public string Message { get; set; }
        [MessageBodyMember]
        public Stream FileStream { get; set; }
    }
  • DownLoadFile 的入参仅有一个 fileName 参数,之所以要定义一个类存放,是因为 WCF 的限制:流式传递时消息体(Message Body)中不能包含其他数据。所以传入参数和传出参数都要分别定义自己的类型
  • OperationContract 的属性 IsOneWay 设为 true 时,入参和出参只能存在一个

2. 修改实现类 UpLoadService 如下

    public class UpLoadService : IUpLoadService
    {
        // my project is based on net 3.5. if not ,you can use Stream.CopyTo() method
        public static void CopyTo(Stream input, Stream output)
        {
            byte[] buffer = new byte[16 * 1024]; // Fairly arbitrary size
            int bytesRead;

            while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
            {
                output.Write(buffer, 0, bytesRead);
            }
        }

        public DownFileResult DownLoadFile(DownFileRequest fileRequest)
        {
            DownFileResult msg = new DownFileResult();
            string fileName = fileRequest.FileName;
            if (!File.Exists(fileName))
            {
                msg.IsSuccess = false;
                msg.FileSize = 0;
                msg.Message = "服务器不存在此文件";
                msg.FileStream = new MemoryStream();
                return msg;
            }

            Stream ms = new MemoryStream();
            FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
            CopyTo(fs, ms);
            ms.Position = 0;  //重要,不为0的话,客户端读取有问题
            
            msg.IsSuccess = true;
            msg.FileSize = ms.Length;
            msg.FileStream = ms;
            fs.Flush();
            fs.Close();
            return msg;
        }

        public void UploadFile(FileUploadMessage request)
        {
            string fileName = request.FileName;
            Stream sourceStream = request.FileData;
            FileStream targetStream = null;

            if (!sourceStream.CanRead)
            {
                throw new Exception("数据流不可读!");
            }

            string uploadFolder = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath;
            if (!Directory.Exists(uploadFolder))
            {
                Directory.CreateDirectory(uploadFolder);
            }

            string filePath = Path.Combine(uploadFolder, fileName);
            using (targetStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
            {
                //read from the input stream in 4K chunks
                //and save to output stream
                const int bufferLen = 4096;
                byte[] buffer = new byte[bufferLen];
                int count = 0;
                while ((count = sourceStream.Read(buffer, 0, bufferLen)) > 0)
                {
                    targetStream.Write(buffer, 0, count);
                }
                targetStream.Close();
                sourceStream.Close();
            }
        }
    }

3. 接下来是重点,需要配置 Web.config 文件支持流式传输大文件

  • system.web 节点下添加配置
<system.web>
    // 指定输入流缓冲阈值的限制,单位为 KB
    <httpRuntime maxRequestLength="2147483647" />
    // 其他的系统配置,比如 authentication 等
</system.web>
  • bindings 下新增配置
    <bindings>
      <basicHttpBinding>
        <binding name="FileTransferServicesBinding" maxReceivedMessageSize="9223372036854775807"
          messageEncoding="Mtom" transferMode="Streamed" sendTimeout="00:10:00" />
      </basicHttpBinding>
    </bindings>
  • service 配置改为如下
      <service behaviorConfiguration="WcfFileUploadService.UpLoadServiceBehavior" name="WcfFileUploadService.UpLoadService">
          <endpoint address="" binding="basicHttpBinding" bindingConfiguration="FileTransferServicesBinding" contract="WcfFileUploadService.IUpLoadService">
          </endpoint>
          <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
      </service>

4. 编写客户端代码,添加 service 引用,输入 running 的 wcf 地址,导入相关调用文件

  • 修改客户端 app.config 文件(与服务器配置一致)
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="FileTransferServicesBinding" maxReceivedMessageSize="9223372036854775807"
                 messageEncoding="Mtom" transferMode="Streamed" sendTimeout="00:10:00" />
      </basicHttpBinding>
    </bindings>
    <client>
      <endpoint address="http://localhost:62805/UpLoadService.svc"
                binding="basicHttpBinding" bindingConfiguration="FileTransferServicesBinding"
                contract="ServiceReference1.IUpLoadService" name="BasicHttpBinding_IUpLoadService" />
    </client>
  </system.serviceModel>
</configuration>
  • 上传调用代码示例
    (1) 使用 app.config 配置
            FileStream stream = File.OpenRead(fileName);
            IUpLoadService service = new UpLoadServiceClient();
            var req = new FileUploadMessage(fileName.Substring(0, fileName.LastIndexOf('.')), "", stream);
            service.UploadFile(req);
            stream.Close();

(2) 手动创建 binding

            FileStream stream = File.OpenRead(fileName);
            var req = new FileUploadMessage(fileName.Substring(fileName.LastIndexOf('\\')+1), "", stream);

            BasicHttpBinding binding = new BasicHttpBinding();
            binding.TransferMode = TransferMode.Streamed;
            binding.SendTimeout = new TimeSpan(0,0,10,0); // 设置十分钟超时
            binding.MessageEncoding = WSMessageEncoding.Mtom;
            binding.MaxReceivedMessageSize = 9223372036854775807;

            IUpLoadService channel = ChannelFactory<IUpLoadService>.CreateChannel(binding,
                new EndpointAddress("http://localhost:62805/UpLoadService.svc"));

            using (channel as IDisposable)
            {
                channel.UploadFile(req);
                stream.Close();
                this.Cursor = Cursors.Default;
                MessageBox.Show("文件上传到服务器成功", "上传WCF", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
  • 下载调用代码示例
    (1) 使用 app.config 配置
            DownFileRequest req = new DownFileRequest(fileName);
            IUpLoadService svc = new UpLoadServiceClient();
            var res = svc.DownLoadFile(req);
            if (res.IsSuccess)
            {
                using (var fileStream = File.Create(AppDomain.CurrentDomain.BaseDirectory+"\\"+fileName.Substring(fileName.LastIndexOf('\\')+1)) )
                {
                    CopyTo(res.FileStream, fileStream);
                }
            }

(2) 手动创建 binding

            DownFileRequest req = new DownFileRequest(fileName);
            BasicHttpBinding binding = new BasicHttpBinding();
            binding.TransferMode = TransferMode.Streamed;
            binding.MessageEncoding = WSMessageEncoding.Mtom;
            binding.MaxReceivedMessageSize = 9223372036854775807;

            IUpLoadService channel = ChannelFactory<IUpLoadService>.CreateChannel(binding,
                new EndpointAddress("http://localhost:62805/UpLoadService.svc"));

            using (channel as IDisposable)
            {
                var res = channel.DownLoadFile(req);
                if (res.IsSuccess)
                {
                    using (var fileStream = File.Create(AppDomain.CurrentDomain.BaseDirectory + "\\" + fileName.Substring(fileName.LastIndexOf('\\') + 1)))
                    {
                        CopyTo(res.FileStream, fileStream);
                    }
                }
                this.Cursor = Cursors.Default;
                MessageBox.Show(res.IsSuccess ? "文件下载成功!" : res.Message, "上传文件测试", MessageBoxButtons.OK,
                    res.IsSuccess ? MessageBoxIcon.Information : MessageBoxIcon.Error);
            }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,122评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,070评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,491评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,636评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,676评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,541评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,292评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,211评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,655评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,846评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,965评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,684评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,295评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,894评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,012评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,126评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,914评论 2 355

推荐阅读更多精彩内容