项目包含一个 WCF 服务器, 以及一个 Winform 客户端,客户端通过硬编码实现 Binding 调用
1. 添加 WCF service UpLoadService
,修改接口文件 IUpLoadService
如下
[ServiceContract] public interface IUpLoadService { [OperationContract(Action = "UploadFile", IsOneWay = true)] void UploadFile(FileUploadMessage request);
<span class="token punctuation">[</span><span class="token function">OperationContract</span><span class="token punctuation">(</span>Action <span class="token operator">=</span> <span class="token string">"DownLoadFile"</span><span class="token punctuation">)</span><span class="token punctuation">]</span> DownFileResult <span class="token function">DownLoadFile</span><span class="token punctuation">(</span>DownFileRequest fileName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
其中,request model 和 result model 定义如下:
[MessageContract] public class FileUploadMessage { [MessageHeader(MustUnderstand = true)] public string SavePath;
<span class="token punctuation">[</span><span class="token function">MessageHeader</span><span class="token punctuation">(</span>MustUnderstand <span class="token operator">=</span> true<span class="token punctuation">)</span><span class="token punctuation">]</span> public string FileName<span class="token punctuation">;</span> <span class="token punctuation">[</span><span class="token function">MessageBodyMember</span><span class="token punctuation">(</span>Order <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">]</span> public Stream FileData<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">[</span>MessageContract<span class="token punctuation">]</span> public class DownFileRequest <span class="token punctuation">{</span> <span class="token punctuation">[</span>MessageHeader<span class="token punctuation">]</span> public string FileName <span class="token punctuation">{</span> get<span class="token punctuation">;</span> set<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">[</span>MessageContract<span class="token punctuation">]</span> public class DownFileResult <span class="token punctuation">{</span> <span class="token punctuation">[</span>MessageHeader<span class="token punctuation">]</span> public <span class="token keyword">long</span> FileSize <span class="token punctuation">{</span> get<span class="token punctuation">;</span> set<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">[</span>MessageHeader<span class="token punctuation">]</span> public bool IsSuccess <span class="token punctuation">{</span> get<span class="token punctuation">;</span> set<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">[</span>MessageHeader<span class="token punctuation">]</span> public string Message <span class="token punctuation">{</span> get<span class="token punctuation">;</span> set<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">[</span>MessageBodyMember<span class="token punctuation">]</span> public Stream FileStream <span class="token punctuation">{</span> get<span class="token punctuation">;</span> set<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span>
-
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;
<span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>bytesRead <span class="token operator">=</span> input<span class="token punctuation">.</span><span class="token function">Read</span><span class="token punctuation">(</span>buffer<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> buffer<span class="token punctuation">.</span>Length<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> output<span class="token punctuation">.</span><span class="token function">Write</span><span class="token punctuation">(</span>buffer<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> bytesRead<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> public DownFileResult <span class="token function">DownLoadFile</span><span class="token punctuation">(</span>DownFileRequest fileRequest<span class="token punctuation">)</span> <span class="token punctuation">{</span> DownFileResult msg <span class="token operator">=</span> new <span class="token function">DownFileResult</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> string fileName <span class="token operator">=</span> fileRequest<span class="token punctuation">.</span>FileName<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>File<span class="token punctuation">.</span><span class="token function">Exists</span><span class="token punctuation">(</span>fileName<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> msg<span class="token punctuation">.</span>IsSuccess <span class="token operator">=</span> false<span class="token punctuation">;</span> msg<span class="token punctuation">.</span>FileSize <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> msg<span class="token punctuation">.</span>Message <span class="token operator">=</span> <span class="token string">"服务器不存在此文件"</span><span class="token punctuation">;</span> msg<span class="token punctuation">.</span>FileStream <span class="token operator">=</span> new <span class="token function">MemoryStream</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> msg<span class="token punctuation">;</span> <span class="token punctuation">}</span> Stream ms <span class="token operator">=</span> new <span class="token function">MemoryStream</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> FileStream fs <span class="token operator">=</span> new <span class="token function">FileStream</span><span class="token punctuation">(</span>fileName<span class="token punctuation">,</span> FileMode<span class="token punctuation">.</span>Open<span class="token punctuation">,</span> FileAccess<span class="token punctuation">.</span>Read<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">CopyTo</span><span class="token punctuation">(</span>fs<span class="token punctuation">,</span> ms<span class="token punctuation">)</span><span class="token punctuation">;</span> ms<span class="token punctuation">.</span>Position <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token comment">//重要,不为0的话,客户端读取有问题</span> msg<span class="token punctuation">.</span>IsSuccess <span class="token operator">=</span> true<span class="token punctuation">;</span> msg<span class="token punctuation">.</span>FileSize <span class="token operator">=</span> ms<span class="token punctuation">.</span>Length<span class="token punctuation">;</span> msg<span class="token punctuation">.</span>FileStream <span class="token operator">=</span> ms<span class="token punctuation">;</span> fs<span class="token punctuation">.</span><span class="token function">Flush</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> fs<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> msg<span class="token punctuation">;</span> <span class="token punctuation">}</span> public <span class="token keyword">void</span> <span class="token function">UploadFile</span><span class="token punctuation">(</span>FileUploadMessage request<span class="token punctuation">)</span> <span class="token punctuation">{</span> string fileName <span class="token operator">=</span> request<span class="token punctuation">.</span>FileName<span class="token punctuation">;</span> Stream sourceStream <span class="token operator">=</span> request<span class="token punctuation">.</span>FileData<span class="token punctuation">;</span> FileStream targetStream <span class="token operator">=</span> null<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>sourceStream<span class="token punctuation">.</span>CanRead<span class="token punctuation">)</span> <span class="token punctuation">{</span> throw new <span class="token function">Exception</span><span class="token punctuation">(</span><span class="token string">"数据流不可读!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> string uploadFolder <span class="token operator">=</span> System<span class="token punctuation">.</span>Web<span class="token punctuation">.</span>Hosting<span class="token punctuation">.</span>HostingEnvironment<span class="token punctuation">.</span>ApplicationPhysicalPath<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>Directory<span class="token punctuation">.</span><span class="token function">Exists</span><span class="token punctuation">(</span>uploadFolder<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> Directory<span class="token punctuation">.</span><span class="token function">CreateDirectory</span><span class="token punctuation">(</span>uploadFolder<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> string filePath <span class="token operator">=</span> Path<span class="token punctuation">.</span><span class="token function">Combine</span><span class="token punctuation">(</span>uploadFolder<span class="token punctuation">,</span> fileName<span class="token punctuation">)</span><span class="token punctuation">;</span> using <span class="token punctuation">(</span>targetStream <span class="token operator">=</span> new <span class="token function">FileStream</span><span class="token punctuation">(</span>filePath<span class="token punctuation">,</span> FileMode<span class="token punctuation">.</span>Create<span class="token punctuation">,</span> FileAccess<span class="token punctuation">.</span>Write<span class="token punctuation">,</span> FileShare<span class="token punctuation">.</span>None<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">//read from the input stream in 4K chunks</span> <span class="token comment">//and save to output stream</span> <span class="token keyword">const</span> <span class="token keyword">int</span> bufferLen <span class="token operator">=</span> <span class="token number">4096</span><span class="token punctuation">;</span> byte<span class="token punctuation">[</span><span class="token punctuation">]</span> buffer <span class="token operator">=</span> new byte<span class="token punctuation">[</span>bufferLen<span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">int</span> count <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>count <span class="token operator">=</span> sourceStream<span class="token punctuation">.</span><span class="token function">Read</span><span class="token punctuation">(</span>buffer<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> bufferLen<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> targetStream<span class="token punctuation">.</span><span class="token function">Write</span><span class="token punctuation">(</span>buffer<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> count<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> targetStream<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> sourceStream<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span>
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 <span class="token operator">=</span> new <span class="token function">BasicHttpBinding</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> binding<span class="token punctuation">.</span>TransferMode <span class="token operator">=</span> TransferMode<span class="token punctuation">.</span>Streamed<span class="token punctuation">;</span> binding<span class="token punctuation">.</span>SendTimeout <span class="token operator">=</span> new <span class="token function">TimeSpan</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">10</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 设置十分钟超时</span> binding<span class="token punctuation">.</span>MessageEncoding <span class="token operator">=</span> WSMessageEncoding<span class="token punctuation">.</span>Mtom<span class="token punctuation">;</span> binding<span class="token punctuation">.</span>MaxReceivedMessageSize <span class="token operator">=</span> <span class="token number">9223372036854775807</span><span class="token punctuation">;</span> IUpLoadService channel <span class="token operator">=</span> ChannelFactory<span class="token operator"><</span>IUpLoadService<span class="token operator">></span><span class="token punctuation">.</span><span class="token function">CreateChannel</span><span class="token punctuation">(</span>binding<span class="token punctuation">,</span> new <span class="token function">EndpointAddress</span><span class="token punctuation">(</span><span class="token string">"http://localhost:62805/UpLoadService.svc"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> using <span class="token punctuation">(</span>channel as IDisposable<span class="token punctuation">)</span> <span class="token punctuation">{</span> channel<span class="token punctuation">.</span><span class="token function">UploadFile</span><span class="token punctuation">(</span>req<span class="token punctuation">)</span><span class="token punctuation">;</span> stream<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> this<span class="token punctuation">.</span>Cursor <span class="token operator">=</span> Cursors<span class="token punctuation">.</span>Default<span class="token punctuation">;</span> MessageBox<span class="token punctuation">.</span><span class="token function">Show</span><span class="token punctuation">(</span><span class="token string">"文件上传到服务器成功"</span><span class="token punctuation">,</span> <span class="token string">"上传WCF"</span><span class="token punctuation">,</span> MessageBoxButtons<span class="token punctuation">.</span>OK<span class="token punctuation">,</span> MessageBoxIcon<span class="token punctuation">.</span>Information<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
- 下载调用代码示例
(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 <span class="token operator">=</span> ChannelFactory<span class="token operator"><</span>IUpLoadService<span class="token operator">></span><span class="token punctuation">.</span><span class="token function">CreateChannel</span><span class="token punctuation">(</span>binding<span class="token punctuation">,</span> new <span class="token function">EndpointAddress</span><span class="token punctuation">(</span><span class="token string">"http://localhost:62805/UpLoadService.svc"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> using <span class="token punctuation">(</span>channel as IDisposable<span class="token punctuation">)</span> <span class="token punctuation">{</span> var res <span class="token operator">=</span> channel<span class="token punctuation">.</span><span class="token function">DownLoadFile</span><span class="token punctuation">(</span>req<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>res<span class="token punctuation">.</span>IsSuccess<span class="token punctuation">)</span> <span class="token punctuation">{</span> using <span class="token punctuation">(</span>var fileStream <span class="token operator">=</span> File<span class="token punctuation">.</span><span class="token function">Create</span><span class="token punctuation">(</span>AppDomain<span class="token punctuation">.</span>CurrentDomain<span class="token punctuation">.</span>BaseDirectory <span class="token operator">+</span> <span class="token string">"\\"</span> <span class="token operator">+</span> fileName<span class="token punctuation">.</span><span class="token function">Substring</span><span class="token punctuation">(</span>fileName<span class="token punctuation">.</span><span class="token function">LastIndexOf</span><span class="token punctuation">(</span><span class="token string">'\\'</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">CopyTo</span><span class="token punctuation">(</span>res<span class="token punctuation">.</span>FileStream<span class="token punctuation">,</span> fileStream<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> this<span class="token punctuation">.</span>Cursor <span class="token operator">=</span> Cursors<span class="token punctuation">.</span>Default<span class="token punctuation">;</span> MessageBox<span class="token punctuation">.</span><span class="token function">Show</span><span class="token punctuation">(</span>res<span class="token punctuation">.</span>IsSuccess <span class="token operator">?</span> <span class="token string">"文件下载成功!"</span> <span class="token operator">:</span> res<span class="token punctuation">.</span>Message<span class="token punctuation">,</span> <span class="token string">"上传文件测试"</span><span class="token punctuation">,</span> MessageBoxButtons<span class="token punctuation">.</span>OK<span class="token punctuation">,</span> res<span class="token punctuation">.</span>IsSuccess <span class="token operator">?</span> MessageBoxIcon<span class="token punctuation">.</span>Information <span class="token operator">:</span> MessageBoxIcon<span class="token punctuation">.</span>Error<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>