【问题标题】:Recommended way to copy arbitrary files using Cocoa使用 Cocoa 复制任意文件的推荐方法
【发布时间】:2012-02-27 17:49:18
【问题描述】:

我需要将文件从一个 OS X 卷复制到另一个 OS X 卷。虽然 *.app 严格来说不是文件而是文件夹,但用户希望它们是一个单元。因此,如果用户选择了一个文件,应用程序不应显示其文件夹的内容,而是将其作为一个单元进行复制。

因此我问,是否存在使用纯 Cocoa 代码复制文件的推荐方法。

可选:哪个命令行工具提供帮助并且可以被 Cocoa 应用程序使用。

【问题讨论】:

    标签: objective-c macos cocoa


    【解决方案1】:

    NSFileManager是你的朋友:

    NSError *error = nil;
    if ([[NSFileManager defaultManager] copyItemAtPath:@"path/to/source" toPath:@"path/to/destination" error:&error])
    {
        // copy succeeded
    }
    else
    {
        // copy failed, print error
    }
    

    【讨论】:

    • 谢谢!除了这个事实:copyItemAtPath:toPath: error: aborts,如果存在某个目标文件。我应该先删除相同的目标文件吗?其他方法?
    • @SteAp 视情况而定。您可以使用 NSFileManager 进行删除、移动等操作,因此您可以自行决定该场景的适当操作。
    • 好的,那我没有忽略什么。另一个版本的 copyItemAtPath: with options 不存在。再次感谢!
    【解决方案2】:

    您也可以使用FSCopyObjectAsync 函数。您可以显示文件复制进度,也可以使用 FSCopyObjectAsync() 取消文件复制。
    看看FSFileOperation 示例代码。

    此示例展示了如何复制和移动文件和文件夹。它 显示了同步和异步(使用 CFRunLoop)的使用 FSFileOperation API。此外,它还显示路径和 FSRef API 的变体以及如何从回调中获取状态。这 API 在概念上类似于引入的 FSVolumeOperation API 在 Mac OS X 10.2 中。

    FSCopyObjectAsync 示例:

    #import <Cocoa/Cocoa.h>
    
    
    @interface AsyncCopyController : NSObject {
    
    }
    -(OSStatus)copySource : (NSString *)aSource ToDestination: (NSString *)aDestDir setDelegate : (id)object;
    //delegate method
    -(void)didReceiveCurrentPath : (NSString *)curremtItemPath bytesCompleted : (unsigned long long)floatBytesCompleted currentStageOfFileOperation : (unsigned long)stage;
    -(void)didCopyOperationComplete : (BOOL)boolean;
    -(void)didReceiveCopyError : (NSString *)Error;
    -(void)cancelAllAsyncCopyOperation;
    @end
    
     #import "AsyncCopyController.h"
    
    static Boolean copying= YES;
    @implementation AsyncCopyController
    
    
    static void statusCallback (FSFileOperationRef fileOp,
                                const FSRef *currentItem,
                                FSFileOperationStage stage,
                                OSStatus error,
                                CFDictionaryRef statusDictionary,
                                void *info )
    {
    
        NSLog(@"Callback got called. %ld", error);
    
        id delegate;
        if (info)
            delegate = (id)info;
        if (error!=0) {
            if (error==-48) {
                [delegate didReceiveCopyError:@"Duplicate filename and version or Destination file already exists or File found instead of folder"];
            }   
    
    
    
        }
        CFURLRef theURL = CFURLCreateFromFSRef( kCFAllocatorDefault, currentItem );
    
        NSString* currentPath = [(NSURL *)theURL path];
    //  NSLog(@"currentPath %@", currentPath);
        // 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);
    
            if (info)
                [delegate didReceiveCurrentPath :currentPath bytesCompleted :floatBytesCompleted currentStageOfFileOperation:stage];
    
        }
        NSLog(@"stage  %d", stage);
        if (stage == kFSOperationStageComplete) {
    
            NSLog(@"Finished copying the file");
            if (info)
            [delegate didCopyOperationComplete:YES];
    
            // Would like to call a Cocoa Method here...
        }
        if (!copying) {
            FSFileOperationCancel(fileOp);
        }
    
    } 
    
    
    -(void)cancelAllAsyncCopyOperation
    {
        copying = NO;
    }
    
    
    
    -(OSStatus)copySource : (NSString *)aSource ToDestination: (NSString *)aDestDir setDelegate : (id)object
    {
    
        NSLog(@"copySource");
        copying = YES;
        CFRunLoopRef runLoop = CFRunLoopGetCurrent();
        NSLog(@"%@", runLoop);
        FSFileOperationRef fileOp = FSFileOperationCreate(kCFAllocatorDefault);
        require(fileOp, FSFileOperationCreateFailed);
        OSStatus status = FSFileOperationScheduleWithRunLoop(fileOp, 
                                                             runLoop, kCFRunLoopDefaultMode);
        if (status) {
            NSLog(@"Failed to schedule operation with run loop: %@", status);
            return status;
        }
        require_noerr(status, FSFileOperationScheduleWithRunLoopFailed);
    
        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
        // because I needed to use the kFSPathMakeRefDefaultOptions
        // to deal with file paths to remote folders via a /Volume reference
    
        status = FSPathMakeRefWithOptions((const UInt8 *)[aSource fileSystemRepresentation],
                                 kFSPathMakeRefDefaultOptions, 
                                 &source, 
                                 NULL);
    
        require_noerr(status, FSPathMakeRefWithOptionsaSourceFailed);
        Boolean isDir = true;
    
        status = FSPathMakeRefWithOptions((const UInt8 *)[aDestDir fileSystemRepresentation],
                                 kFSPathMakeRefDefaultOptions, 
                                 &destination, 
                                 &isDir);
        require_noerr(status, FSPathMakeRefWithOptionsaDestDirFailed);
        // Needed to change from the original to use CFStringRef so I could convert
        // from an NSString (aDestFile) to a CFStringRef (targetFilename)
    
        FSFileOperationClientContext    clientContext;
    
    
        // The FSFileOperation will copy the data from the passed in clientContext so using
        // a stack based record that goes out of scope during the operation is fine.
        if (object)
        {
            clientContext.version = 0;
            clientContext.info = (void *) object;
            clientContext.retain = CFRetain;
            clientContext.release = CFRelease;
            clientContext.copyDescription = CFCopyDescription;
        }
    
    
        // Start the async copy.
    
        status = FSCopyObjectAsync (fileOp,
                                    &source,
                                    &destination, // Full path to destination dir
                                    NULL,// Use the same filename as source
                                    kFSFileOperationDefaultOptions,
                                    statusCallback,
                                    1.0,
                                    object != NULL ? &clientContext : NULL);
    
        //CFRelease(fileOp);
        NSLog(@"Failed to begin asynchronous object copy: %d", status);
    
        if (status) {
    
            NSString * errMsg = [NSString stringWithFormat:@" - %@", status];
    
            NSLog(@"Failed to begin asynchronous object copy: %d", status);
        }
        if (object)
        {
            [object release];
        }
    FSFileOperationScheduleWithRunLoopFailed:
        CFRelease(fileOp);
    FSPathMakeRefWithOptionsaSourceFailed:
    FSPathMakeRefWithOptionsaDestDirFailed:
    FSFileOperationCreateFailed:
        return status;
    
    }
    
    @end  
    

    FSCopyObjectAsync 在 OS X v10.8 中已弃用

    copyfile(3) 是 FSCopyObjectAsync 的替代方案。 Here 是带有进度回调的 copyfile(3) 示例。

    【讨论】:

    • FSCopyObjectAsync 未被弃用。
    • 不,绝对不是。但是还没有看到如此大量的参考......
    • 看看this的实现。
    • 感谢您的出色回答。你在头文件中声明的其他方法中做了什么?像 didReceiveCurrentPath?想要实现这个只是想确保我没有错过任何东西。谢谢
    • @Fabiosoft 也许stackoverflow.com/questions/10414802/… 会有所帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-26
    • 1970-01-01
    相关资源
    最近更新 更多