之前,数据文件是通过ClickOnce一起和应用程序部署的。ClickOnce会自动判断数据文件是否更新了,然后来确定是否下载这些数据文件。而在Silverlight中,如果把数据文件作为Content打包在XAP文件中,那么每次下载(或更新)XAP都会下载这些数据文件。解决方法有两种:把数据文件单独放在一个程序集中,把程序集设置为On-Demand下载或用Application Library Caching机制来缓存;或者,自定义一个数据文件下载和升级的机制。
我采用了第二种方式,即自定义了数据文件的下载更新机制,下面就详细介绍。
首先了解一下参考资料《Silverlight: Downloading Zipped files with the WebClient (stand Silverlight 2 beta 1)》
http://www.galasoft.ch/mydotnet/articles/article-2008032301.html
接着,说明一下我的思路:
整个流程就是这样,由于我没有安装Visio 2010,就不画流程图了。相关代码如下:
public partial class MainPage : UserControl { Application app = Application.Current; public MainPage() { InitializeComponent(); //在OfB中才用代码去更新 if (app.InstallState==InstallState.Installed) app.CheckAndDownloadUpdateCompleted += new CheckAndDownloadUpdateCompletedEventHandler(app_CheckAndDownloadUpdateCompleted); } void app_CheckAndDownloadUpdateCompleted(object sender, CheckAndDownloadUpdateCompletedEventArgs e) { if (e.UpdateAvailable) { MessageBox.Show("An application update has been downloaded. " + "Restart the application to run the new version."); } else if (e.Error != null && e.Error is PlatformNotSupportedException) { MessageBox.Show("An application update is available, " + "but it requires a new version of Silverlight. " + "Visit the application home page to upgrade."); } else { MessageBox.Show("There is no update available."); this.IsEnabled = true; InstallOrUpdateDB(); } } private void InstallOrUpdateDB() { if (ProgramBase.ShouldIncreaseQuota) { button1.Visibility = System.Windows.Visibility.Visible; } else { this.CheckDBManifest(); } } private void UserControl_Loaded(object sender, RoutedEventArgs e) { // Do not load your data at design time. // if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this)) // { // //Load your data here and assign the result to the CollectionViewSource. // System.Windows.Data.CollectionViewSource myCollectionViewSource = (System.Windows.Data.CollectionViewSource)this.Resources["Resource Key for CollectionViewSource"]; // myCollectionViewSource.Source = your data // } if (app.InstallState == InstallState.Installed) { if (NetworkInterface.GetIsNetworkAvailable()) this.IsEnabled = false; app.CheckAndDownloadUpdateAsync(); } else InstallOrUpdateDB(); } private void CheckDBManifest() { if (!NetworkInterface.GetIsNetworkAvailable()) return; WebClient wc = new WebClient(); wc.DownloadStringCompleted += new DownloadStringCompletedEventHandler(wc_DownloadStringCompleted); wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler(wc_DownloadProgressChanged); label1.Content = "DB updated checking ..."; var url = ProgramBase.LCADBWebPath + "manifest.xml"; wc.DownloadStringAsync(new Uri(url)); } void wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) { progressBar1.Value= e.ProgressPercentage; } List<LCADBZipFile> zipfilesWeb; List<LCADBZipFile> zipfilesLocal; int checkfileIndex = 0; void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) { label1.Content = "Finished DB updated checking."; var lcadb_zipfiles_manifest = "lcadb_zipfiles_manifest.xml"; XDocument docWeb = XDocument.Parse(e.Result); zipfilesWeb = (from m in docWeb.Descendants("file") select new LCADBZipFile { Name = m.Attribute("name").Value, Created = DateTime.Parse(m.Attribute("created").Value) }).ToList(); using (var store = ProgramBase.OpenStore()) { if (store.FileExists(lcadb_zipfiles_manifest)) { var s = store.OpenFile(lcadb_zipfiles_manifest, FileMode.Open); XDocument docLocal = XDocument.Load(s); zipfilesLocal = (from m in docLocal.Descendants("file") select new LCADBZipFile { Name = m.Attribute("name").Value, Created = DateTime.Parse(m.Attribute("created").Value) }).ToList(); s.Close(); } else { zipfilesLocal = new List<LCADBZipFile>(); } docWeb.Save(store.OpenFile(lcadb_zipfiles_manifest, FileMode.Create)); } //检查哪些文件需要下载 foreach (var item in zipfilesWeb) { var localfile = zipfilesLocal.FirstOrDefault(o => o.Name == item.Name); if (localfile == null || localfile.Created < item.Created) { qDownloadFiles.Enqueue(item.Name); } } DownloadAndExtract(); } private Queue<string> qDownloadFiles = new Queue<string>(); private void DownloadAndExtract() { if (qDownloadFiles.Count > 0) { var name = qDownloadFiles.Dequeue(); //下载并解压 WebClient wc2 = new WebClient(); wc2.OpenReadCompleted += new OpenReadCompletedEventHandler(wc2_OpenReadCompleted); wc2.DownloadProgressChanged += new DownloadProgressChangedEventHandler(wc2_DownloadProgressChanged); //显示下载进度 label1.Content = "Downloading " + name; wc2.OpenReadAsync(new Uri(ProgramBase.LCADBWebPath + name)); } else { //显示主页面 label1.Content = "Downloaded!"; } } void wc2_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) { progressBar1.Value = e.ProgressPercentage; } void wc2_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e) { FastZip zip = new FastZip(); string path=IO.Path.Combine(ProgramBase.LCADBXmlPath,zipfilesWeb[checkfileIndex].Name.Split('_')[0]); using (var store=ProgramBase.OpenStore()) { if (!store.DirectoryExists(path)) store.CreateDirectory(path); zip.ExtractZip(store, e.Result, path,FastZip.Overwrite.Always,null,"","",true); } //继续检查下载下一个 DownloadAndExtract(); } class LCADBZipFile { public string Name { get; set; } public DateTime Created { get; set; } } private void button1_Click(object sender, RoutedEventArgs e) { ProgramBase.CreateDBDirectory(); this.CheckDBManifest(); } }
SharpZipLib的扩展代码如下:
#region 在独立存储区中解压zip文件 //developed by zyg, ITKE public void ExtractZip(IsolatedStorageFile store, Stream stream, string targetDirectory, Overwrite overwrite, ConfirmOverwriteDelegate confirmDelegate, string fileFilter, string directoryFilter, bool restoreDateTime) { if ((overwrite == Overwrite.Prompt) && (confirmDelegate == null)) { throw new ArgumentNullException("confirmDelegate"); } continueRunning_ = true; overwrite_ = overwrite; confirmDelegate_ = confirmDelegate; targetDirectory_ = targetDirectory; fileFilter_ = new NameFilter(fileFilter); directoryFilter_ = new NameFilter(directoryFilter); restoreDateTimeOnExtract_ = restoreDateTime; using (zipFile_ = new ZipFile(stream)) { #if !NETCF_1_0 if (password_ != null) { zipFile_.Password = password_; } #endif System.Collections.IEnumerator enumerator = zipFile_.GetEnumerator(); while (continueRunning_ && enumerator.MoveNext()) { ZipEntry entry = (ZipEntry)enumerator.Current; if (entry.IsFile) { if (directoryFilter_.IsMatch(Path.GetDirectoryName(entry.Name)) && fileFilter_.IsMatch(entry.Name)) { ExtractEntryInIsolatedStorage(store, entry); } } else if (entry.IsDirectory) { if (directoryFilter_.IsMatch(entry.Name) && CreateEmptyDirectories) { ExtractEntryInIsolatedStorage(store, entry); } } else { // Do nothing for volume labels etc... } } } } private void ExtractEntryInIsolatedStorage(IsolatedStorageFile store, ZipEntry entry) { bool doExtraction = false; string nameText = entry.Name; if (entry.IsFile) { // TODO: Translate invalid names allowing extraction still. doExtraction = NameIsValid(nameText) && entry.IsCompressionMethodSupported(); } else if (entry.IsDirectory) { doExtraction = NameIsValid(nameText); } // TODO: Fire delegate were compression method not supported, or name is invalid? string dirName = null; string targetName = null; if (doExtraction) { // Handle invalid entry names by chopping of path root. if (Path.IsPathRooted(nameText)) { string workName = Path.GetPathRoot(nameText); nameText = nameText.Substring(workName.Length); } if (nameText.Length > 0) { targetName = Path.Combine(targetDirectory_, nameText); if (entry.IsDirectory) { dirName = targetName; } else { //dirName = Path.GetDirectoryName(Path.GetFullPath(targetName)); dirName = targetDirectory_; } } else { doExtraction = false; } } if (doExtraction && !store.DirectoryExists(dirName)) { if (!entry.IsDirectory || CreateEmptyDirectories) { try { //Directory.CreateDirectory(dirName); store.CreateDirectory(dirName); } catch (Exception ex) { doExtraction = false; if (events_ != null) { if (entry.IsDirectory) { continueRunning_ = events_.OnDirectoryFailure(targetName, ex); } else { continueRunning_ = events_.OnFileFailure(targetName, ex); } } else { continueRunning_ = false; } } } } if (doExtraction && entry.IsFile) { ExtractFileEntryIsolatedStorage(store,entry, targetName); } } private void ExtractFileEntryIsolatedStorage(IsolatedStorageFile store, ZipEntry entry, string targetName) { bool proceed = true; if (overwrite_ != Overwrite.Always) { if (store.FileExists(targetName)) { if ((overwrite_ == Overwrite.Prompt) && (confirmDelegate_ != null)) { proceed = confirmDelegate_(targetName); } else { proceed = false; } } } if (proceed) { if (events_ != null) { continueRunning_ = events_.OnProcessFile(entry.Name); } if (continueRunning_) { try { using (var outputISStream=store.OpenFile(targetName,FileMode.OpenOrCreate)) { if (buffer_ == null) { buffer_ = new byte[4096]; } if ((events_ != null) && (events_.Progress != null)) { StreamUtils.Copy(zipFile_.GetInputStream(entry), outputISStream, buffer_, events_.Progress, events_.ProgressInterval, this, entry.Name); } else { StreamUtils.Copy(zipFile_.GetInputStream(entry), outputISStream, buffer_); } if (events_ != null) { continueRunning_ = events_.OnCompletedFile(entry.Name); } } } catch (Exception ex) { if (events_ != null) { continueRunning_ = events_.OnFileFailure(targetName, ex); } else { continueRunning_ = false; } } } } } #endregion
注意,以上代码未经过仔细测试,请谨慎使用!