在 UWP 中快速访问文件属性

了解如何从库中收集文件及其属性的列表,然后在应用中使用这些属性。  

先决条件

  • 通用 Windows 平台 (UWP) 应用的异步编程 若要了解如何使用 C# 或 Visual Basic 编写异步应用,请参阅使用 C# 或 Visual Basic 调用异步 API。 若要了解如何使用 C++ 编写异步应用,请参阅使用 C++ 进行异步编程。 
  • 库的访问权限 这些示例中的代码需要 picturesLibrary 功能,但是文件位置可能需要其他功能或根本不需要任何功能。 要了解详细信息,请参阅文件访问权限。 
  • 简单文件枚举 本示例使用 QueryOptions 来设置几个高级枚举属性。 要详细了解如何只获取一个较小目录的简单文件列表,请参阅枚举和查询文件和文件夹。 

用法

许多应用都需要列出一组文件的属性,但不必始终直接与这些文件交互。 例如,一个音乐应用一次播放(打开)一个文件,但它需要一个文件夹中所有文件的属性,以便该应用显示歌曲队列,从而让用户能够选择有效的文件来播放。

本页上的示例不应该用于会修改每个文件元数据的应用,或会与产生的所有 StorageFile 交互而不只是读取其属性的应用。 请参阅枚举和查询文件和文件夹了解详细信息。 

枚举某个位置的所有图片

在本示例中,我们将

  • 构造一个 QueryOptions 对象来指示应用需要尽快枚举文件。
  • 通过将 StorageFile 对象分页到应用中来获取文件属性。 将文件分页可减少应用使用的内存,并实现用户能感知的响应速度提升。

创建查询

为了构建查询,我们使用 QueryOptions 对象指定应用仅枚举特定类型的图像文件而筛选掉 Windows 信息保护 (System.Security.EncryptionOwners) 所保护的文件。 

请务必设置应用将使用 QueryOptions.SetPropertyPrefetch 访问的属性。 如果应用访问未预取的属性,会明显影响性能。

设置 IndexerOption.OnlyUseIndexerAndOptimzeForIndexedProperties 即可通知系统尽快返回结果,但返回的结果仅包括在 SetPropertyPrefetch 中指定的属性。

结果中的分页

用户的图片库中可能有数千或数以百万计的文件,因此调用 GetFilesAsync 会大量占用其计算机资源,因为它会为每个图像创建 StorageFile。 这个问题可以这样解决:一次只创建固定数量的 StorageFile、处理它们并显示到 UI,然后释放内存。 

本例中的方法是:使用 StorageFileQueryResult.GetFilesAsync (UInt32 maxNumberOfItems UInt32 StartIndex),每次只获取 100 个文件。 然后,应用将处理这些文件,并允许操作系统在此之后释放内存。 这种方法限制了应用能使用的最大内存,从而确保系统保持一定的响应能力。 当然,你需要根据应用场景调整返回的文件数量,但要确保所有用户的响应体验,建议每次获取的文件数不超过 500 个。

示例  

StorageFolder folderToEnumerate = KnownFolders.PicturesLibrary; 
// Check if the folder is indexed before doing anything. 
IndexedState folderIndexedState = await folderToEnumerate.GetIndexedStateAsync(); 
if (folderIndexedState == IndexedState.NotIndexed || folderIndexedState == IndexedState.Unknown) 
{ 
    // Only possible in indexed directories.  
    return; 
} 
 
QueryOptions picturesQuery = new QueryOptions() 
{ 
    FolderDepth = FolderDepth.Deep, 
    // Filter out all files that have WIP enabled
    ApplicationSearchFilter = "System.Security.EncryptionOwners:[]", 
    IndexerOption = IndexerOption.OnlyUseIndexerAndOptimizeForIndexedProperties 
}; 

picturesQuery.FileTypeFilter.Add(".jpg"); 
string[] otherProperties = new string[] 
{ 
    SystemProperties.GPS.LatitudeDecimal, 
    SystemProperties.GPS.LongitudeDecimal 
}; 
 
picturesQuery.SetPropertyPrefetch(PropertyPrefetchOptions.BasicProperties | PropertyPrefetchOptions.ImageProperties, 
                                    otherProperties); 
SortEntry sortOrder = new SortEntry() 
{ 
    AscendingOrder = true, 
    PropertyName = "System.FileName" // FileName property is used as an example. Any property can be used here.  
}; 
picturesQuery.SortOrder.Add(sortOrder); 
 
// Create the query and get the results 
uint index = 0; 
const uint stepSize = 100; 
if (!folderToEnumerate.AreQueryOptionsSupported(picturesQuery)) 
{ 
    log("Querying for a sort order is not supported in this location"); 
    picturesQuery.SortOrder.Clear(); 
} 
StorageFileQueryResult queryResult = folderToEnumerate.CreateFileQueryWithOptions(picturesQuery); 
IReadOnlyList<StorageFile> images = await queryResult.GetFilesAsync(index, stepSize); 
while (images.Count != 0 || index < 10000) 
{ 
    foreach (StorageFile file in images) 
    { 
        // With the OnlyUseIndexerAndOptimizeForIndexedProperties set, this won't  
        // be async. It will run synchronously. 
        var imageProps = await file.Properties.GetImagePropertiesAsync(); 
 
        // Build the UI 
        log(String.Format("{0} at {1}, {2}", 
                    file.Path, 
                    imageProps.Latitude, 
                    imageProps.Longitude)); 
    } 
    index += stepSize; 
    images = await queryResult.GetFilesAsync(index, stepSize); 
} 

结果

产生的 StorageFile 文件仅包含请求的属性,但与其他 IndexerOption 相比,返回速度快 10 倍。 该应用仍然可以请求对还未包含在查询中的属性进行访问,但是打开文件和检索这些属性不会造成性能下降。  

将文件夹添加到库

应用可以请求用户使用 StorageLibrary.RequestAddFolderAsync 将位置添加到索引。 包含该位置后,它将自动编入索引,应用可以使用此技术来枚举文件。  

请参阅

QueryOptions API 参考
枚举和查询文件和文件夹
文件访问权限