您的位置:首页 > 其它

【分享】 在silverlight中使用wcf上传文件并实时显示进度

2012-02-25 16:35 507 查看
公司一个项目要用到文件上件, Google后了解到上传一般有三种方式:WebClient、WebService和WCF。
最开始用WebClient.OpenWrite实现了文件上传,但弄不明白怎么才能得到上传进度,只好放弃了。

用WebService还不如直接WCF,参考http://www.cnblogs.com/blackcore/archive/2009/11/21/1607823.html后,自己做了一个,最终的效果图如下:



简单实现步骤如下:

一、在sl.Web项目中添加“启用了 Silverlight 的 WCF 服务”。WCF服务的内容十分简单,只需一个功能:接收字节并储存即可。

代码如下:

[ServiceContract(Namespace = "")]
[SilverlightFaultBehavior]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class UploadFileWCFService
{
[OperationContract]
public void DoWork()
{
// 在此处添加操作实现
return;
}

/// <summary>
/// 开始上传。需要在WCF服务启动文件夹下建立UploadFiles、TempUploadFolder文件夹。
/// UploadFiles储存上传文件夹
/// TempUploadFolder为上传临时文件夹
/// </summary>
/// <param name="FullPath">保存路径及文件名</param>
/// <param name="FileContext">文件内容</param>
/// <param name="Overwrite">是否覆盖</param>
[OperationContract]
public void BeginUpload(string FullPath, byte[] FileContext, bool Overwrite)
{
try
{
if (!FullPath.StartsWith("\\"))
{
FullPath = "\\" + FullPath;
}
string defaultFolder = System.Web.Hosting.HostingEnvironment.MapPath("~/UploadFiles");
string tempUploadFolder = System.Web.Hosting.HostingEnvironment.MapPath("~/TempUploadFolder");
string SaveFolder = Path.GetDirectoryName(FullPath);
string Filename = Path.GetFileName(FullPath);
string BlockString = Path.GetExtension(Filename);
string[] block = BlockString.Split('-');
if (block.Length != 2)
{
throw new Exception("系统不支持此操作");
}
int FilesIndex = Convert.ToInt32(block[0].Replace(".", ""));
int FilesCount = Convert.ToInt32(block[1]);
string tempFolder = tempUploadFolder + "\\" + Path.GetFileNameWithoutExtension(FullPath); //上传到临时文件夹

Directory.CreateDirectory(tempFolder);
if (!Directory.Exists(defaultFolder + "\\" + SaveFolder))
{
Directory.CreateDirectory(defaultFolder + "\\" + SaveFolder);
}
FileMode mode = FileMode.Create;
if (!Overwrite)
{
mode = FileMode.Append;
}
using (FileStream fs = new FileStream(tempFolder + "\\" + Filename, mode, FileAccess.Write))
{
fs.Write(FileContext, 0, FileContext.Length);
}
if (FilesIndex == FilesCount - 1)//是否最后一块文件,如是则合并文件块得到完整文件
{
string UploadFile = defaultFolder + "\\" + SaveFolder + "\\" + Path.GetFileNameWithoutExtension(FullPath);
string[] files = Directory.GetFiles(tempFolder);
using (FileStream sFile = new FileStream(UploadFile, FileMode.Create, FileAccess.Write))
{
foreach (string file in files)
{
using (FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read))
{
byte[] context = new byte[fs.Length];
fs.Read(context, 0, context.Length);
sFile.Write(context, 0, context.Length);
}
File.Delete(file);
}
}
Directory.Delete(tempFolder);
}
}
catch (Exception)
{
throw;
}
return;
}
}


二、为让WCF服务支持大文件需对sl.Web项目中的web.config进行配置,增加MaxArrayLength属性的设置:

<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<customBinding>
<binding name="AutoloanMobileManager.Web.Service.UploadFileWCFService.customBinding0">
<binaryMessageEncoding>
<readerQuotas maxStringContentLength="2147483647" maxArrayLength="2147483647"
maxBytesPerRead="2147483647" />
</binaryMessageEncoding>
<httpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" />
</binding>
</customBinding>
</bindings>
<services>
<service name="AutoloanMobileManager.Web.Service.UploadFileWCFService">
<endpoint address="" binding="customBinding" bindingConfiguration="AutoloanMobileManager.Web.Service.UploadFileWCFService.customBinding0"
contract="AutoloanMobileManager.Web.Service.UploadFileWCFService" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true" />


三、在sl项目中添加上传类,实现上传方法。

利用WCF异步调用的特点,将要上传的文件分成N个文件块依次上传,由此获得准确的上传进度。代码如下:

