一.文件服务接口

1.文件上传

2.文件传输(上传按钮)

3.文件传输停止

服务地址:

在客端添加服务器引用,从而实现客户端调用服务器的功能。

二.契约

服务契约[ServiceContract]:定义服务器这边的功能。

操作契约[OperationContract]:简单的说,就是指定服务器的功能方法是否可以被客户端调用,如果服务器方法未添加操作契约,则无法被客户端那边调用。

数据契约[DataContract]:和操作契约类似,只不过操作契约作用的对象是方法,数据契约作用的对象是基本类型数据,主要是数据成员或者属性。

回调契约CallbackContract:比如说,客户端完成一个功能,需要告诉服务端,这就需要向服务器回调一个消息,此时就需要定义一个回调契约(也就是一个接口)。

三.创建项目

首先,新建一个【WCF服务库】,然后在CRMServer中把系统自己添加的IService和Service删除,再添加一个【WCF服务】,最后添加一个窗体应用程序,作为客户端,如下:

服务器端生成的配置文件:

<?xml version="1.0" encoding="utf-8" ?>
<configuration> <appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
</appSettings>
<system.web>
<compilation debug="true" />
</system.web>
<!-- 部署服务库项目时,必须将配置文件的内容添加到
主机的 app.config 文件中。System.Configuration 不支持库的配置文件。-->
<system.serviceModel>
<services>
<service name="CRMServer.FileService">
<!--双工通信:客户端既可以访问服务器,服务器也可以回调信息给客户端-->
<endpoint address="" binding="wsDualHttpBinding" contract="CRMServer.IFileService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<!--元数据-->
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<!--这个节点的地址就是客户端那边要使用的-->
<add baseAddress="http://localhost:8733/Design_Time_Addresses/CRMServer/FileService/" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- 为避免泄漏元数据信息,
请在部署前将以下值设置为 false -->
<serviceMetadata httpGetEnabled="True" httpsGetEnabled="True"/>
<!-- 要接收故障异常详细信息以进行调试,
请将以下值设置为 true。在部署前设置为 false
以避免泄漏异常信息-->
<serviceDebug includeExceptionDetailInFaults="False" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel> </configuration>

上面更改为双工通信wsDualHttpBinding。

下面还要让客户端能够调用服务器的方法,所以需要添加服务引用,过程如下:

第一次添加服务引用,需要先运行一下服务端,将服务端设置为启动项,然后复制元数据地址

然后在客户端【引用】----》【添加服务引用】,拷贝元数据地址,转到

添加成功后就可以关闭。然后在项目结构中就可以看到服务引用:

查看服务引用,可以看到服务端那边所有的方法等,这样就可以实现客户端和服务器之间的交流了。

最后总的项目结构如下:

(1)FileModel.cs

封装文件数据模块

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks; namespace CRMServer
{
[Serializable]//允许以流的文件进行传输
[DataContract]//数据契约
public class FileModel
{
[DataMember]
/// <summary>
/// 文件标识
/// </summary>
public string FileId { get; set; } [DataMember]
/// <summary>
/// 文件名
/// </summary>
public string FileName { get; set; } [DataMember]
/// <summary>
/// 文件路径全名
/// </summary>
public string FullName { get; set; } [DataMember]
/// <summary>
/// 总文件大小,以字节为单位
/// </summary>
public long FileSize { get; set; } [DataMember]
/// <summary>
/// 每次传输的文件byte[]
/// </summary>
public byte[] FileBytes { get; set; } [DataMember]
/// <summary>
/// 每次传输的文件长度
/// </summary>
public int FileCount { get; set; } [DataMember]
/// <summary>
/// 文件扩展名
/// </summary>
public string FileExtName { get; set; }
}
}

(2)IFileService.cs文件

服务契约接口,该接口定义了服务的功能:上传文件,文件传输,取消文件传输。

服务契约回调接口ICallBack:客户端调用服务器方法后需要回调消息给服务器,这时就需要回调接口。  [ServiceContract(CallbackContract=typeof(ICallBack))]表示指定

回调接口为ICallBack。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text; namespace CRMServer
{
/// <summary>
/// 服务契约
/// </summary>
[ServiceContract(CallbackContract=typeof(ICallBack))]
public interface IFileService
{
/// <summary>
/// 上传文件
/// </summary>
[OperationContract(IsOneWay = true)]
void FileUpLoad(FileModel fileModel); /// <summary>
/// 文件传输
/// </summary>
[OperationContract(IsOneWay = true)]
void FileTransfer(FileModel fileModel); /// <summary>
/// 取消文件传输
/// </summary>
[OperationContract(IsOneWay = true)]
void FileStop(FileModel fileModel);
} /// <summary>
/// 回调契约接口,在客户端实现接口
/// </summary>
public interface ICallBack
{
/// <summary>
/// 回调已经传输的文件的大小
/// </summary>
/// <param name="length"></param>
[OperationContract(IsOneWay = true)]
void ToFileSize(long length);
} }

(3)FileService.cs

该类实现了IFileService接口,大文件传输的过程中,受带宽影响,一个大文件可能会分为多个部分上传,也就是会上传多次,这里建立一个字典,将每部分的文件流保存

下来,以备后面使用。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.IO; namespace CRMServer
{
public class FileService : IFileService
{
//保存很多个传输的文件,因为有的文件可能要多次才能传完
public Dictionary<string ,FileStream> dic=new Dictionary<string,FileStream>();
#region 实现IFileService接口
/// <summary>
/// 文件上传
/// </summary>
/// <param name="fileModel"></param>
public void FileUpLoad(FileModel fileModel)
{
try
{
//写到e盘的files文件夹下
FileStream filestream = new FileStream("e:/files/" + fileModel.FileName, FileMode.Create);
dic.Add(fileModel.FileId, filestream);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// 文件传输
/// </summary>
/// <param name="fileModel"></param>
public void FileTransfer(FileModel fileModel)
{
try
{
//添加到字典
FileStream filestream = dic[fileModel.FileId];
//写入到磁盘中
filestream.Write(fileModel.FileBytes, 0, fileModel.FileCount);
//写入成功,告诉客户端写入好了,回调
//OperationContext:操作上下文,全局文件对象,获取当前客户端和服务器的交流通道,通过这个通道调用服务器端的方法
ICallBack callback = OperationContext.Current.GetCallbackChannel<ICallBack>();
callback.ToFileSize(filestream.Length);
//假设10000kB,传了1000次,如果上传完了
if (filestream.Length >= fileModel.FileSize)
{
filestream.Close();
dic.Remove(fileModel.FileId);
}
}
catch (Exception)
{ } } /// <summary>
/// 文件关闭,取消传输
/// </summary>
/// <param name="fileModel"></param>
public void FileStop(FileModel fileModel)
{
try
{
FileStream filestream = dic[fileModel.FileId];
filestream.Close();
dic.Remove(fileModel.FileId);
}
catch (Exception)
{
throw;
}
}
#endregion
}
}

(4)FileServiceCallBack.cs

该类实现了服务器回调接口,在这里创建了一个委托变量,在ToFileSize方法中调用该委托,在FileUpLoadForm窗体注册委托,实现了在FileUpLoadForm中实现

ToFileSize方法,相当于在FileUpLoadForm中调用ToFileSize方法。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CRMClient.Service; namespace CRMClient
{
public class FileServiceCallBack:IFileServiceCallback
{
//定义一个委托事件(+=注册)
public event Action<long> ToFileSizeCallBack; #region 实现服务端的契约回调接口
/// <summary>
/// 回传传输的文件大小
/// 实际的实现方法将会和委托关联(注册)
/// </summary>
/// <param name="length">文件传输了多少</param>
public void ToFileSize(long length)
{
ToFileSizeCallBack(length);
}
#endregion
}
}

(5)MainForm窗体

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms; namespace CRMClient
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
} private void btn_Browser_Click(object sender, EventArgs e)
{
OpenFileDialog sfd = new OpenFileDialog();
if(sfd.ShowDialog()==DialogResult.OK)
{
txtFilePath.Text = sfd.FileName;
}
} private void bnt_UpLoad_Click(object sender, EventArgs e)
{
FileUpLoadForm fileUpLoadForm = new FileUpLoadForm(txtFilePath.Text);
//注册回调(实现FileUpLoadForm的回调),弹出一个文件上传成功的提示框。
fileUpLoadForm.CloseFormCallBack += fileUpLoadForm_CloseFormCallBack;
fileUpLoadForm.Show();
} void fileUpLoadForm_CloseFormCallBack(Service.FileModel obj)
{
MessageBox.Show(obj.FileName+"已上传完毕");
} }
}

(6)FileUpLoadForm窗体

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using CRMClient.Service;
using System.IO;
using System.ServiceModel; namespace CRMClient
{
public partial class FileUpLoadForm : Form
{
FileModel fileModel ; FileServiceClient fileServiceClient;//服务端和客户端的对接对象 BackgroundWorker bw = new BackgroundWorker();
public FileUpLoadForm()
{
InitializeComponent(); } /// <summary>
///
/// </summary>
/// <param name="filePath">文件路径</param>
public FileUpLoadForm(string filePath)
{
InitializeComponent();
//文件信息
FileInfo fileInfo = new FileInfo(filePath);
fileModel = new FileModel();
fileModel.FileId = Guid.NewGuid().ToString();//生成36位唯一标识
fileModel.FileName = fileInfo.Name;
fileModel.FileExtName = fileInfo.Extension;
fileModel.FileSize = fileInfo.Length;
fileModel.FullName = fileInfo.FullName;
//实现回调
FileServiceCallBack fileServiceCallBack=new FileServiceCallBack();
//注册回调,实现ToFileSize方法
fileServiceCallBack.ToFileSizeCallBack += fileServiceCallBack_ToFileSizeCallBack;
//客户端和服务端进行对接(调用服务端的方法),FileServiceClient其实就是服务端的 FileService,这里系统自动加了个Client后缀
fileServiceClient = new FileServiceClient(new InstanceContext(fileServiceCallBack));
//调用服务端的方法,上传文件
fileServiceClient.FileUpLoad(fileModel); } /// <summary>
/// 修改进度条信息(通知回调)
/// </summary>
/// <param name="positionSize">已经传输的文件大小</param>
void fileServiceCallBack_ToFileSizeCallBack(long positionSize)
{
//步长,进度条走一步相当于多少个字节
int stepSize = (int)(this.fileModel.FileSize / this.PrograssBar.Maximum);
//未传输完
if (positionSize > stepSize * PrograssBar.Value)
{
//进度条走了多少步
int result = this.PrograssBar.Value + 1;
long SizeMb = positionSize / 1024 / 1024;//传输了几MB
//如果线程未结束
if (this.bw.IsBusy)
{
//报告进度条进度
this.bw.ReportProgress(result, SizeMb);//手动触发bw_ProgressChanged方法
}
}
} private void FileUpLoadForm_Load(object sender, EventArgs e)
{
this.txtTotalSize.Text = (fileModel.FileSize / 1024 / 1024).ToString()+"MB";//将字节b单位转换为MB
this.PrograssBar.Minimum = 0;
this.PrograssBar.Maximum = 100;
this.bw.WorkerReportsProgress = true;//报告进度条的更新,不设置可能导致bw_ProgressChanged不触发
this.bw.WorkerSupportsCancellation = true;//是否支持取消线程
//多线程控件,三个方法
bw.DoWork += bw_DoWork;//开启线程触发
bw.ProgressChanged+=bw_ProgressChanged;//辅助线程执行的时候触发
bw.RunWorkerCompleted+=bw_RunWorkerCompleted;//任务结束的时候触发
//开始任务,执行bw_DoWork
this.bw.RunWorkerAsync();
}

//这里创建一个委托变量,用于在FileUpLoadForm文件上传成功后,可以向MainForm弹出一个提示文件已经上传成功的提示框。
public event Action<FileModel> CloseFormCallBack;
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if(this.PrograssBar.Value>=this.PrograssBar.Maximum)
{
this.Close();//关闭
//并通知文件上传窗体已经成功上传(回调),回调的实现在MainForm实现
CloseFormCallBack(fileModel);
}
} private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.PrograssBar.Value = e.ProgressPercentage > 100 ? 100 : e.ProgressPercentage;//显示进度条百分比
if (e.UserState!=null)
{
this.txtBytesSize.Text = e.UserState.ToString();//实时文件大小
}
} void bw_DoWork(object sender, DoWorkEventArgs e)
{
using(FileStream fileStream = new FileStream(fileModel.FullName,FileMode.Open))//打开文件
{
//当文件还未传完
while(fileStream.Position<fileStream.Length)
{
//如果终止上传
if (this.bw.CancellationPending)
{
this.bw.ReportProgress(0, null);//设置进度条为0
e.Cancel = true;//取消事件
return;
}
//循环上传,每次10kb
byte[] bytes=new byte[10240];
//循环读取文件内容,返回每次读取的实际大小
int count= fileStream.Read(bytes,0,bytes.Length);
fileModel.FileBytes = bytes;
fileModel.FileCount = count;
fileServiceClient.FileTransfer(fileModel);//循环发送
}
}
}
//取消上传
private void btn_Cancel_Click(object sender, EventArgs e)
{
this.bw.CancelAsync();//取消后,bw.CancellationPending=true
this.fileServiceClient.FileStop(this.fileModel);
this.Close();
}
}
}

上面的测试是在同一台电脑上进行的,也就是服务器和客户端是运行在同一台电脑上的,那如果要实现在两台电脑运行,一台运行服务器,一台运行客户端,可不可以

呢?

这里就要注意,由于WCF服务程序是不能独立运行的,他必须依托宿主才能运行,这个宿主可以是一个窗体程序或者控制台程序,也可以是IIS服务程序。

怎么实现客户端和服务器分别在不同的电脑也能通信?需要做一些配置:

一台电脑作为服务器,其端口是唯一的,即时两个不同的程序,其服务器的端口也是唯一的,操作系统默认为WCF服务分配的端口是8733,给我们提供了一个测试主机。

如果你改成其他的端口是使用不了WCF测试服务的,那么你如果想改成其他端口,并且还要能够实现不同电脑之间也可以通信测试,需要做以下配置:

第一步:设置出站入站端口

勾选程序,下一步--------》勾选所有程序,下一步--------》......

出站:别人访问你,设置出站端口,就可以让被人访问你的电脑

入站:你访问别人,设置入站端口,就可以让别人的电脑被你访问

第二步:勾选这三个,点击确定,就会安装相应的服务。

接下来怎么实现WCF程序的寄宿?

我们在刚刚的项目新建一个窗体项目CRMMain,并添加CRMServer的引用:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.ServiceModel;
using CRMServer; namespace CRMMain
{
public partial class MianServer : Form
{
public MianServer()
{
InitializeComponent();
}
ServiceHost host;
private void btn_Start_Click(object sender, EventArgs e)
{
Uri uri = new Uri("http://localhost:8733/Design_Time_Addresses/CRMServer/FileService/");
host = new ServiceHost(typeof(FileService),uri);//指定服务和服务地址
//指定元数据地址和对接接口
host.AddServiceEndpoint(typeof(IFileService),new WSDualHttpBinding(),"");
host.Open();//开启服务
MessageBox.Show("服务已经开启");
} private void btn_Close_Click(object sender, EventArgs e)
{
host.Close();
MessageBox.Show("服务已经关闭");
}
}
}

然后设置A=CRMMain为启动项,运行程序,点击开启服务,WCF服务就运行了。然后再运行客户端CRMClient就可以实现客户端和服务器分别以不同程序运行了。

源码下载链接:https://pan.baidu.com/s/1tp5a__5UCykAVGsvvYTMjQ     提取码:ts7s

WCF实现大文件上传的更多相关文章

  1. Nginx集群之WCF大文件上传及下载(支持6G传输)

    目录 1       大概思路... 1 2       Nginx集群之WCF大文件上传及下载... 1 3       BasicHttpBinding相关配置解析... 2 4       编写 ...

  2. 解决PHP大文件上传问题

    PHP大文件上传问题    今天负责创业计划大赛的老师问我作品上报系统上传不了大文件,我当时纳闷了,做的时候没限制上传文件的大小阿,怎么会传不了呢,自己亲自体验了番,果然不 行,想了好一会儿才有点眉目 ...

  3. 使用commons-fileupload包进行大文件上传注意事项

    项目中使用 commons-fileupload-1.2.1.jar 进行大文件上传. 测试了一把,效果很不错. 总结如下: 必须设置好上传文件的最大阀值 final long MAX_SIZE = ...

  4. 因用了NeatUpload大文件上传控件而导致Nonfile portion > 4194304 bytes错误的解决方法

    今天遇到一个问题,就是“NeatUpload大文件上传控件而导致Nonfile portion > 4194304 bytes错误”,百度后发现了一个解决方法,跟大家分享下: NeatUploa ...

  5. ASP.NET 大文件上传的简单处理

    在 ASP.NET 开发的过程中,文件上传往往使用自带的 FileUpload 控件,可是用过的人都知道,这个控件的局限性十分大,最大的问题就在于上传大文件时让开发者尤为的头疼,而且,上传时无法方便的 ...

  6. 【原创】用JAVA实现大文件上传及显示进度信息

    用JAVA实现大文件上传及显示进度信息 ---解析HTTP MultiPart协议 (本文提供全部源码下载,请访问 https://github.com/grayprince/UploadBigFil ...

  7. BootStrap Progressbar 实现大文件上传的进度条

    1.首先实现大文件上传,如果是几兆或者几十兆的文件就用基本的上传方式就可以了,但是如果是大文件上传的话最好是用分片上传的方式.我这里主要是使用在客户端进行分片读取到服务器段,然后保存,到了服务器段读取 ...

  8. 使用NeatUpload控件实现ASP.NET大文件上传

    使用NeatUpload控件实现ASP.NET大文件上传 一般10M以下的文件上传通过设置Web.Config,再用VS自带的FileUpload控件就可以了,但是如果要上传100M甚至1G的文件就不 ...

  9. Asp.net mvc 大文件上传 断点续传

    Asp.net mvc 大文件上传 断点续传 进度条   概述 项目中需要一个上传200M-500M的文件大小的功能,需要断点续传.上传性能稳定.突破asp.net上传限制.一开始看到51CTO上的这 ...

随机推荐

  1. vivo 基于 JaCoCo 的测试覆盖率设计与实践

    作者:vivo 互联网服务器团队- Xu Shen 本文主要介绍vivo内部研发平台使用JaCoCo实现测试覆盖率的实践,包括JaCoCo原理介绍以及在实践过程中遇到的新增代码覆盖率统计问题和频繁发布 ...

  2. 不想当Window的Dialog不是一个好Modal,弹窗翻身记

    弹窗是我们熟视无睹的一种交互方式,经常用到,但从没好好想过这种交互行为背后的意义... 弹窗是Windows的灵魂 Windows的灵魂是什么?当然是Window,当方便快捷的多窗口进入人们视野的时候 ...

  3. Linux安装Jenkins及配置svn使用

    目录 1. 下载 2. 创建文件夹 3. 安装 4. 修改端口,不用这步 5. 安装插件提速 6. 启动 7. 页面访问 8. 新建用户 9. 安装Subversion插件 10. 安装maven插件 ...

  4. kafka详解(一)--kafka是什么及怎么用

    kafka是什么 在回答这个问题之前,我们需要先了解另一个东西--event streaming. 什么是event streaming 我觉得,event streaming 是一个动态的概念,它描 ...

  5. dotnet 设计规范 · 抽象类

    X 不要定义 public 或 protected internal 访问的构造函数.默认 C# 语言不提供抽象类的公开构造函数方法. 如果一个构造函数定义为公开,只有在开发者需要创建这个类的实例的时 ...

  6. ak日记 831 dxm

    import sys from math import inf line = sys.stdin.readline().strip() vs = list(map(int, line.split()) ...

  7. 【android 逆向】arm if

    #include <stdio.h> void if1(int n){ //if else语句 if(n < 10){ printf("the number less th ...

  8. DOS文档

  9. Java基础(标识符,数据类型,数据转换,变量)

    注释 Java中的注释有3种: 单行注释 // 多行注释 /**/ 文档注释 /***/ 注释不会被执行,是给我们写代码的人看的 书写注释是一个非常好的习惯 标识符 Java所有的组成部分都需要名字, ...

  10. 图解 Kubernetes Service

    文章转载自:https://www.qikqiak.com/post/visually-explained-k8s-service/ 原文链接:https://medium.com/swlh/kube ...