使用FSCopyObjectAsync显示文件复制进度

时间:2010-09-24 04:39:30

标签: objective-c cocoa macos-carbon nsfilemanager nsprogressindicator

在大量搜索之后,在尝试执行文件复制并显示相对于已复制文件数量的进度指示器时似乎存在常见问题。在花了一些相当长的时间试图解决这个问题后,我发现自己再次受到StackOverflow众神的支配:-) - 希望有一天我会成为那些可以帮助新手的人之一!

我正在尝试获取进度条以显示复制过程的状态,并且在复制过程完成后,调用Cocoa方法。挑战 - 我需要使用文件管理器碳调用,因为NSFileManager不能提供我所需的全部功能。

我开始试图利用Matt Long网站Cocoa Is My Girlfriend上的代码。代码让我有了一些很好的距离。我设法让文件复制进度正常工作。栏更新和(在Apple文档中进行一些额外搜索)我发现如何判断文件复制过程是否已完成...

if (stage == kFSOperationStageComplete)

然而,我有一个最后一个障碍,比我现在的飞跃大一点。我不知道如何将对象引用传递给回调,我不知道如何在回调完成后调用Cocoa方法。这是我的碳限制 - >可可 - >碳理解。其中一条评论在博客上说

  

“不是通过静态指针访问进度指示器,而是可以使用FSFileOperationClientContext结构的void * info字段,并传递AppDelegate或进度指示器本身。”

听起来很棒。不知道该怎么做。为了其他人似乎遇到了这个问题并且来自非碳背景,主要基于Matt示例中的代码,这里有一些简化的代码作为问题的一个例子......

在正常的可可方法中:

CFRunLoopRef runLoop = CFRunLoopGetCurrent();
FSFileOperationRef fileOp = FSFileOperationCreate(kCFAllocatorDefault);

OSStatus status = FSFileOperationScheduleWithRunLoop(fileOp, 
                     runLoop, kCFRunLoopDefaultMode);

if (status) {
    NSLog(@"Failed to schedule operation with run loop: %@", status);
    return NO;
}

// Create a filesystem ref structure for the source and destination and 
// populate them with their respective paths from our NSTextFields.

FSRef source;
FSRef destination;

// Used FSPathMakeRefWithOptions instead of FSPathMakeRef which is in the 
// original example because I needed to use the kFSPathMakeRefDefaultOptions
// to deal with file paths to remote folders via a /Volume reference

FSPathMakeRefWithOptions((const UInt8 *)[aSource fileSystemRepresentation],
    kFSPathMakeRefDefaultOptions, 
    &source, 
    NULL);

Boolean isDir = true;

FSPathMakeRefWithOptions((const UInt8 *)[aDestDir fileSystemRepresentation],
    kFSPathMakeRefDefaultOptions, 
    &destination, 
    &isDir);

// Needed to change from the original to use CFStringRef so I could convert
// from an NSString (aDestFile) to a CFStringRef (targetFilename)

CFStringRef targetFilename = (CFStringRef)aDestFile;

// Start the async copy.

status = FSCopyObjectAsync (fileOp,
             &source,
             &destination, // Full path to destination dir
             targetFilename,
             kFSFileOperationDefaultOptions,
             statusCallback,
             1.0,
             NULL);

CFRelease(fileOp);

if (status) {

    NSString * errMsg = [NSString stringWithFormat:@"%@ - %@", 
                           [self class], status];

        NSLog(@"Failed to begin asynchronous object copy: %@", status);
}

然后是回调(在同一个文件中)

static void statusCallback (FSFileOperationRef fileOp,
           const FSRef *currentItem,
           FSFileOperationStage stage,
           OSStatus error,
           CFDictionaryRef statusDictionary,
           void *info )
{

    NSLog(@"Callback got called.");

    // If the status dictionary is valid, we can grab the current values to 
    // display status changes, or in our case to update the progress indicator.

    if (statusDictionary)
    {

        CFNumberRef bytesCompleted;

        bytesCompleted = (CFNumberRef) CFDictionaryGetValue(statusDictionary,
                 kFSOperationBytesCompleteKey);

        CGFloat floatBytesCompleted;
        CFNumberGetValue (bytesCompleted, kCFNumberMaxType, 
                              &floatBytesCompleted);

        NSLog(@"Copied %d bytes so far.", 
                              (unsigned long long)floatBytesCompleted);

        // fileProgressIndicator is currently declared as a pointer to a 
        // static progress bar - but this needs to change so that it is a 
        // pointer passed in via the controller. Would like to have a 
        // pointer to an instance of a progress bar

        [fileProgressIndicator setDoubleValue:(double)floatBytesCompleted];
        [fileProgressIndicator displayIfNeeded];
     }

if (stage == kFSOperationStageComplete) {

    NSLog(@"Finished copying the file");

    // Would like to call a Cocoa Method here...
}

} 

所以底线是我如何:

  1. 将指针从调用方法传递到进度条的实例到回调
  2. 完成后,回拨一个正常的Cocoa方法
  3. 和往常一样,非常感谢帮助(希望答案能解决我在许多主题中看到的许多问题和抱怨!)

1 个答案:

答案 0 :(得分:7)

您可以使用FSCopyObjectAsync()的最后一个参数来执行此操作,FSFileOperationClientContextinfo类型的结构。该结构的一个字段是FSCopyObjectAsync(),这是一个void *参数,您可以根据需要使用它。无论你分配给结构域的那个字段,你传递给info都将依次传递给你的回调函数作为那里的最后一个FSFileOperationClientContext clientContext = {0}; //zero out the struct to begin with clientContext.info = myProgressIndicator; //All the other setup code status = FSCopyObjectAsync (fileOp, &source, &destination, // Full path to destination dir targetFilename, kFSFileOperationDefaultOptions, statusCallback, 1.0, &clientContext); 函数参数。 void *可以是任何东西,包括指向对象的指针,因此您可以使用它来传递您想要处理回调的对象实例。

设置代码如下所示:

static void statusCallback (FSFileOperationRef fileOp,
       const FSRef *currentItem,
       FSFileOperationStage stage,
       OSStatus error,
       CFDictionaryRef statusDictionary,
       void *info )
{
    NSProgressIndicator* fileProgressIndicator = (NSProgressIndicator*)info;
    [fileProgressIndicator setDoubleValue:(double)floatBytesCompleted];
    [fileProgressIndicator displayIfNeeded];
}

然后,在你的回调函数中:

{{1}}