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