public class WcfUploadFileService : INotifyPropertyChanged
{

public class FileList : INotifyPropertyChanged
{
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}

private int m_BufferSize = 4096;
private FileInfo m_FileList;
private UploadStatusType m_UploadStauts;
private long m_BytesSent;
private long m_BytesSentCount;
private int m_SentPercentage;
private string m_SavePath;
private ObservableCollection<UploadLog> m_LOG = new ObservableCollection<UploadLog>();

public enum UploadStatusType
{
Waitting, Uploading, Completed, Failed, Pause
}

/// <summary>
/// 每次上传的字节数。应在给File赋值前设定。
/// </summary>
public int BufferSize
{
get
{
return m_BufferSize;
}
set
{
m_BufferSize = value;
}
}

public class UploadLog
{
public UploadLog(DateTime time, string context, object tag)
{
LogTime = time;
LogContext = context;
Tag = tag;
}

public DateTime LogTime { get; set; }
public string LogContext { get; set; }
public object Tag { get; set; }
}

/// <summary>
/// 上传日志
/// </summary>
public ObservableCollection<UploadLog> LOG
{
get
{
return m_LOG;
}
set
{
m_LOG = value;
NotifyPropertyChanged("LOG");
}
}

private void AddLog(UploadLog log)
{
LOG.Add(log);
}

/// <summary>
/// 等待传输的字节数据列表。设计this.File时自动赋值,按BufferSize读取至Byte[]等待上传。
/// </summary>
public List<Byte[]> FileContext { get; set; }

/// <summary>
/// 要上传的文件
/// </summary>
public FileInfo File
{
get { return m_FileList; }
set
{
m_FileList = value;
Stream soureFile = null;
UploadStatus = UploadStatusType.Waitting;
try
{
soureFile = value.OpenRead();
}
catch (Exception)
{
UploadStatus = UploadStatusType.Failed;
AddLog(new UploadLog(DateTime.Now, "无法读取文件", null));
}
long BytesCount = soureFile.Length;
long BytesRead = 0;
if (FileContext == null)
{
FileContext = new List<byte[]>();
}
else
{
FileContext.Clear();
}
long ReadSize = BufferSize;
while (BytesRead < BytesCount)
{
if (BytesRead + BufferSize > BytesCount) //调整最后一个文件块的大小
{
ReadSize = BytesCount - BytesRead;
}
byte[] bytes = new byte[ReadSize];
BytesRead += soureFile.Read(bytes, 0, bytes.Length);
FileContext.Add(bytes);
}
soureFile.Close();
soureFile.Dispose();
NotifyPropertyChanged("File");
}
}

/// <summary>
/// 保存路径
/// </summary>
public string SavePath
{
get { return m_SavePath; }
set { m_SavePath = value; NotifyPropertyChanged("SavePath"); }
}

/// <summary>
/// 上传状态
/// </summary>
public UploadStatusType UploadStatus
{
get { return m_UploadStauts; }
set { m_UploadStauts = value; NotifyPropertyChanged("UploadStatus"); }
}
/// <summary>
/// 本次上传字节
/// </summary>
public long BytesSent
{
get { return m_BytesSent; }
set { m_BytesSent = value; NotifyPropertyChanged("BytesSent"); }
}
/// <summary>
/// 已上传字节
/// </summary>
public long BytesSentCount
{
get { return m_BytesSentCount; }
set { m_BytesSentCount = value; NotifyPropertyChanged("BytesSentCount"); }
}
/// <summary>
/// 已上传比率
/// </summary>
public int SentPercentage
{
get { return m_SentPercentage; }
set { m_SentPercentage = value; NotifyPropertyChanged("SentPercentage"); }
}

#region INotifyPropertyChanged 成员

public event PropertyChangedEventHandler PropertyChanged;

#endregion
}

UploadFileWCFServiceClient uploadClient = new UploadFileWCFServiceClient();

public WcfUploadFileService()
{
uploadClient.BeginUploadCompleted += new EventHandler<AsyncCompletedEventArgs>(uploadClient_BeginUploadCompleted);
}

void uploadClient_BeginUploadCompleted(object sender, AsyncCompletedEventArgs e)
{
//通过CurrentFileIndex 、CurrentFileContextIndex控制下一个上传的块
bool beginNew = false; //是开始一个新文件或续传
if (e.Error != null)
{
//如错误,放弃当前文件,传下一文件
Files[CurrentFileIndex].UploadStatus = FileList.UploadStatusType.Failed;
Files[CurrentFileIndex].LOG.Add(new FileList.UploadLog(DateTime.Now, e.Error.Message, null));
CurrentFileIndex++;
CurrentFileContextIndex = 0;
beginNew = true;
}
else
{
Files[CurrentFileIndex].BytesSent = Files[CurrentFileIndex].FileContext[CurrentFileContextIndex].Length;
Files[CurrentFileIndex].BytesSentCount += Files[CurrentFileIndex].BytesSent;
Files[CurrentFileIndex].SentPercentage = Convert.ToInt32((double)Files[CurrentFileIndex].BytesSentCount / (double)Files[CurrentFileIndex].File.Length * 100);
//计算下一个上传的文件号和块号
if (CurrentFileIndex < Files.Count)
{
if (CurrentFileContextIndex < Files[CurrentFileIndex].FileContext.Count - 1)
{
CurrentFileContextIndex++;
}
else
{
Files[CurrentFileIndex].UploadStatus = FileList.UploadStatusType.Completed;
CurrentFileIndex++;
CurrentFileContextIndex = 0;
beginNew = true;
}
}
}
if (CurrentFileIndex < Files.Count)
{
Files[CurrentFileIndex].UploadStatus = FileList.UploadStatusType.Uploading;
uploadClient.BeginUploadAsync(Files[CurrentFileIndex].SavePath + "//"
+ Files[CurrentFileIndex].File.Name + "."
+ CurrentFileContextIndex.ToString().PadLeft(9,'0')
+ "-" + Files[CurrentFileIndex].FileContext.Count,
Files[CurrentFileIndex].FileContext[CurrentFileContextIndex], beginNew);
}
else
{
Files[CurrentFileIndex - 1].UploadStatus = FileList.UploadStatusType.Completed;
}
}

private ObservableCollection<FileList> m_Files = new ObservableCollection<FileList>();

/// <summary>
/// 准备上传的文件列表
/// </summary>
public ObservableCollection<FileList> Files
{
get
{
return m_Files;
}
set
{
m_Files = value;
NotifyPropertyChanged("Files");
}
}

/// <summary>
/// 当前上传的文件索引
/// </summary>
private int CurrentFileIndex;
/// <summary>
/// 当前上传的文件块索引
/// </summary>
private int CurrentFileContextIndex;

/// <summary>
/// 开始上传。按BufferSize将文件分块,块命名规则为 filename.blockid-blockcount,服务器接收完最后一块后合并成源文件。
/// </summary>
public void BeginUpload()
{
if (Files.Count > 0)
{
CurrentFileIndex = 0;
CurrentFileContextIndex = 0;
Files[CurrentFileIndex].UploadStatus = FileList.UploadStatusType.Uploading;
uploadClient.BeginUploadAsync(Files[CurrentFileIndex].SavePath + "//"
+ Files[CurrentFileIndex].File.Name + "."
+ CurrentFileContextIndex.ToString().PadLeft(9, '0')
+ "-" + Files[CurrentFileIndex].FileContext.Count,
Files[CurrentFileIndex].FileContext[CurrentFileContextIndex], true);
}
}

#region INotifyPropertyChanged 成员

public event PropertyChangedEventHandler PropertyChanged;

private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}

#endregion


四、制作简单的上传控件,代码如下:

<UserControl x:Class="AutoloanMobileManager.Controls.UploadFileControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ProgressBar x:Name="uploadProgress" Value="{Binding SentPercentage}" Height="20"
Background="Yellow" Width="400" Grid.Column="1"/>
<StackPanel Orientation="Horizontal" Grid.Column="1" HorizontalAlignment="Right">
<TextBlock Text="{Binding BytesSentCount}" VerticalAlignment="Center"/>
<TextBlock Text="/" VerticalAlignment="Center"/>
<TextBlock Text="{Binding File.Length}" VerticalAlignment="Center"/>
</StackPanel>
<TextBlock Text="{Binding File.Name}" Grid.Column="1" HorizontalAlignment="Left"
VerticalAlignment="Center" Foreground="DarkBlue"
Margin="8,0"/>
<TextBlock Text="{Binding UploadStatus}" Grid.Column="0" VerticalAlignment="Center"/>
</Grid>
</UserControl>


五、新建一个Page,前台界面如下:

注意添加第四步中定义的UploadFileControl的引用

<navigation:Page x:Class="AutoloanMobileManager.Pages.UploadFilePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:control="clr-namespace:AutoloanMobileManager.Controls"
mc:Ignorable="d"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
d:DesignWidth="640" d:DesignHeight="480"
Title="UploadFilePage Page">
<StackPanel>
<ListBox Margin="10" x:Name="list_uploadlist">
<ListBox.ItemTemplate>
<DataTemplate>
<control:UploadFileControl/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Content="选择文件" Click="Button_Click" Width="80" HorizontalAlignment="Left" Margin="5"/>
</StackPanel>
</navigation:Page>


后台代码:

public partial class UploadFilePage : Page
{
WcfUploadFileService upload = new WcfUploadFileService();

public UploadFilePage()
{
InitializeComponent();
list_uploadlist.ItemsSource = upload.Files;
}

// 当用户导航到此页面时执行。
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}

private void Button_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog file = new OpenFileDialog();
file.Multiselect = true;
bool? result = file.ShowDialog();
if (result.HasValue)
{
if (result.Value)
{
upload.Files.Clear();
foreach (System.IO.FileInfo f in file.Files)
{
WcfUploadFileService.FileList filelist = new WcfUploadFileService.FileList();
filelist.SavePath = "20120222";
filelist.BufferSize = 1024 * 32;
filelist.File = f;
upload.Files.Add(filelist);
}

upload.BeginUpload();
}
}
}

}


HOHO运行一下看效果怎么样吧!

项目源码 我接触siverlight和wcf不久,望大牛指教啊。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: