2016 年 12 月

第 31 卷,第 13 期

本文章是由機器翻譯。

通用 Windows 平台 - 通用 Windows 平台應用程式的檔案系統監視

Adam Wilson |2016 年 12 月

檔案系統的 Windows 裝置的快速變更。共用程式庫,例如手機相簿,是其中一個裝置上的所有處理程序其中同時使用相同的資料互動的幾個地方。建置通用 Windows 平台 (UWP) 應用程式提供很好的經驗與使用者的相片表示你已經深入到這個混亂。

在視窗 10 週年紀念日更新中,Microsoft 會加入新功能,使管理更容易這個混亂。系統現在仍能提供一份文件庫,從圖片特別乃至於要刪除的整個資料夾內所發生的所有變更。如果您想要建置雲端備份提供者、 追蹤檔案關閉裝置正在移動,或甚至只是顯示最新的相片,這會是大有幫助。

顯示裝置所花費的最新相片不僅是供開發人員建置下一步 Instagram。最近我與企業夥伴合作,依建置時檢查其組織的應用程式的樂趣。應用程式,請依照類似的模式︰ 偵測器瀏覽網站 Windows 平板電腦或電話,填滿一些站台,相關資訊的報表中會使用裝置的站台的圖片和,最後,將報表上傳到安全的伺服器。所有的公司很重要的正確、 未經修改的相片上傳的報表。

在下一步的幾頁我逐步建置企業檢查應用程式。一路我指出我在開發過程中遇到一些非常棘手問題,請注意,您可以如何避免這些應用程式中。這些課程也可以套用至任何其他應用程式,想要追蹤的變更,在檔案系統中,例如雲端備份服務,但因為檢查應用程式是很常見,首先我要那里,您可以修改您要建置的應用程式的程式碼。

檢查程序︰ 每個人都沒有它們

整個企業中的所有大小和產業,一件事是︰ 有一些超強重要的商務程序的偵測器。大規模製造公司,追蹤其設備的狀態,確保會正確組合顯示,企業取決於進行確定事情一切零售商從都一致且安全地跨所有他們的網站。

我要支援此基本程序就相當簡單︰

  1. 偵測器他平板電腦上建立站台的新報表執行個體。
  2. 偵測器會報告之網站的圖片。
  3. 將圖片上載至安全的伺服器,以及報表。

在步驟 2 中,不過,有幾個可能出錯︰

  • 偵測器可以挑選要附加到報告錯誤的圖片。
  • 圖片無法修改,以顯示不正確的資訊。
  • 圖片可能會不小心刪除,但報表會上傳之前偵測器離開位置之後。

在任何這些情況下,報表將無效,而需要重複執行檢查,額外的費用,企業的偵測器。幸運的是,在 Windows 10 週年紀念日更新新的變更追蹤 Api,沒有簡單的方式來防止這類錯誤,並協助使用者快速準確地完成其工作。

基本概念: 從相機取得圖片的存取

