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

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

作者: 俊果果 | 来源:发表于2018-08-07 23:20 被阅读414次

    项目包含一个 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);
                }
    

    相关文章

      网友评论

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

          本文链接:https://www.haomeiwen.com/subject/gouzvftx.html