使用 NSMetadataQuery 和 NSPredicate 返回目录列表

发布于 2024-12-11 17:09:23 字数 2296 浏览 0 评论 0原文

我正在尝试获取用户 iCloud 文件夹中的目录列表。我研究出了如何查找特殊类型的文件(例如 txt 文件),并且效果很好:

NSMetadataQuery *query = [[NSMetadataQuery alloc] init];
_query = query;

//Search all files in the Documents directories of the application’s iCloud container directories:
[query setSearchScopes:[NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]]; 

NSPredicate *pred = [NSPredicate predicateWithFormat:@"%K ENDSWITH '.txt'", NSMetadataItemFSNameKey];

[query setPredicate:pred];
[query startQuery];

但是,我现在尝试仅获取目录。我已阅读有关 NSPredicate,但我不知道如何寻找目录。我猜 NSPredicate 不是为此而设计的?我可以检查某个东西是否是这样的目录:

    BOOL isDir;
BOOL exists = [fm fileExistsAtPath:path isDirectory:&isDir];
if (exists) {
    /* file exists */
    if (isDir) {
        /* file is a directory */
    }
 }

但是如何将其应用于 NSMetadataQuery,我不知道。如果我能得到任何帮助,我将不胜感激。


编辑:

我将谓词更改为 [NSPredicate predicateWithFormat:@"%K.pathExtension = ''", NSMetadataItemFSNameKey];

然后我确定查询何时完成,如下所示:

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(queryDidFinishGathering:) name:NSMetadataQueryDidFinishGatheringNotification object:query];
[query startQuery];

- (void)queryDidFinishGathering:(NSNotification *)notification {

NSMetadataQuery *query = [notification object];
[query disableUpdates]; // You should invoke this method before iterating over query results that could change due to live updates.
[query stopQuery]; // You would call this function to stop a query that is generating too many results to be useful but still want to access the available results.

[self loadData:query];
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSMetadataQueryDidFinishGatheringNotification object:query];
_query = nil; // we're done with it

}

最后,我进行计数,但这总是给我 0,无论我在云中有多少个目录(我通过 Lion 和 Mobile Documents 文件夹检查了这一点;例如,我有 Documents/myTXTs 等)。这很奇怪。如果我对文本文件进行计数,它会给出 4,因为我有 4 个 txt 文件。所以我猜我的目录没有被计算在内:

 - (void)loadData:(NSMetadataQuery *)query {

    NSLog(@"Query count %i", [query resultCount]);

...

I'm trying to get a list of directories in the user's iCloud folder. I worked out how to look for special types of files (such as txt files) and it works fine:

NSMetadataQuery *query = [[NSMetadataQuery alloc] init];
_query = query;

//Search all files in the Documents directories of the application’s iCloud container directories:
[query setSearchScopes:[NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]]; 

NSPredicate *pred = [NSPredicate predicateWithFormat:@"%K ENDSWITH '.txt'", NSMetadataItemFSNameKey];

[query setPredicate:pred];
[query startQuery];

However, I'm now trying to get only directories. I've read through the docs concerning NSPredicate, but I have no clue how to go about looking for directories. I guess NSPredicate is not made for this? I can check if something is a directory like this:

    BOOL isDir;
BOOL exists = [fm fileExistsAtPath:path isDirectory:&isDir];
if (exists) {
    /* file exists */
    if (isDir) {
        /* file is a directory */
    }
 }

But how to apply this to a NSMetadataQuery, I have no clue. I'd be grateful for any help I can get.


EDIT:

I changed the predicate to [NSPredicate predicateWithFormat:@"%K.pathExtension = ''", NSMetadataItemFSNameKey];

I then determine when the query is finished like so:

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(queryDidFinishGathering:) name:NSMetadataQueryDidFinishGatheringNotification object:query];
[query startQuery];

- (void)queryDidFinishGathering:(NSNotification *)notification {

NSMetadataQuery *query = [notification object];
[query disableUpdates]; // You should invoke this method before iterating over query results that could change due to live updates.
[query stopQuery]; // You would call this function to stop a query that is generating too many results to be useful but still want to access the available results.

[self loadData:query];
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSMetadataQueryDidFinishGatheringNotification object:query];
_query = nil; // we're done with it

}

And finally, I do a count, but this will always give me 0 however many directories I have in the cloud (I checked this via Lion and the Mobile Documents folder; e.g. I have Documents/myTXTs etc.). This is very strange. If I do a count on the text files, it will give me 4 as I have 4 txt files. So I guess my directories are not being counted:

 - (void)loadData:(NSMetadataQuery *)query {

    NSLog(@"Query count %i", [query resultCount]);

...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

风月客 2024-12-18 17:09:23

我尝试过 EmptyStack 的答案,但没有运气......

我发现 NSMetadataQuery 找不到目录,问题不在于 NSPredicate正在从结果中过滤目录,问题只是查询找不到文件夹。我尝试使用调试器,发现找到的所有 LBItem 都是常规文件而不是目录。我希望苹果将来能改进这一点。
也许在 MountainLion 中这更好,因为他们似乎有文件夹支持......哎呀,当然你没有在这里读到这篇文章,因为它仍然是保密协议。算了

但是,至少有一种解决此问题的方法:

例如,如果您的文件结构是;

|_Folder
| |_file
| |_file2
|_Folder1
| |_file
|_file

我们用谓词进行查询:

predicateWithFormat:@"%K LIKE '*'", NSMetadataItemFSNameKey

结果将是(使用NSMetadataItemURLKey来获取URL)

path/to/iCloud/Documents/Folder/file
path/to/iCloud/Documents/Folder/file2
path/to/iCloud/Documents/Folder1/file
path/to/iCloud/Documents/file

我们需要做的是通过查看每个URL的组成部分的数量并进行截断来自己过滤这些结果根据需要提供 URL。第一级的项目应该有 numberOfComponentsOfUbiquitousContainer + 1,下一级甚至会有一个,依此类推。
我们还需要丢弃重复项(或者像在本示例中一样,我们将获得 Folder 两次)。

可能不是最佳的,但对我有用,

更新:

更好地使用下面的谓词,它在爬网子文件夹时减少查询结果。

// dirPath is an NSString path of the directory you want
// to look at. Usually, for the top level directory:
// [NSFileManager URLForUbiquityContainerIdentifier:nil].path;
predicateWithFormat:@"%K BEGINSWITH %@", NSMetadataItemPathKey, dirPath];

