建议使用Cocoa复制任意文件的方法

时间:2012-02-27 17:49:18

标签: objective-c macos cocoa

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

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

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

2 个答案:

答案 0 :(得分:11)

NSFileManager是你的朋友:

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

答案 1 :(得分:6)

您还可以使用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  

在OS X v10.8中不推荐使用FSCopyObjectAsync

copyfile(3)是FSCopyObjectAsync的替代方案。 Here是带有Progress Callback的copyfile(3)的示例。

相关问题