使用 FSCopyObjectAsync 显示文件复制进度

发布于 2024-09-25 08:30:43 字数 4318 浏览 5 评论 0原文

经过多次搜索后发现,在尝试进行文件复制并显示相对于已复制文件量的进度指示器时,似乎存在一个常见问题。在花了相当多的时间试图解决这个问题之后,我发现自己再次受到 StackOverflow 诸神的摆布:-) - 希望有一天我也能成为那些能够帮助新手的人之一!

我试图获取一个进度条来显示复制过程的状态,一旦复制过程完成,就调用 Cocoa 方法。挑战 - 我需要使用文件管理器 Carbon 调用,因为 NSFileManager 没有提供我所需的全部功能。

我首先尝试利用 Matt Long 网站上的代码 可可是我的女朋友。代码让我有了很好的距离。我设法让文件复制进度正常工作。栏更新并且(在 Apple 文档中进行了一些额外的搜索)我发现了如何判断文件复制过程是否已完成...

if (stage == kFSOperationStageComplete)

但是,我还有最后一个障碍这比我现在的跳跃要大一点。我不知道如何将对象引用传递到回调中,也不知道如何在完成后从回调中调用 Cocoa 方法。这是我Carbon的极限->可可->碳的理解。博客上的一条评论说

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

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

在正常的可可中方法:

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 方法

如下总是,非常感谢您的帮助(希望答案能够解决我在许多线程中看到的许多问题和投诉!!)

It appears after much searching that there seems to be a common problem when trying to do a file copy and show a progress indicator relative to the amount of the file that has been copied. After spending some considerable time trying to resolve this issue, I find myself at the mercy of the StackOverflow Gods once again :-) - Hopefully one day I'll be among those that can help out the rookies too!

I am trying to get a progress bar to show the status of a copy process and once the copy process has finished, call a Cocoa method. The challenge - I need to make use of File Manager Carbon calls because NSFileManager does not give me the full ability I need.

I started out by trying to utilize the code on Matt Long's site Cocoa Is My Girlfriend. The code got me some good distance. I managed to get the file copy progress working. The bar updates and (with some additional searching within Apple docs) I found out how to tell if the file copy process has finished...

if (stage == kFSOperationStageComplete)

However, I have one last hurdle that is a little larger than my leap right now. I don't know how to pass an object reference into the callback and I don't know how to call a Cocoa method from the callback once finished. This is a limit of my Carbon -> Cocoa -> Carbon understanding. One of the comments on the blog said

"Instead of accessing the progress indicator via a static pointer, you can just use the void *info field of the FSFileOperationClientContext struct, and passing either the AppDelegate or the progress indicator itself."

Sounds like a great idea. Not sure how to do this. For the sake of everyone else that appears to bump into this issue and is coming from a non-Carbon background, based mostly upon the code from Matt's example, here is some simplified code as an example of the problem...

In a normal cocoa method:

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);
}

Then the callback (in the same file)

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...
}

} 

So the bottom line is how can I:

  1. Pass a pointer to an instance of a progress bar from the calling method to the callback
  2. Upon completion, call back out to a normal Cocoa method

And as always, help is much appreciated (and hopefully the answer will solve many of the issues and complaints I have seen in many threads!!)

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

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

发布评论

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

评论(1

流心雨 2024-10-02 08:30:43

您可以通过使用 FSCopyObjectAsync() 的最后一个参数来执行此操作,该参数是 FSFileOperationClientContext 类型的结构。该结构的字段之一是 info,它是一个 void* 参数,您基本上可以根据需要使用它。无论您分配给传递给 FSCopyObjectAsync() 的结构体字段的内容,都将依次传递给您的回调函数,作为最后一个 info 函数参数。 void* 可以是任何东西,包括指向对象的指针,因此您可以使用它来传递要处理回调的对象的实例。

设置代码如下所示:

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);

然后,在回调函数中:

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];
}

You can do this by using the last parameter to FSCopyObjectAsync(), which is a struct of type FSFileOperationClientContext. One of the fields of that struct is info, which is a void* parameter that you can basically use as you see fit. Whatever you assign to that field of the struct you pass into FSCopyObjectAsync() will be passed in turn to your callback function as the last info function parameter there. A void* can be anything, including a pointer to an object, so you can use that to pass the instance of your object that you want to handle the callback.

The setup code would look like this:

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);

Then, in your callback function:

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