美文网首页
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