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