I've tried EmptyStack's answer without luck....

I've found that NSMetadataQuery does NOT find directories, The problem is not the NSPredicate is filtering directories from the results, the problem is simply the query don't find folders. I've tried with the debugger and found that all LBItems found, are of regular files not directories. I hope Apple improves this in the future.
Maybe in MountainLion this is better since they seem to have folder support ... oops off course you didn't read this here because it's still NDA. Forget it

However there is at least one way of working-around this:

For example if your file structure is;

|_Folder
| |_file
| |_file2
|_Folder1
| |_file
|_file

and we make a query with predicate:

predicateWithFormat:@"%K LIKE '*'", NSMetadataItemFSNameKey

results will be (use NSMetadataItemURLKey to get the URL)

path/to/iCloud/Documents/Folder/file
path/to/iCloud/Documents/Folder/file2
path/to/iCloud/Documents/Folder1/file
path/to/iCloud/Documents/file

What we need to do is to filter these results by ourselves by looking at the number of components of each URL and chopping URLs as needed. Items at the first level should have numberOfComponentsOfUbiquitousContainer + 1, next level would have even one more and so on.
We need to discard repetitions too (or like in this example we will get Folder twice).

Probably not optimal but works for me,

Update:

Better use below predicate, it reduce query results when crawling sub folders.

// dirPath is an NSString path of the directory you want
// to look at. Usually, for the top level directory:
// [NSFileManager URLForUbiquityContainerIdentifier:nil].path;
predicateWithFormat:@"%K BEGINSWITH %@", NSMetadataItemPathKey, dirPath];
还如梦归 2024-12-18 17:09:23

如果您只想获取目录,请使用以下谓词。

predicateWithFormat:@"%K.pathExtension = ''", NSMetadataItemFSNameKey

请注意,这并不能保证您使用此方法只能获得目录,因为有(我见过一些)没有扩展名的文件。但如果您确定所有文件都有扩展名,那么您可以很好地使用它。

如果您想获取所有文件而不考虑其扩展名,只需将 NOT 关键字添加到上面的谓词中,

predicateWithFormat:@"NOT %K.pathExtension = ''", NSMetadataItemFSNameKey

If you want to get only the directories use the following predicate.

predicateWithFormat:@"%K.pathExtension = ''", NSMetadataItemFSNameKey

Note that, this doesn't guarantee that you will get only the directories by using this method as there are (I've seen some) files which has no extensions. But if you are sure that all your files has extensions then you can very well use this.

If you want to get all the files regardless of their extension, just add the NOT keyword to the above predicate,

predicateWithFormat:@"NOT %K.pathExtension = ''", NSMetadataItemFSNameKey
猫七 2024-12-18 17:09:23

您可以使用 MDQuery 来完成此操作 - 这是 NSMetadataquery 使用的“真实”代码。

在终端中,输入“mdls”,然后拖入文件夹。然后输入 mdfind 并使用提供的示例,仅将 kMDItemContentType 类型树更改为 public.folder - 您将获得大量结果。

然后,要在代码中执行此操作,您需要遵循 MDQueryRef 示例。代码流程与 Cocoa 的内容相同,而且我发现查询更容易创建和理解。您可以将 cocoa 回调设置为 MDQuery - 因此它很好地与 NSMetadataquery 兼容。

NSMetadataQuery 有一些 iCloud 的东西,我认为 MDQuery 中没有。所以我认为它们并不相同。

You can do this with MDQuery - which is the 'real' code that NSMetadataquery uses.

In Terminal, type 'mdls' then drag in a folder. Then type mdfind and use the provided example only change the kMDItemContentType type tree to public.folder - you will get lots of results.

Then to do this in code, you need to follow an MDQueryRef example. The code flow is identical to the Cocoa stuff, and I find the queries much easier to create and understand. You can set cocoa callbacks to the MDQuery - so its pretty well drop in compatible with NSMetadataquery.

NSMetadataQuery has some iCloud stuff that I don't think is in MDQuery. So they are not identical, I think.

木有鱼丸 2024-12-18 17:09:23

试试这个:

[query setPredicate:[NSPredicate predicateWithFormat:@"%K like '*.txt'", NSMetadataItemFSNameKey]];

它对我有用。

Try this:

[query setPredicate:[NSPredicate predicateWithFormat:@"%K like '*.txt'", NSMetadataItemFSNameKey]];

It works for me.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文