第一個步驟確定應用程式可以存取來自相機的圖片。在 Windows 中,系統相機會寫入至相機相簿資料夾,也就是子資料夾的圖片媒體櫃。我無法 (和實際上並未) 撰寫一整篇文章有關如何存取 [圖片媒體櫃 (bit.ly/2dCdj4O),但以下是基本概念︰

  • 宣告您的目的來存取您的資訊清單中的圖片媒體櫃 (bit.ly/2dqoRJX) 藉由新增功能名稱 ="musicLibrary"/ > 在資訊清單編輯器] 或 [檢查在精靈中的 [功能] 索引標籤下方的圖片媒體櫃方塊。
  • 取得 StorageFolder,代表正在使用 KnownFolders.CameraRoll 寫入相機中的位置 (bit.ly/2dEiVMS)。
  • 取得物件,表示整個圖片媒體櫃使用 StorageLibrary.GetLibraryAsync(KnownLibraryId.Pictures) (bit.ly/2dmZ85X)。

別擔心,如果裝置具有 sd 記憶卡和使用者設定的內部儲存體或 sd 記憶卡撰寫新的圖片。KnownFolder 類別將抽離,為您,並提供包含相機可以寫入檔案的所有位置的虛擬資料夾。

內容變更時獲得通知

一旦您有存取檔案時,就開始追蹤變更的時間。我建議您盡早這麼做。在完美的世界中,偵測器會一律建立新的報表才能啟動拍照,但實際上它們通常已經少數圖片他們建立新的報表,請記得之前。

設定通知和變更追蹤包含三個步驟︰

  1. 正在初始化變更追蹤程式,來告訴系統您感興趣追蹤哪些程式庫。即使您的應用程式並未執行,而應用程式可以讀取變更的清單,在任何時候,會繼續追蹤。
  2. 註冊變更通知,在背景中啟動應用程式的背景工作,就會變更程式庫,不論是否目前正在執行。
  3. 註冊在前景中的變更通知。如果您的應用程式在前景中,您可以註冊其他事件,每當檔案變更的特定範圍內時。

請注意,步驟 2 和 3 可能無法重疊。我將討論如何設定這兩種類型的通知本文中,但在圖表中的**[圖 1**可協助您選擇您想要在您的應用程式中使用。一般會建議一律使用 StorageLibraryContentChangeTrigger,背景變更通知,並使用前景事件,如果您有特定的 UI 需求,例如顯示給使用者的檔案系統的檢視。

[圖 1 的變更通知類型

  前景變更事件 背景變更通知
使用期限 只有當您的應用程式正在執行 將會觸發背景工作,即使您的應用程式未執行
範圍 您可以自訂任何資料夾或在系統上的程式庫 只有名為程式庫 (圖片、 視訊、 音樂,文件)
篩選 可以進行篩選,以引發特定的檔案類型的事件 將會引發事件的任何檔案或資料夾的變更
觸發機制 具名的事件 背景工作觸發程序

StorageLibraryChangeTracker 是週年紀念日更新中加入新類別 (bit.ly/2dMFlfu)。它可讓應用程式來訂閱一份文件庫內所發生的變更。系統監看文件庫中的所有檔案,並建立一份會發生的變更。您的應用程式可以要求變更的清單,並在其休閒處理記錄。

如果您曾經使用過,但它可用於格式化為 FAT 的磁碟機,以及,StorageLibraryChangeTracker 是非常類似於 NTFS 變更日誌。如需詳細資訊可以用閱讀我的部落格上深入探討 (bit.ly/2dQ6MEK)。

初始化 StorageLibraryChangeTracker 是相當簡單 — 您只要取得特定程式庫的執行個體的變更追蹤程式,並呼叫啟用︰

StorageLibrary picsLib =
  await StorageLibrary.GetLibraryAsync(KnownLibraryId.Pictures);
picsLib.ChangeTracker.Enable();

從這裡開始,變更追蹤程式會維護一份文件庫的所有變更。現在讓我們來確定您的應用程式會收到通知,每次變更。

接聽前景通知

若要聆聽前景通知您的應用程式有建立、 執行和停留開啟查詢您的變更有關的位置。建立和執行的查詢會告知系統的位置是應用程式相關。保留查詢的結果的參考之後,表示您的應用程式想要在某個項目變更時收到通知︰

StorageFolder photos = KnownFolders.CameraRoll;
// Create a query containing all the files your app will be tracking
QueryOptions option = new QueryOptions(CommonFileQuery.DefaultQuery,
  supportedExtentions);
option.FolderDepth = FolderDepth.Shallow;
// This is important because you are going to use indexer for notifications
option.IndexerOption = IndexerOption.UseIndexerWhenAvailable;
StorageFileQueryResult resultSet =
  photos.CreateFileQueryWithOptions(option);
// Indicate to the system the app is ready to change track
await resultSet.GetFilesAsync(0, 1);
// Attach an event handler for when something changes on the system
resultSet.ContentsChanged += resultSet_ContentsChanged;

如您所見,我使用了一些有趣的最佳化作業,可能會很有幫助您的應用程式︰

  • CommonFileQuery.DefaultQuery 在其中沒有索引子的情況下讓整個作業速度。如果使用另一個排序順序和索引子不提供,系統必須引導整個查詢空間,再傳回第一個結果。
  • 淺層查詢的查詢。這是因為相機要總是撰寫根目錄的手機相簿中,且避免深層的查詢可減少的檔案系統已追蹤的變更數目。
  • 使用索引子不是必要項目,但它能加速應用程式中出現的通知。不含索引子,通知可能需要 30 秒的時間到達您的應用程式。
  • 查詢的一個檔案是開始追蹤變更索引的位置,最快的方法,不過要查詢更多檔案,以防 UI 需要它們。

現在只要在查詢中的項目變更時,事件處理常式將會觸發,讓您的應用程式有機會處理的變更。

正在註冊的背景變更觸發程序

並非所有的變更將會發生在您的應用程式的前景,即使您的應用程式在前景,它可能不需要的前景通知資料粒度。StorageLibraryContentsChangedTrigger 是文件庫中發生任何變更時收到通知的好方法。因為登錄背景工作使用標準程序 (bit.ly/2dqKt9i),我會針對它進行快速 (請參閱**[圖 2**)。

[圖 2 註冊背景工作

// Check if your app has access to the background
var requestStatus = await BackgroundExecutionManager.RequestAccessAsync();
if (!(requestStatus ==
  BackgroundAccessStatus.AllowedMayUseActiveRealTimeConnectivity ||
  requestStatus == BackgroundAccessStatus.AllowedSubjectToSystemPolicy ||
  requestStatus ==
    BackgroundAccessStatus.AllowedWithAlwaysOnRealTimeConnectivity ||
  requestStatus == BackgroundAccessStatus.AlwaysAllowed))
{
  log("Failed to get access to the background");
  return;
}
// Build up the trigger to fire when something changes in the pictures library
var builder = new BackgroundTaskBuilder();
builder.Name = "Photo Change Trigger";
StorageLibrary picturesLib =
  await StorageLibrary.GetLibraryAsync(KnownLibraryId.Pictures);
var picturesTrigger = StorageLibraryContentChangedTrigger.Create(picturesLib);
// We are registering to be activated in OnBackgroundActivated instead of
// BackgroundTask.Run; either works, but I prefer the single-process model
builder.SetTrigger(picturesTrigger);
BackgroundTaskRegistration task = builder.Register();

有幾個重要事項来注意的範例中**[圖 2**。

您仍然可以使用舊的兩個處理序模型以註冊您的背景工作,但在單一處理序模型週年紀念日更新中加入仔細研究一下。它快速贏得我有多麼簡單使用與前景應用程式時,收到背景觸發程序是多麼的容易。

StorageLibraryContentChangedTrigger 就會引發圖片庫,其中可能包含不是應用程式相關的檔案中的任何變更。我將介紹如何在稍後的章節中,將那些篩選掉,但永遠是必須瞭解有時會有您的應用程式啟動時並無關聯。

您可以試試檢查背景工作,是否因為資源配置不同,您正在執行中的背景或前景。您可以在資源配置模型,在找到更詳細地bit.ly/2cNvcSr。 

讀取所做的變更

現在您的應用程式要有機會執行程式碼,每當手機相簿,請在前景或背景的內容變更。若要找出有哪些改變,您需要從 StorageLibraryChangeTracker 讀取的變更集。第一個步驟是取得讀取器物件,這可讓您列舉自從上次檢查您的應用程式所發生的變更。當您在它時,您也可以抓取第一個批次的變更来處理︰

StorageLibrary picturesLib =
  await StorageLibrary.GetLibraryAsync(KnownLibraryId.Pictures);           
StorageLibraryChangeTracker picturesTracker= picturesLib.ChangeTracker;
picturesTracker.Enable();
StorageLibraryChangeReader changeReader = picturesTracker.GetChangeReader();
IReadOnlyList<StorageLibraryChange> changes = await changeReader.ReadBatchAsync();

一旦您熟悉的一組變更,就加以處理。在此應用程式,我要把焦點放在要變更的圖片僅,並忽略其他所有檔案和資料夾的變更。如果您想在其他類型的系統上的變更,我深入探討在變更追蹤程式已變更的所有不同類型的相關詳細資料 (bit.ly/2dQ6MEK)。

我要逐步進行變更,並拉出項目中的我有興趣。此應用程式,它會.jpg 檔案,內容的任何變更中所示**[圖 3**。

[圖 3 檢查檔案的變更

foreach (StorageLibraryChange change in changes)
{
  if (change.ChangeType == StorageLibraryChangeType.ChangeTrackingLost)
  {
    // Change tracker is in an invalid state and must be reset
    // This should be a very rare case, but must be handled
    picturesLib.ChangeTracker.Reset();
    return;
  }
  if (change.IsOfType(StorageItemTypes.File))
  {
    await ProcessFileChange(change);
  }
  else if (change.IsOfType(StorageItemTypes.Folder))
  {
    // No-op; not interested in folders
  }
  else
  {
    if (change.ChangeType == StorageLibraryChangeType.Deleted)
    {
      UnknownItemRemoved(change.Path);
    }
  }
}
// Mark that all the changes have been seen and for the change tracker
// to never return these changes again
await changeReader.AcceptChangesAsync();

以下是一些有趣的程式碼片段中的項目**[圖 3**。

我所做的第一件事是 StorageLibraryChangeType.ChangeTrackingLost 檢查。這應該只有在大型的檔案系統作業 (其中系統沒有足夠的儲存體的整組變更) 之後發生或發生嚴重的內部失敗。任一情況下,不會再被讀取的變更追蹤的任何項目可以信任。應用程式必須重設為可以信任的未來結果的變更追蹤。

UnknownItemRemoved (變更。路徑) 會被叫用任何時間的檔案或資料夾會刪除從 FAT 磁碟分割,例如 SD 卡。一旦從系統無法分辨它是否是目錄或檔案已刪除的 FAT 磁碟分割中刪除項目。我要逐步完成一些程式碼中位元可顯示如何可以找出您的應用程式中發生的事件。

已處理的所有變更,當您呼叫 changeReader.AcceptChangesAsync。這會指示變更 tracker 應用程式已經處理過的所有變更,而且不需要再次看到它們。下一次建立 StorageLibraryChangeReader 會包含此點之後發生的變更。不會呼叫 AcceptChangesAsync 會導致內部變更緩衝區填滿溢位而導致 StorageLibraryChangeType.ChangeTrackingLost。

處理變更

現在您知道如何逐步解說所做的變更下, 一步就是實質 ProcessFileChange 方法。要記住的一個重要概念是,開啟檔案 (藉由建立 StorageFile 物件) 是非常耗費資源。若要取得 StorageFile 物件在過程中,系統必須能夠檢查權限、 建立檔案的控制代碼、 從磁碟讀取檔案相關的一些中繼資料,然後封送處理的控制代碼和回應用程式的程序的中繼資料的跨處理序呼叫。因此,您會想要減少的 StorageFiles 建立,特別是如果您不想要開啟的檔案資料流。

變更追蹤程式提供應用程式路徑,並建立 StorageFile 之前感興趣的變更,協助您判斷是否檔案類型的應用程式類型。讓我們先來進行多篩選盡量與這項資訊建立 StorageFile 之前所示**[圖 4**。

[圖 4 處理變更

private async Task ProcessFileChange(StorageLibraryChange change)
{
  // Temp variable used for instantiating StorageFiles for sorting if needed later
  StorageFile newFile = null;
  switch (change.ChangeType)
  {
    // New File in the Library
    case StorageLibraryChangeType.Created:
    case StorageLibraryChangeType.MovedIntoLibrary:
    case StorageLibraryChangeType.MovedOrRenamed:
      if (change.Path.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase))
      {
        StorageFile image = (StorageFile)(await change.GetStorageItemAsync());
        AddImageToReport(image);                                               
      }                   
      break;
    // File Removed From Library
    case StorageLibraryChangeType.Deleted:
    case StorageLibraryChangeType.MovedOutOfLibrary:
      if (change.Path.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase))
      {
        var args = new FileDeletedWarningEventArgs();
        args.Path = change.Path;
        FileDeletedWarningEvent(this, args);
      }
      break;
    // Modified Contents
    case StorageLibraryChangeType.ContentsChanged:
      if (change.Path.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase))
      {
        newFile = (StorageFile)(await change.GetStorageItemAsync());
        var imageProps = await newFile.Properties.GetImagePropertiesAsync();
        DateTimeOffset dateTaken = imageProps.DateTaken;
        DateTimeOffset dateModified = newFile.DateCreated;
        if (DateTimeOffset.Compare(dateTaken.AddSeconds(70), dateModified) > 0)
        {
          // File was modified by the user
          log("File path: " + newFile.Path + " was modified after being taken");
        }
      }                  
      break;
    // Ignored Cases
    case StorageLibraryChangeType.EncryptionChanged:
    case StorageLibraryChangeType.ContentsReplaced:
    case StorageLibraryChangeType.IndexingStatusChanged:
    default:
      // These are safe to ignore in this application
      break;                   
  }
}

讓我們細分為以下狀況來看看每個 case 陳述式。

程式庫中的新檔案︰ 在第一個陳述式中,我正在尋找已加入至程式庫的新檔案。在系統相機上,檔案會建立文件庫中,我們 StorageLibraryChangeType.Created 類型的變更。某些協力廠商應用程式實際上其應用程式資料容器中建立映像,然後將它移至程式庫,所以我太 StorageLibraryChangeType.MovedIntoLibrary 視為建立,

我要處理程序 StorageLibraryChangeType.MovedOrRenamed 為新的檔案。這是為了解決 Windows phone 上的內建攝影機的很奇怪。圖片相機時,它寫入暫存檔結束.jpg。 ~ tmp。它會在稍後完成圖片,移除。 ~ tmp 延伸模組。如果您的應用程式是快速,足以完成相機之前攔截檔案完成映像,它可能會看到重新命名事件,而不是建立事件。

因為此應用程式會想要只在使用者所採取的圖片中,我會篩選到只.jpg 副檔名的檔案。您可以這麼建立 StorageFile 並檢查其 ContentType 屬性,但我想要避免建立不必要的 StorageFiles。一旦確知檔案是其中一個感興趣,我要將檔案與另一個方法進行一些資料處理。

我正在使用的轉換,而不與關鍵字因為我已經要求 StorageLibraryChange StorageItem 型別將會使用︰ StorageLibraryChange.IsOfType(StorageItemTypes.File)。

從媒體庫移除檔案︰ 在第二個案例中,應用程式會查看檔案已移除從程式庫的情況。您會注意到,我已經組成兩個不同的變更類型一次。從磁碟、 使用檔案系統 Api 刪除傳統案例中永久刪除檔案時,就會引發 StorageLibraryChangeType.Deleted。不過,如果使用者手動刪除檔案從檔案總管相反地,檔案會傳送到資源回收筒。這會顯示為 StorageLibraryChangeType.MovedOutOfLibrary 因為檔案仍存在於磁碟上。

在此情況下,我引發警告,以偵測器檔案是看不見了,如果它已被意外刪除。在更注重安全性的應用程式,它可能合理來儲存檔案刪除或修改稍後稽核調查發生。

修改內容︰ 修改檔案的內容是有趣的案例,此應用程式。此應用程式可能會用於安全操作,因為您不想允許使用者變更映像之前他們正在上傳到報表中,雖然可能有正當的理由映像的內容之後圖片拍攝變更。

您可能會看到型別 StorageLibraryChangeType.ContentsChanged 大約三到 60 秒之後使用相機拍攝圖片,會引發的通知。這是因為它有時最多可能需要一分鐘的時間来取得其座標,從 GPS 系統。此應用程式中我不擔心 GPS 資訊讓我要立即處理的檔案。在某些應用程式,它可能合理檢查位置資料已寫入至檔案,且未指定,等到的位置決定。

在此情況下,我提供非常安全的折衷。如果檔案已修改超過 70 秒拍攝之後,我將假設使用者已修改,登出的相容性問題,供日後調查。如果修改 70-第二個視窗中,我假設這是由系統新增 GPS 資料時,您可以放心忽略。

變更加密︰ 是否應該關心本例中有一個問題︰ 沒有您的企業 Windows 資訊保護 (WIP) 搭配使用加密來保護機密的資訊嗎? 如果是變更的的話,這類是變更的大問題。這表示有人變更檔案的保護,並且檔案可以坐在未受保護的磁碟。逐步解說如何檢查檔案是否安全 (相對於只移至另一個保護層級) 的 WIP 資訊的所有已超出本文的範圍。如果您要在企業中部署 WIP,這類是變更的很重要,讓您監視。

對於不使用 WIP 或加密來保護機密的企業資產,我強烈建議您考慮,但現在這項變更是安全地忽略。

略過的情況下︰ 最後,在此應用程式有意義要略過的一些可能發生在檔案系統的作業。StorageLibraryChangeType.IndexingStatusChanged 只很重要,在桌面應用程式即將執行的程式庫重複的查詢,每次必須是相同的結果。StorageLibraryChangeType.ContentsReplaced 表示,已有趣此應用程式不會變更檔案的永久連結。

偵測到 SD 卡上的影像刪除

您應該還記得,是否移除的項目是檔案或資料夾之後,就會刪除在 FAT 磁碟機,例如 SD 卡無法判斷系統。因此,如果您想知道在 FAT 磁碟機上的刪除動作,則需要一些特殊的排序程式碼。我的應用程式中,我只想要知道是否正在刪除圖片從 sd 記憶卡,可以讓程式碼很簡單︰

private void UnknownItemRemoved(StorageLibraryChange change)
{
  if (change.Path.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase))
  {
    var args = new FileDeletedWarningEventArgs();
    args.Path = change.Path;
    FileDeletedWarningEvent(this, args);
  }
}

程式碼只會檢查是否有.jpg 副檔名,使用的項目和如果有會產生相同的 UI 事件中**[圖 4**。

結論

一次的基本程式碼的位置,有幾個值得考量可協助您確定新的應用程式會在執行盡可能順暢的項目。

試驗您的組態 ! 這是建議的最重要,讓您建立追蹤檔案系統變更的應用程式。寫入記錄檔之前取得編碼太深,發生在您的裝置上的快速應用程式。寫出暫存檔案,並嘗試快速地刪除它們,例如內建的攝影機稍早所討論的嘗試大量元件。我甚至已了解在現實世界,其中應用程式所寫出不完整的檔案,關閉控制代碼,然後立即重新開啟完成寫入至磁碟的檔案控制代碼的情況。使用變更追蹤的應用程式,您不只是打算開始看到所有的變更,但可能會取得這些作業的中間。請務必優良的系統,這表示,了解並接受其他的應用程式執行的嘗試。

請記住,因為它的電源已關閉,而在 FAT 檔案系統上沒有任何日誌功能,可以從裝置移除 SD 卡,變更追蹤無法保證 SD 卡上跨開機工作階段。如果您的企業具有非常嚴格的要求,確保檔案未遭竄改,請考慮使用行動裝置管理原則強制寫入內部儲存體加密的數位相機影像。這可確保資料受到保護靜止和檔案的所有變更都計算都在內的。

總結

這樣就行了。檢查應用程式現在就會自動新增檔案,當它們在系統上,建立和刪除其中之一時通知使用者。雖然看似簡單,啟用這項體驗可讓使用者專注於其操作,而不是挑選運用系統選擇器經驗的縮圖。如果偵測器會不斷拍照相似的設備,自動在報表中包含新映像可以大幅縮減成本企業時間和金錢的錯誤。最重要的是,它可以協助您脫穎而出之間的欄位太過擁擠的企業應用程式適用於 Windows 的應用程式。


Adam Wilson是 Windows 開發人員生態系統與平台小組的專案經理使用的 Windows 索引子,並推播通知。 您可以連線到他Adam.D.Wilson@microsoft.com

感謝下列 Microsoft 技術專家檢閱這份文件︰ Brendan Flynn、 Mary 安妮 Noskowski 和 Kyle Yuan