コード例 #1
0
 protected virtual void OnFileUpdated(HttpUpdaterFileEventArgs e) => FileUpdated?.Invoke(this, e);
コード例 #2
0
        /// <summary>
        /// 執行線上更新。
        /// </summary>
        /// <returns>已更新的檔案數量(包含刪除的檔案)。/// </returns>
        public async Task <int> UpdateAsync()
        {
            if (!HasUpdates())
            {
                return(0);
            }

            CleanUp();

            // 加入預設的變更記錄檔名.
            if (!String.IsNullOrEmpty(m_ChangeLogFileName))
            {
                UpdateItem updItem = new UpdateItem(m_ChangeLogFileName, UpdateAction.Overwrite);
                if (!m_UpdateItems.Contains(updItem))
                {
                    m_UpdateItems.Add(updItem);
                }
            }

            int updCount = 0;

            // execute the following line even for check runs
            List <RollbackItem> rollBackList = new List <RollbackItem>();

            var httpDownloader = new HttpDownloader(OnDownloadProgress, noCache: true);

            try
            {
                foreach (UpdateItem item in m_UpdateItems)
                {
                    var serverFileUrl    = ServerUri + item.FileName;
                    var clientFileName   = ClientPath + item.FileName;
                    var tempFileName     = clientFileName + DateTime.Now.Ticks.ToString() + TempFileExtension;
                    var toDeleteFileName = tempFileName + ToDeleteExtension;


                    // 確保子目錄存在
                    var actualClientPath = Path.GetDirectoryName(clientFileName);
                    if (!Directory.Exists(actualClientPath))
                    {
                        Directory.CreateDirectory(actualClientPath);
                    }

                    // 開始執行更新作業
                    var updEvtArgs = new HttpUpdaterFileEventArgs(item.FileName, updCount + 1, m_UpdateItems.Count);
                    OnFileUpdating(updEvtArgs);

                    RollbackItem rollBackItem;
                    switch (item.Operation)
                    {
                    case UpdateAction.Overwrite:
                        // Logger.Debug($"正在下載檔案,來源: {serverFileUrl},目的: {tempFileName}");

                        // 1.下載檔案並存成暫時檔名
                        await httpDownloader.DownloadAsync(new Uri(serverFileUrl), tempFileName);

                        if (!FileExistsAndNotEmpty(tempFileName))       // 檢查檔案下載是否成功
                        {
                            throw new Exception(ErrorDownloadingFile + item.FileName);
                            // note: 錯誤訊息中不要顯示完整檔案路徑,以免使用者看到完整的下載路徑。
                        }

                        // 2.將欲覆蓋的原有檔案更名為待刪除檔案
                        if (File.Exists(clientFileName))        // 再檢查一次檔案是否存在,以確保不會發生錯誤
                        {
                            // 此時不真的刪除檔案,而是用 rename 的方式將目前的檔案更名(因為檔案可能
                            // 正在使用中),待下次執行更新作業時,便會由 Cleanup 真正將它們刪除。
                            File.Move(clientFileName, toDeleteFileName);
                        }

                        // 3.將下載的檔案更名為欲覆蓋的檔案
                        File.Move(tempFileName, clientFileName);

                        // 4.建立 rollback 項目
                        rollBackItem = new RollbackItem(toDeleteFileName, clientFileName, RollbackAction.Rename);
                        rollBackList.Add(rollBackItem);
                        break;

                    case UpdateAction.Delete:
                        if (File.Exists(clientFileName))        // 檢查檔案是否存在,以確保不會發生錯誤
                        {
                            // 此時不真的刪除檔案,而是用 rename 的方式將目前的檔案更名(因為檔案可能
                            // 正在使用中),待下次執行更新作業時,便會由 Cleanup 真正將它們刪除。
                            File.Move(clientFileName, toDeleteFileName);

                            // 建立 rollback 資訊
                            rollBackItem = new RollbackItem(toDeleteFileName, clientFileName, RollbackAction.Rename);
                            rollBackList.Add(rollBackItem);
                        }
                        break;

                    default:
                        break;
                    }

                    // 注意這裡用了一個技巧:先利用 File.Move 把 client 端要更新的檔案 rename 為待下次
                    // 刪除的暫存檔名,然後才把下載下來的新版檔案 rename 成目標檔案。這樣的話,即使目
                    // 標檔案是正在執行中的主程式(自己),也一樣可以成功更新。這是因為執行中的檔案是
                    // 允許改名的。

                    OnFileUpdated(updEvtArgs);

                    updCount++;
                }

                return(updCount);
            }
            catch (Exception)
            {
                // Rollback
                RollbackItem rollback;

                for (int i = rollBackList.Count - 1; i >= 0; i--)
                {
                    rollback = rollBackList[i];
                    if (rollback.Operation == RollbackAction.Rename)
                    {
                        if (File.Exists(rollback.TargetFileName))
                        {
                            File.Delete(rollback.TargetFileName);
                        }
                        if (File.Exists(rollback.SourceFileName))
                        {
                            File.Move(rollback.SourceFileName, rollback.TargetFileName);
                        }
                    }
                }
                // Logger.Error(ex, "更新檔案時發生錯誤!");
                throw;
            }
            finally
            {
                rollBackList.Clear();
            }
        }