您的位置:首页 > 其它

取经之旅——把WinForms开发的桌面应用移植到Silverlight开发的RIA(第2部分)

2010-03-30 17:53 615 查看

6,数据文件的部署

之前,数据文件是通过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

接着,说明一下我的思路:

在承载Silverlight应用程序的Web应用程序上建立一个文件夹,如“LCADB”
在Web端的LCADB目录中放置一个(或多个)数据文件的zip文件,和一个manifest.xml文件来描述LCADB包含哪些文件,这些文件的最新更新时间
每个zip文件都包含了若干xml文件,我的基础数据都是保存在xml文件中的
如果是Out-Of-Browser的话,在Silverlight程序启动的时候,首先要检查是应用程序本身否有更新,
更新完毕提示用户重启应用程序,
无更新则调用InstallOrUpdateDB
如果不是OfB的话,要用InstallState属性来跳过应用程序更新的代码,直接调用InstallOrUpdateDB
InstallOrUpdateDB,检查是否已经分配独立存储区(我分配了100M,数据文件解压后大致用到30M,其他可能作为临时空间,这点后面叙述)
如果未分配,显示一个“Install DB”的按钮给用户,让用户点击以运行IncreaseQuotaTo,这样做的原因是安全机制限制了IncreaseQuotaTo方法必须由用户的事件所引发
分配完成,接着调用CheckDBManifest
如果已分配,直接调用CheckDBManifest
CheckDBManifest,用来检查服务器上的manifest.xml内容,和本地保存的manifest.xml进行比较以确定需要下载那些zip文件,这里是通过DownloadStringAsync方法直接获得manifest.xml的文本内容
如果需要下载zip文件,就调用DownloadAndExtract,
DownloadAndExtract,用来下载zip文件并解压到独立存储区中,这里是通过OpenReadAsync来获取文件流,
在OpenReadAsync异步方法完成后,解压zip文件的内容到独立存储区中,解压完成后,继续处理下一个需要下载的zip文件,并不断重复。在此处,我对SharpZipLib进行扩展,可以把文件解压到独立存储区中。
整个流程就是这样,由于我没有安装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


注意,以上代码未经过仔细测试,请谨慎使用!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: