wcf是微软开发出的用户数据通信的app接口,在.net framework3.0中与wpf,wf一同集成,是.net框架的一部分。
具体请参见点击打开链接。
本文主要讲述了使用wcf服务契约来进行文件或者数据的服务器上传或者下载工作,使用的工具为vs2012(使用过程中发现了不少bug)。WCF是以契约来规定通信双方的沟通协议。双方必须定义相同的协议绑定才能实现沟通。本文使用基本的http支持,也就是basicHttpBinding。看具体步骤
1:搭建服务器,添加ServiceFile.svc文件
添加成功后,会在项目中增加一个该服务类对应的一个接口,IServiceFile,这个接口中定义的未实现方法就是将来要实现的服务代码,也是契约的定义声明,这里使用操作契约。
定义的接口代码:
// 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的接口名“IServiceFile”。
[ServiceContract]
public interface IServiceFile
{
///
/// 上传操作
///
///
///
[OperationContract]
CustomFileInfo UpLoadFileInfo(CustomFileInfo fileInfo);
///
/// 获取文件操作
///
///
///
[OperationContract]
CustomFileInfo GetFileInfo(string fileName);
}
///
/// 自定义文件属性类
///
[DataContract]
public class CustomFileInfo
{
///
/// 文件名称
///
[DataMember]
public string Name { get; set; }
///
/// 文件大小
///
[DataMember]
public long Length { get; set; }
///
/// 文件偏移量
///
[DataMember]
public long OffSet { get; set; }
///
/// 发送的字节
///
[DataMember]
public byte[] SendByte { get; set; }
}
ServiceInfo.svc实现:
// 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码、svc 和配置文件中的类名“ServiceFile”。
// 注意: 为了启动 WCF 测试客户端以测试此服务,请在解决方案资源管理器中选择 ServiceFile.svc 或 ServiceFile.svc.cs,然后开始调试。
public class ServiceFile : IServiceFile
{
public CustomFileInfo UpLoadFileInfo(CustomFileInfo fileInfo)
{
// 获取服务器文件上传路径
string fileUpLoadPath = System.Web.Hosting.HostingEnvironment.MapPath("~/UpLoadFile/");
// 如需指定新的文件夹,需要进行创建操作。
Console.WriteLine("1");
// 创建FileStream对象
FileStream fs = new FileStream(fileUpLoadPath + fileInfo.Name, FileMode.OpenOrCreate);
Console.WriteLine("2");
long offSet = fileInfo.OffSet;
// 使用提供的流创建BinaryWriter对象
var binaryWriter = new BinaryWriter(fs, Encoding.UTF8);
binaryWriter.Seek((int)offSet, SeekOrigin.Begin);
binaryWriter.Write(fileInfo.SendByte);
fileInfo.OffSet = fs.Length;
fileInfo.SendByte = null;
binaryWriter.Close();
fs.Close();
Console.WriteLine("2");
return fileInfo;
}
public CustomFileInfo GetFileInfo(string fileName)
{
string filePath = System.Web.Hosting.HostingEnvironment.MapPath("~/UpLoadFile/") + fileName;
if (File.Exists(filePath))
{
var fs = new FileStream(filePath, FileMode.OpenOrCreate);
CustomFileInfo fileInfo = new CustomFileInfo
{
Name = fileName,
OffSet = fs.Length,
};
fs.Close();
return fileInfo;
}
return null;
}
服务端协议:
并成功部署到IIS(本文IIS版本为8.0),创建过程中创建存放上传文件的文件夹。
2:创建客户端项目看界面:
其实就三个操作,选择文件,上传文件,下载文件。
1:选择文件,最简单的弹出框选择文件,使用OpenFileDialog。
2:上传文件,使用的是独立线程。
- 定义客户端协议
客户端:
上传代码:
string filePath = this.txtFileName.Text; BackgroundWorker worker = new BackgroundWorker(); worker.DoWork += worker_DoWork; worker.WorkerReportsProgress = true; worker.ProgressChanged += worker_ProgressChanged; worker.RunWorkerCompleted += worker_RunWorkerCompleted; worker.RunWorkerAsync(filePath);
#region 独立线程 ///
/// 进度条更改 /// /// /// void worker_ProgressChanged(object sender, ProgressChangedEventArgs e) { if (progressBar.InvokeRequired) { progressBar.Invoke((ThreadStart)delegate { progressBar.Value = e.ProgressPercentage; }); } else { progressBar.Value = e.ProgressPercentage; progressBar.Update(); } } ////// 执行操作完成 /// /// /// void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if ((bool)e.Result == false) { } else { if (this.InvokeRequired) { this.Invoke((ThreadStart)delegate { MessageBox.Show("上传成功"); }); } else { MessageBox.Show("上传成功"); } } if (progressBar.InvokeRequired) { progressBar.Invoke((ThreadStart)delegate { progressBar.Value = 0; }); } else { progressBar.Value = 0; } if (txtFileName.InvokeRequired) { txtFileName.Invoke((ThreadStart)delegate { this.txtFileName.Clear(); }); } else { this.txtFileName.Clear(); } if (this.btnUpload.InvokeRequired) { this.btnUpload.Invoke((ThreadStart)delegate { this.btnUpload.Enabled = true; }); } else { this.btnUpload.Enabled = true; } } void worker_DoWork(object sender, DoWorkEventArgs e) { string path = e.Argument.ToString(); System.IO.FileInfo fileInfoIO = new System.IO.FileInfo(path); // 要上传的文件地址 FileStream fs = File.OpenRead(fileInfoIO.FullName); // 实例化服务客户的 ServiceFileClient client = new ServiceFileClient(); try { int maxSiz = 1024 * 100; // 根据文件名获取服务器上的文件 CustomFileInfo file = client.GetFileInfo(fileInfoIO.Name); if (file == null) { file = new CustomFileInfo(); file.OffSet = 0; } file.Name = fileInfoIO.Name; file.Length = fs.Length; if (file.Length == file.OffSet) //如果文件的长度等于文件的偏移量,说明文件已经上传完成 { MessageBox.Show("该文件已存在"); e.Result = false; // 设置异步操作结果为false } else { while (file.Length != file.OffSet) { file.SendByte = new byte[file.Length - file.OffSet <= maxSiz ? file.Length - file.OffSet : maxSiz]; //设置传递的数据的大小 fs.Position = file.OffSet; //设置本地文件数据的读取位置 fs.Read(file.SendByte, 0, file.SendByte.Length);//把数据写入到file.Data中 file = client.UpLoadFileInfo(file); //上传 //int percent = (int)((double)file.OffSet / (double)((long)file.Length)) * 100; int percent = (int)(((double)file.OffSet / (double)((long)file.Length)) * 100); (sender as BackgroundWorker).ReportProgress(percent); } // 移动文件到临时目录(此部分创建可以使用sqlserver数据库代替) string address = string.Format(@"{0}\{1}", Helper.ServerFolderPath, file.Name); fileInfoIO.CopyTo(address, true); LoadUpLoadFile(); // 上传成功重新加载文件 e.Result = true; } } catch (Exception ex) { MessageBox.Show(ex.Message); } finally { fs.Close(); fs.Dispose(); client.Close(); client.Abort(); } } ////// 选择文件对话框 /// /// /// private void button2_Click(object sender, EventArgs e) { OpenFileDialog dialog = new OpenFileDialog(); dialog.InitialDirectory = "C:"; //dialog.Multiselect = true; //dialog.Filter = ""; if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.Cancel) { return; } this.txtFileName.Text = dialog.FileName; } #endregion
3:下载文件 使用WebRequest,WebResponse对象获取Internet相应数据流大小,使用FileStream保存文件。下载使用了多线程。
下载代码:
Thread downLoadThread = new Thread(new ThreadStart(DownLoadFile)); downLoadThread.IsBackground = true; downLoadThread.SetApartmentState(ApartmentState.STA); downLoadThread.Name = "downLoadThreade"; downLoadThread.Start(); #region 下载多线程 private void DownLoadFile() { // 测试使用 //string fileFullpath = "http://localhost:29700//UpLoadFile"; string fileFullpath = Helper.ServerFilePath; // 获得DataGridView选中行 DataGridViewRow selectedRow = this.dgvFiles.SelectedRows[0]; string fileName = selectedRow.Cells[0].Value.ToString(); // 文件名称 SaveFileDialog sfd = new SaveFileDialog(); sfd.InitialDirectory = "C:"; sfd.FileName = fileName; if (sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { fileFullpath += string.Format("//{0}", fileName); WebRequest request = WebRequest.Create(fileFullpath); WebResponse fs = null; try { fs = request.GetResponse(); } catch (Exception ex) { MessageBox.Show(ex.Message); } long contentLength = fs.ContentLength; if (pbDownLoad.InvokeRequired) { pbDownLoad.Invoke((ThreadStart)delegate { pbDownLoad.Maximum = (int)contentLength; }); } Stream st = fs.GetResponseStream(); try { byte[] byteLength = new byte[contentLength]; int allByte = (int)contentLength; int startByte = 0; while (contentLength > 0) { int downByte = st.Read(byteLength, startByte, allByte); if (downByte == 0) { break; } startByte += downByte; // 计算下载进度 //int percent = (int)(((double)startByte / ((long)(double)allByte)) * 100); //(sender as BackgroundWorker).ReportProgress(percent); if (pbDownLoad.InvokeRequired) { pbDownLoad.Invoke((ThreadStart)delegate { pbDownLoad.Value = startByte; }); } allByte -= downByte; } // 保存路径 string downLoadPath = sfd.FileName; FileStream stream = new FileStream(downLoadPath, FileMode.OpenOrCreate, FileAccess.Write); stream.Write(byteLength, 0, byteLength.Length); stream.Close(); fs.Close(); } catch (Exception ex) { MessageBox.Show(ex.Message); } finally { st.Close(); st.Dispose(); request.Abort(); Thread.Sleep(500); MessageBox.Show("下载成功"); if (pbDownLoad.InvokeRequired) { pbDownLoad.Invoke((ThreadStart)delegate { pbDownLoad.Maximum = 0; }); } } } #endregion
4:Helper类代码
上传成功图:public static class Helper { public static string ServerFilePath { get { // 获取客户端中定义的服务器地址 ClientSection clientSection = ConfigurationManager.GetSection("system.serviceModel/client") as ClientSection; if (clientSection == null) { return ""; } ChannelEndpointElementCollection collection = clientSection.Endpoints; Uri uri = collection[0].Address; return "http://" + uri.Authority + "//UpLoadFile"; } } ///
/// 服务器存放文件的文件夹地址 /// public static string ServerFolderPath { //get { return @"E:\WCFCESHI1\UpLoadFile"; } get { return @"C:\Test"; } } }经过测试,本地局域网可以上传<=2G的文件。应该可以满足需求。
下载成功图:
下载的时候对于选择文件名称有严格的要求,如果你的文件包含特殊字符或者其它wcf服务解析不了的字符,就会出现 404 not found 错误。
总结:继续努力工作,努力学习。
源码地址:WCF