macOS 监听文件夹里文件的状态

2023-03-12  本文已影响0人  木马不在转
工具作用:

FSEventStreamTool:用于监听某个文件夹里所有文件的操作回调,比如删除、移动、编辑等.多用于NSDocument文档项目的文档状态监听.

FSEventStreamTool的实现

.h文件

/// 文件里的操作类型
typedef NS_ENUM(NSInteger, FSEventStreamType) {
    /// 有文件创建
    FSEventStreamTypeCreate = 0,
    /// 有文件移动
    FSEventStreamTypeMV,
    /// 有文件移进来了
    FSEventStreamTypeMoveIn,
    /// 有文件移出去了
    FSEventStreamTypeMoveOut,
    /// 有文件删除了
    FSEventStreamTypeRemove,
    /// 有文件修改了
    FSEventStreamTypeModify
};
NS_ASSUME_NONNULL_BEGIN

/// 文件监听工具
@interface FSEventStreamTool : NSObject

/// 监听文件夹
/// @param paths 文件路径集合
/// @param callback 回调
+ (FSEventStreamTool *)monitorFileWithPaths:(NSArray <NSString *>*)paths
                    callback:(void(^)(FSEventStreamType type, NSString *path))callback;

/// 取消监听
- (void)cancelMonitor;
@end

.m文件

#import "FSEventStreamTool.h"
#import <CoreServices/CoreServices.h>

@interface FSEventStreamTool ()
@property(nonatomic, assign) NSInteger syncEventID;
@property(nonatomic, assign) FSEventStreamRef syncEventStream;
@property (nonatomic, copy) void(^callback)(FSEventStreamType type, NSString *path);
@end
@implementation FSEventStreamTool

/// 监听文件夹
/// @param paths 文件路径集合
/// @param callback 回调
+ (FSEventStreamTool *)monitorFileWithPaths:(NSArray <NSString *>*)paths
                                   callback:(void(^)(FSEventStreamType type, NSString *path))callback {
    FSEventStreamTool *tool = [[FSEventStreamTool alloc] initWithPaths:paths];
    tool.callback = callback;
    return tool;
}
- (instancetype)initWithPaths:(NSArray *)paths{
    if (self = [super init]) {
        
        if(self.syncEventStream) {
            FSEventStreamStop(self.syncEventStream);
            FSEventStreamInvalidate(self.syncEventStream);
            FSEventStreamRelease(self.syncEventStream);
            self.syncEventStream = NULL;
        }
        FSEventStreamContext context;
        context.info = (__bridge void * _Nullable)(self);
        context.version = 0;
        context.retain = NULL;
        context.release = NULL;
        context.copyDescription = NULL;
        self.syncEventStream = FSEventStreamCreate(NULL, &fsevents_callback, &context, (__bridge CFArrayRef _Nonnull)(paths), self.syncEventID, 1, kFSEventStreamCreateFlagFileEvents | kFSEventStreamCreateFlagUseCFTypes);
        FSEventStreamScheduleWithRunLoop(self.syncEventStream, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
        FSEventStreamStart(self.syncEventStream);
    }
    return self;
}

/// 取消监听
- (void)cancelMonitor {
    
    if(self.syncEventStream) {
        FSEventStreamStop(self.syncEventStream);
        FSEventStreamInvalidate(self.syncEventStream);
        FSEventStreamRelease(self.syncEventStream);
        self.syncEventStream = NULL;
        self.callback = nil;
    }
}

#pragma mark - private method
-(void)updateEventID {
    self.syncEventID = FSEventStreamGetLatestEventId(self.syncEventStream);
}
#pragma mark - setter
-(void)setSyncEventID:(NSInteger)syncEventID{
    [[NSUserDefaults standardUserDefaults] setInteger:syncEventID forKey:@"SyncEventID"];
}
-(NSInteger)syncEventID {
    NSInteger syncEventID = [[NSUserDefaults standardUserDefaults] integerForKey:@"SyncEventID"];
    if(syncEventID == 0) {
        syncEventID = kFSEventStreamEventIdSinceNow;
    }
    return syncEventID;
}
void fsevents_callback(ConstFSEventStreamRef streamRef,
                       void *userData,
                       size_t numEvents,
                       void *eventPaths,
                       const FSEventStreamEventFlags eventFlags[],
                       const FSEventStreamEventId eventIds[]) {
    
    FSEventStreamTool *streamTool = (__bridge FSEventStreamTool *)userData;
    if (!streamTool.callback) {return;}
    NSArray *pathArr = (__bridge NSArray*)eventPaths;
    FSEventStreamEventId lastRenameEventID = 0;
    NSString* lastPath = nil;
    
    for(int i=0; i<numEvents; i++){
        FSEventStreamEventFlags flag = eventFlags[i];
        NSString* currentPath = pathArr[i];
        if(kFSEventStreamEventFlagItemCreated & flag) {
            streamTool.callback(FSEventStreamTypeCreate,currentPath);
        }
        if(kFSEventStreamEventFlagItemRenamed & flag) {
            FSEventStreamEventId currentEventID = eventIds[i];
            if (currentEventID == lastRenameEventID + 1) {
                // 重命名或者是移动文件
                streamTool.callback(FSEventStreamTypeMV,currentPath);
            } else {
                // 其他情况, 例如移动进来一个文件, 移动出去一个文件, 移动文件到回收站
                if ([[NSFileManager defaultManager] fileExistsAtPath:currentPath]) {
                    // 移动进来一个文件
                    streamTool.callback(FSEventStreamTypeMoveIn,currentPath);
                } else {
                    // 移出一个文件
                    streamTool.callback(FSEventStreamTypeMoveOut,currentPath);
                }
            }
            lastRenameEventID = currentEventID;
            lastPath = currentPath;
        }
        if(kFSEventStreamEventFlagItemRemoved & flag) {
            streamTool.callback(FSEventStreamTypeRemove,currentPath);
        }
        if(kFSEventStreamEventFlagItemModified & flag) {
            streamTool.callback(FSEventStreamTypeModify,currentPath);
        }
    }
    [streamTool updateEventID];
}
@end
使用方法
    self.tool = [FSEventStreamTool monitorFileWithPaths:@[@"/Users/xxx/Desktop/macOS/"] callback:^(FSEventStreamType type, NSString *path) {
        NSLog(@"变化类型:%ld 变化的文件路径%@",type,path);
    }];
上一篇下一篇

猜你喜欢

热点阅读