更新--本地检查需要下载/删除的文件,然后做替换/删除,并重启
2015-06-02 09:53
295 查看
如何制作自动更新程序?
[版权所有 邱秋 2014 metaphysis@yeah.net, 转载请注明出处]
最近为单位写了一个C/S结构的软件,这个软件是工作在单位的局域网内的。为了减轻为程序进行升级的工作量,需要解决程序自动更新的问题。那么如何做一个自动更新程序呢?
想了一下,更新程序需要解决以下问题:
(A)它需要知道哪些是需要更新的文件,哪些是不需要的文件;
(B)它需要知道从哪里下载更新文件;
(C) 它需要将更新的文件下载下来,并将旧的文件替换掉,将不再需要的文件删除掉;
(D)它需要能够在更新完毕后自动重新启动程序以便用户继续使用;
问题(A)可以通过版本控制的方法来解决。具体方法是为程序所使用的文件都设定一个版本号,所有文件的版本号都记录在一个 XML 文件中,当升级时,检查最新程序的版本控制文件和当前的版本控制文件,当版本号较大时,则表示该文件需要更新。最新的版本控制文件可以放在一个匿名 FTP 上以便程序下载下来和本地的版本控制文件进行比对。如果一个文件不再需要了,则将该文件的版本信息从最新的版本控制文件中删除,通过对比控制文件,就知道该文件不再需要了,可以将之删除。由于我写的程序除主程序外,其他组件都不会发生太多改动,所以我使用了如下的方式来表示一个文件的版本信息:
[vb] view
plaincopy
<?xml version="1.0" encoding="utf-8"?>
<AutoUpdater>
<Updater>
<UpdateUrl>ftp://192.168.1.24/update/</UpdateUrl>
<MainVersion>1.1.102.0</MainVersion>
<LastUpdateTime>2014-01-27</LastUpdateTime>
<UpdateDescription>自动更新程序</UpdateDescription>
</Updater>
<UpdateFileList>
<UpdateFile Version="2.2.5.0" Name="AForge.dll" />
<UpdateFile Version="2.2.5.0" Name="AForge.Video.DirectShow.dll" />
<UpdateFile Version="2.2.5.0" Name="AForge.Video.dll" />
<UpdateFile Version="1.0.100.0" Name="USBCleaner.exe" />
<UpdateFile Version="1.0.100.0" Name="USBViewer.exe" />
</UpdateFileList>
</AutoUpdater>
UpdateUrl 告诉程序要从什么地方下载最新的版本控制文件和更新文件,这里我使用了 FTP 的方式,这样简单一些,我将版本控制文件和最新的程序文件都放在了 ftp://192.168.1.24/update/ 下。MainVersion 表示程序的版本,用来确定是否需要进行升级。LastUpdateTime 表示程序最后的更新时间。UpdateDescription 表示更新程序的描述。UpdateFile 则表示程序中的每一个文件条目,Version
表示其版本,Name 表示相对于程序根目录的文件路径名,如果文件是在根目录下面,则直接是文件名,如果是在子目录下,则在前面加上相应的子目录。
有了这个版本控制文件,问题(B)也解决了,因为从指定的地址下载即可。
问题(C)可以通过比对版本控制文件,确定需要下载的文件和不再需要的文件。然后通过 WebClient 类来下载需要的文件。
问题(D)可以这样解决,主程序先检查是否有升级,如果有升级,则将旧的更新程序再复制一份,启动复制的更新程序,并启动它来下载更新文件,这样的话,就可以解决更新更新程序本身的问题,因为将新的更新程序下载来后,可以直接覆盖掉原始的更新程序而不会产生文件正在使用无法更新的问题,因为运行的是旧的更新程序的副本,在全部更新完毕后,主程序中可以加一段代码检测是否有更新副本产生,只要有就将它删除即可。
想清楚了这些问题,就是具体代码实现了,以下把版本文件解析的代码和更新下载文件的代码贴出来,整个更新模块也提供了下载,供有兴趣的朋友参考使用。下载链接:http://download.csdn.net/detail/metaphysis/6891593。
[vb] view
plaincopy
XmlVersionConfigFile.vb
Imports System.Xml
Imports System.Xml.Linq
Public Class XmlVersionConfigFile
Public Property UpdateUrl As String = String.Empty
Public Property MainVersion As Version = Version.Parse("0.0.0.0")
Public Property LastUpdateTime As Date = DateTimePicker.MinimumDateTime
Public Property UpdateDescription As String = String.Empty
Public Property UpdateFileList As Dictionary(Of String, Version) = Nothing
Public Sub New(ByVal fileContent As String)
ParseXmlVersionFile(fileContent)
End Sub
Private Function ParseXmlVersionFile(ByVal fileContent As String) As Boolean
Dim xdoc As XDocument = Nothing
Try
xdoc = XDocument.Parse(fileContent)
Catch ex As Exception
Return False
End Try
Me.UpdateUrl = xdoc.Element("AutoUpdater").Element("Updater").Element("UpdateUrl").Value
Me.MainVersion = Version.Parse(xdoc.Element("AutoUpdater").Element("Updater").Element("MainVersion").Value)
Date.TryParse(xdoc.Element("AutoUpdater").Element("Updater").Element("LastUpdateTime").Value, Me.LastUpdateTime)
Me.UpdateDescription = xdoc.Element("AutoUpdater").Element("Updater").Element("UpdateDescription").Value
Me.UpdateFileList = New Dictionary(Of String, Version)
Dim query = From UpdateFile In xdoc.Descendants("UpdateFile") Select UpdateFile
For Each fileInfo As XElement In query
Me.UpdateFileList.Add(fileInfo.Attribute("Name").Value.ToLower, Version.Parse(fileInfo.Attribute("Version").Value))
Next
Return True
End Function
End Class
[vb] view
plaincopy
UpdatingForm.vb
Imports System.IO
Public Class UpdatingForm
Public Property LocalVersionConfig As XmlVersionConfigFile = Nothing
Public Property ServerVersionConfig As XmlVersionConfigFile = Nothing
Private WithEvents webClient As New System.Net.WebClient
Private _downloadIndex As Integer
Private _localConfigFileName As String = "version.xml"
Private _localXmlFilePath As String = Path.Combine(Application.StartupPath, _localConfigFileName)
Private _updateUrl As String = String.Empty
Private _deleteFileList As New List(Of String)
Private Sub webClient_DownloadFileCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.AsyncCompletedEventArgs) Handles webClient.DownloadFileCompleted
Me.lvwFile.Items(_downloadIndex).ImageIndex = 2
lblSinglePercent.Text = "0%"
prbSingle.Value = 0
DownloadNextFile()
End Sub
Private Sub webClient_DownloadProgressChanged(ByVal sender As System.Object, ByVal e As System.Net.DownloadProgressChangedEventArgs) Handles webClient.DownloadProgressChanged
Dim currentPercent As String = e.ProgressPercentage & "%"
If currentPercent <> Me.lvwFile.Items(_downloadIndex).SubItems(3).Text Then
Me.lvwFile.Items(_downloadIndex).SubItems(3).Text = currentPercent
prbSingle.Value = e.ProgressPercentage
lblSinglePercent.Text = currentPercent
prbAll.Value = Int((_downloadIndex + 1) / Me.lvwFile.Items.Count * 100)
lblAllPercent.Text = prbAll.Value & "%"
End If
End Sub
Private Sub btnQuit_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnQuit.Click
Me.Close()
End Sub
Private Sub DownloadNextFile()
If _downloadIndex < (lvwFile.Items.Count - 1) Then
_downloadIndex += 1
lvwFile.Items(_downloadIndex).ImageIndex = 1
Try
Dim destPath As String = IO.Path.Combine(Application.StartupPath, lvwFile.Items(_downloadIndex).SubItems(1).Text)
File.Delete(destPath)
webClient.DownloadFileAsync(New Uri(_updateUrl & lvwFile.Items(_downloadIndex).SubItems(1).Text), destPath)
Catch ex As Exception
Me.lvwFile.Items(_downloadIndex).ImageIndex = 3
MsgBox("下载文件发生错误,更新失败。错误原因: " & ex.Message, MsgBoxStyle.Critical, "错误")
Me.Close()
End Try
Else
UpdateFileCompleted()
End If
End Sub
Private Sub UpdateFileCompleted()
' 更新显示信息。
prbSingle.Value = prbSingle.Maximum
lblSinglePercent.Text = "100%"
lblAllPercent.Text = "100%"
' 删除不需要的文件。
For Each f As String In _deleteFileList
Try
File.Delete(Path.Combine(Application.StartupPath, f))
Catch ex As Exception
'
End Try
Next
Me.btnQuit.Enabled = True
Process.Start(IO.Path.Combine(Application.StartupPath, "szpt.exe"))
Me.Close()
End Sub
Private Sub LoadUpdateFile()
_updateUrl = Me.ServerVersionConfig.UpdateUrl
' 查找客户端需要更新的文件和需要删除的文件。
For Each p As KeyValuePair(Of String, Version) In Me.LocalVersionConfig.UpdateFileList
If Me.ServerVersionConfig.UpdateFileList.ContainsKey(p.Key) Then
If Me.ServerVersionConfig.UpdateFileList(p.Key) > Me.LocalVersionConfig.UpdateFileList(p.Key) Then
Dim item As ListViewItem = Me.lvwFile.Items.Add(String.Empty, 0)
item.SubItems.Add(p.Key)
item.SubItems.Add(Me.ServerVersionConfig.UpdateFileList(p.Key).ToString)
item.SubItems.Add(String.Empty)
End If
Else
_deleteFileList.Add(p.Key)
End If
Next
' 查找服务器端新增需要下载的文件。
For Each p As KeyValuePair(Of String, Version) In Me.ServerVersionConfig.UpdateFileList
If Me.LocalVersionConfig.UpdateFileList.ContainsKey(p.Key) = False Then
Dim item As ListViewItem = Me.lvwFile.Items.Add(String.Empty, 0)
item.SubItems.Add(p.Key)
item.SubItems.Add(p.Value.ToString)
item.SubItems.Add(String.Empty)
End If
Next
' 版本控制文件为必须下载文件。
Dim itemVersion As ListViewItem = Me.lvwFile.Items.Add(String.Empty, 0)
itemVersion.SubItems.Add("version.xml")
itemVersion.SubItems.Add(Me.ServerVersionConfig.MainVersion.ToString)
itemVersion.SubItems.Add(String.Empty)
' 设置当前下载的文件序数。
_downloadIndex = -1
End Sub
Private Sub UpdatingForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
LoadUpdateFile()
DownloadNextFile()
End Sub
End Class
[版权所有 邱秋 2014 metaphysis@yeah.net, 转载请注明出处]
最近为单位写了一个C/S结构的软件,这个软件是工作在单位的局域网内的。为了减轻为程序进行升级的工作量,需要解决程序自动更新的问题。那么如何做一个自动更新程序呢?
想了一下,更新程序需要解决以下问题:
(A)它需要知道哪些是需要更新的文件,哪些是不需要的文件;
(B)它需要知道从哪里下载更新文件;
(C) 它需要将更新的文件下载下来,并将旧的文件替换掉,将不再需要的文件删除掉;
(D)它需要能够在更新完毕后自动重新启动程序以便用户继续使用;
问题(A)可以通过版本控制的方法来解决。具体方法是为程序所使用的文件都设定一个版本号,所有文件的版本号都记录在一个 XML 文件中,当升级时,检查最新程序的版本控制文件和当前的版本控制文件,当版本号较大时,则表示该文件需要更新。最新的版本控制文件可以放在一个匿名 FTP 上以便程序下载下来和本地的版本控制文件进行比对。如果一个文件不再需要了,则将该文件的版本信息从最新的版本控制文件中删除,通过对比控制文件,就知道该文件不再需要了,可以将之删除。由于我写的程序除主程序外,其他组件都不会发生太多改动,所以我使用了如下的方式来表示一个文件的版本信息:
[vb] view
plaincopy
<?xml version="1.0" encoding="utf-8"?>
<AutoUpdater>
<Updater>
<UpdateUrl>ftp://192.168.1.24/update/</UpdateUrl>
<MainVersion>1.1.102.0</MainVersion>
<LastUpdateTime>2014-01-27</LastUpdateTime>
<UpdateDescription>自动更新程序</UpdateDescription>
</Updater>
<UpdateFileList>
<UpdateFile Version="2.2.5.0" Name="AForge.dll" />
<UpdateFile Version="2.2.5.0" Name="AForge.Video.DirectShow.dll" />
<UpdateFile Version="2.2.5.0" Name="AForge.Video.dll" />
<UpdateFile Version="1.0.100.0" Name="USBCleaner.exe" />
<UpdateFile Version="1.0.100.0" Name="USBViewer.exe" />
</UpdateFileList>
</AutoUpdater>
UpdateUrl 告诉程序要从什么地方下载最新的版本控制文件和更新文件,这里我使用了 FTP 的方式,这样简单一些,我将版本控制文件和最新的程序文件都放在了 ftp://192.168.1.24/update/ 下。MainVersion 表示程序的版本,用来确定是否需要进行升级。LastUpdateTime 表示程序最后的更新时间。UpdateDescription 表示更新程序的描述。UpdateFile 则表示程序中的每一个文件条目,Version
表示其版本,Name 表示相对于程序根目录的文件路径名,如果文件是在根目录下面,则直接是文件名,如果是在子目录下,则在前面加上相应的子目录。
有了这个版本控制文件,问题(B)也解决了,因为从指定的地址下载即可。
问题(C)可以通过比对版本控制文件,确定需要下载的文件和不再需要的文件。然后通过 WebClient 类来下载需要的文件。
问题(D)可以这样解决,主程序先检查是否有升级,如果有升级,则将旧的更新程序再复制一份,启动复制的更新程序,并启动它来下载更新文件,这样的话,就可以解决更新更新程序本身的问题,因为将新的更新程序下载来后,可以直接覆盖掉原始的更新程序而不会产生文件正在使用无法更新的问题,因为运行的是旧的更新程序的副本,在全部更新完毕后,主程序中可以加一段代码检测是否有更新副本产生,只要有就将它删除即可。
想清楚了这些问题,就是具体代码实现了,以下把版本文件解析的代码和更新下载文件的代码贴出来,整个更新模块也提供了下载,供有兴趣的朋友参考使用。下载链接:http://download.csdn.net/detail/metaphysis/6891593。
[vb] view
plaincopy
XmlVersionConfigFile.vb
Imports System.Xml
Imports System.Xml.Linq
Public Class XmlVersionConfigFile
Public Property UpdateUrl As String = String.Empty
Public Property MainVersion As Version = Version.Parse("0.0.0.0")
Public Property LastUpdateTime As Date = DateTimePicker.MinimumDateTime
Public Property UpdateDescription As String = String.Empty
Public Property UpdateFileList As Dictionary(Of String, Version) = Nothing
Public Sub New(ByVal fileContent As String)
ParseXmlVersionFile(fileContent)
End Sub
Private Function ParseXmlVersionFile(ByVal fileContent As String) As Boolean
Dim xdoc As XDocument = Nothing
Try
xdoc = XDocument.Parse(fileContent)
Catch ex As Exception
Return False
End Try
Me.UpdateUrl = xdoc.Element("AutoUpdater").Element("Updater").Element("UpdateUrl").Value
Me.MainVersion = Version.Parse(xdoc.Element("AutoUpdater").Element("Updater").Element("MainVersion").Value)
Date.TryParse(xdoc.Element("AutoUpdater").Element("Updater").Element("LastUpdateTime").Value, Me.LastUpdateTime)
Me.UpdateDescription = xdoc.Element("AutoUpdater").Element("Updater").Element("UpdateDescription").Value
Me.UpdateFileList = New Dictionary(Of String, Version)
Dim query = From UpdateFile In xdoc.Descendants("UpdateFile") Select UpdateFile
For Each fileInfo As XElement In query
Me.UpdateFileList.Add(fileInfo.Attribute("Name").Value.ToLower, Version.Parse(fileInfo.Attribute("Version").Value))
Next
Return True
End Function
End Class
[vb] view
plaincopy
UpdatingForm.vb
Imports System.IO
Public Class UpdatingForm
Public Property LocalVersionConfig As XmlVersionConfigFile = Nothing
Public Property ServerVersionConfig As XmlVersionConfigFile = Nothing
Private WithEvents webClient As New System.Net.WebClient
Private _downloadIndex As Integer
Private _localConfigFileName As String = "version.xml"
Private _localXmlFilePath As String = Path.Combine(Application.StartupPath, _localConfigFileName)
Private _updateUrl As String = String.Empty
Private _deleteFileList As New List(Of String)
Private Sub webClient_DownloadFileCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.AsyncCompletedEventArgs) Handles webClient.DownloadFileCompleted
Me.lvwFile.Items(_downloadIndex).ImageIndex = 2
lblSinglePercent.Text = "0%"
prbSingle.Value = 0
DownloadNextFile()
End Sub
Private Sub webClient_DownloadProgressChanged(ByVal sender As System.Object, ByVal e As System.Net.DownloadProgressChangedEventArgs) Handles webClient.DownloadProgressChanged
Dim currentPercent As String = e.ProgressPercentage & "%"
If currentPercent <> Me.lvwFile.Items(_downloadIndex).SubItems(3).Text Then
Me.lvwFile.Items(_downloadIndex).SubItems(3).Text = currentPercent
prbSingle.Value = e.ProgressPercentage
lblSinglePercent.Text = currentPercent
prbAll.Value = Int((_downloadIndex + 1) / Me.lvwFile.Items.Count * 100)
lblAllPercent.Text = prbAll.Value & "%"
End If
End Sub
Private Sub btnQuit_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnQuit.Click
Me.Close()
End Sub
Private Sub DownloadNextFile()
If _downloadIndex < (lvwFile.Items.Count - 1) Then
_downloadIndex += 1
lvwFile.Items(_downloadIndex).ImageIndex = 1
Try
Dim destPath As String = IO.Path.Combine(Application.StartupPath, lvwFile.Items(_downloadIndex).SubItems(1).Text)
File.Delete(destPath)
webClient.DownloadFileAsync(New Uri(_updateUrl & lvwFile.Items(_downloadIndex).SubItems(1).Text), destPath)
Catch ex As Exception
Me.lvwFile.Items(_downloadIndex).ImageIndex = 3
MsgBox("下载文件发生错误,更新失败。错误原因: " & ex.Message, MsgBoxStyle.Critical, "错误")
Me.Close()
End Try
Else
UpdateFileCompleted()
End If
End Sub
Private Sub UpdateFileCompleted()
' 更新显示信息。
prbSingle.Value = prbSingle.Maximum
lblSinglePercent.Text = "100%"
lblAllPercent.Text = "100%"
' 删除不需要的文件。
For Each f As String In _deleteFileList
Try
File.Delete(Path.Combine(Application.StartupPath, f))
Catch ex As Exception
'
End Try
Next
Me.btnQuit.Enabled = True
Process.Start(IO.Path.Combine(Application.StartupPath, "szpt.exe"))
Me.Close()
End Sub
Private Sub LoadUpdateFile()
_updateUrl = Me.ServerVersionConfig.UpdateUrl
' 查找客户端需要更新的文件和需要删除的文件。
For Each p As KeyValuePair(Of String, Version) In Me.LocalVersionConfig.UpdateFileList
If Me.ServerVersionConfig.UpdateFileList.ContainsKey(p.Key) Then
If Me.ServerVersionConfig.UpdateFileList(p.Key) > Me.LocalVersionConfig.UpdateFileList(p.Key) Then
Dim item As ListViewItem = Me.lvwFile.Items.Add(String.Empty, 0)
item.SubItems.Add(p.Key)
item.SubItems.Add(Me.ServerVersionConfig.UpdateFileList(p.Key).ToString)
item.SubItems.Add(String.Empty)
End If
Else
_deleteFileList.Add(p.Key)
End If
Next
' 查找服务器端新增需要下载的文件。
For Each p As KeyValuePair(Of String, Version) In Me.ServerVersionConfig.UpdateFileList
If Me.LocalVersionConfig.UpdateFileList.ContainsKey(p.Key) = False Then
Dim item As ListViewItem = Me.lvwFile.Items.Add(String.Empty, 0)
item.SubItems.Add(p.Key)
item.SubItems.Add(p.Value.ToString)
item.SubItems.Add(String.Empty)
End If
Next
' 版本控制文件为必须下载文件。
Dim itemVersion As ListViewItem = Me.lvwFile.Items.Add(String.Empty, 0)
itemVersion.SubItems.Add("version.xml")
itemVersion.SubItems.Add(Me.ServerVersionConfig.MainVersion.ToString)
itemVersion.SubItems.Add(String.Empty)
' 设置当前下载的文件序数。
_downloadIndex = -1
End Sub
Private Sub UpdatingForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
LoadUpdateFile()
DownloadNextFile()
End Sub
End Class
相关文章推荐
- NYOJ 20 吝啬的国度
- 《飞轮效应:数据驱动的企业》
- C++细节2
- android中实现记住密码功能
- Xcode 工程文件打开不出来, cannot be opened because the project file cannot be parsed.
- C++细节2
- MyEclipse Hibernate Reverse Engineering 找不到项目错误
- Android动态加载字节码
- [LeetCode] Linked List Cycle II
- NYOJ 27 水池数目
- Python中for循环控制语句用法实例
- Structual设计--Facade模式
- 使用Java API创建(create),查看(describe),列举(list),删除(delete)Kafka主题(Topic)--转载
- 批量修改sql server 2008的架构
- 第一节---nginx安装
- NOI 2011 题解
- MySQL之——新安装的MySQL必须调整的10项配置(插曲)
- 手机应用软件测试经验总结
- Microsoft.Office.Interop.Excel的用法以及利用Microsoft.Office.Interop.Excel将web页面转成PDF
- jQuery 效果- 隐藏和显示