在上文我粗略的介绍了如何创建WCF Service,并在客户端调用生成的WCF Service来取得数据
本文将用一个上传程序来继续介绍下Silverlight中的WCF Service应用
问题:
调用WCF Service的时候,并没有一个DownloadProcessChanged之类的事件来反馈已经上传了多少
那么我们如何来实现在客户端实时展示当天已经上传了多少呢?
解决方案:
我们可以把文件分成很多块,逐次上传一小部分(比如2K,4K,8K等等)
1。首先我们还是按照Silverlight WCF通信(一)这个教程中所示的先创建个新的Silverlight工程
并添加进一个Silverlight-Enabled WCF Service(我取名为DownloadService,以前随便去的名字,懒得改了)
其里面含有的操作契约如下:
[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class DownloadService
{
[OperationContract]
public string UploadImg(string fileName, byte[] fileData, bool firstChunk, bool lastChunk)
{
if (!File.Exists(@HostingEnvironment.ApplicationPhysicalPath + @"/Uploads/" + fileName))
{
string tmpExtension = "_tmp";
string tempFileName = fileName + tmpExtension;
if (firstChunk)
{
fileName += tmpExtension;
if (File.Exists(@HostingEnvironment.ApplicationPhysicalPath + tempFileName))
{
File.Delete(@HostingEnvironment.ApplicationPhysicalPath + tempFileName);
}
}
FileStream fs = File.Open(@HostingEnvironment.ApplicationPhysicalPath + tempFileName, FileMode.Append);
fs.Write(fileData, 0, fileData.Length);
fs.Close();
if (lastChunk)
{
//Rename file to original file
File.Move(@HostingEnvironment.ApplicationPhysicalPath + tempFileName, @HostingEnvironment.ApplicationPhysicalPath + "/ClientBin/Uploads/" + fileName);
}
}
return "./Uploads/" + fileName;
}
}Upload这个操作契约的输入参数有文件名,文件的比特数组,firstChunk用来表示是否传输的是文件的第一个包
lastChunk代表文件的包是不是最后一个包
如果还不是最后一个包时,将传输过来的文件的文件扩展名加上后缀_tmp来存放
一旦lastChunk为true时,将该文件存为原文件名
2.实现客户端的界面
我们需要三个东西
一个用来调用选择上传文件对话框的Button
一个用来展示上传进度的进度条
一个用来展示结果的Image控件
(我设置为只能上传JPG或者PNG文件,结果返回一个上传后的图片的相对路径)
代码如下:
3.具体底层的控制代码
a.选择上传文件对话框的实现如下
public void uploadBtn_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "JPG Files|*.jpg|PNG Files|*.png";
ofd.Multiselect = false;
if ((bool)ofd.ShowDialog())
{
this.pb.Visibility = Visibility.Visible;
this.img.Opacity = 0;
dataSent = 0;
stream = ofd.File.OpenRead();
dataLength = stream.Length;
if (dataLength > 16384)
{
firstChunk = true;
lastChunk = false;
fileName = ofd.File.Name;
byte[] buffer = new byte[4 * 4096];
int read = stream.Read(buffer, 0, buffer.Length);
dataSent += read;
if (read != 0)
{
if (dataSent == dataLength)
lastChunk = true;
client.UploadImgAsync(fileName, buffer, firstChunk, lastChunk);
firstChunk = false;
}
}
else
{
MessageBox.Show("The upload file is too small!");
}
}
}我设置了每个包的大小是16K
也就是每次调用WCF Service最多只能传16K的东西
BTW:其中Client的定义为 private DownloadServiceRef.DownloadServiceClient client;
b.展示上传进度并显示最终上传结果
void client_UploadImgCompleted(object sender, ReadImageTest.DownloadServiceRef.UploadImgCompletedEventArgs e)
{
if (dataSent < dataLength)
{
byte[] buffer = new byte[4 * 4096];
int read = stream.Read(buffer, 0, buffer.Length);
dataSent += read;
this.pb.Value = (double)dataSent / dataLength;
if (read != 0)
{
if (dataSent == dataLength)
lastChunk = true;
client.UploadImgAsync(fileName, buffer, firstChunk, lastChunk);
firstChunk = false;
}
}
else
{
this.pb.Visibility = Visibility.Collapsed;
this.img.Opacity = 1;
this.img.Source = new BitmapImage(new Uri(e.Result, UriKind.RelativeOrAbsolute));
}
}每上传完一个包就更新下上传进度条
如果传送的包的大小已经等于文件大小时,隐藏进度条,并展示上传的图片
总结:
Silverlight目前对WCF的支持虽然只局限在普通的HttpBinding,但是功能也还算强大
本文只是小试牛刀,展示了个小小的图片上传工具实现,希望能起到抛砖引玉的作用