推荐的方法来复制任意文件使用Cocoa [英] Recommended way to copy arbitrary files using Cocoa

查看:208
本文介绍了推荐的方法来复制任意文件使用Cocoa的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



因此,我要求,如果存在

解决方案

您还可以使用 FSCopyObjectAsync 函数。您可以显示文件复制进度,也可以使用FSCopyObjectAsync()取消文件复制。

查看 FSFileOperation 示例代码。


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


FSCopyObjectAsync的示例:

  #import< Cocoa / Cocoa.h> 


@interface AsyncCopyController:NSObject {

}
- (OSStatus)copySource:(NSString *)aSource ToDestination :( NSString *)aDestDir setDelegate :(id)object;
//委托方法
- (void)didReceiveCurrentPath:(NSString *)curremtItemPath bytesCompleted:(unsigned long long)floatBytesCompleted currentStageOfFileOperation:(unsigned long)stage;
- (void)didCopyOperationComplete:(BOOL)boolean;
- (void)didReceiveCopyError:(NSString *)Error;
- (void)cancelAllAsyncCopyOperation;
@end

#importAsyncCopyController.h

static Boolean copy = YES;
@implementation AsyncCopyController


static void statusCallback(FSFileOperationRef fileOp,
const FSRef * currentItem,
FSFileOperationStage阶段,
OSStatus错误,
CFDictionaryRef statusDictionary,
void * info)
{

NSLog(@Callback gets called。%ld,error);

id delegate;
if(info)
delegate =(id)info;
if(error!= 0){
if(error == - 48){
[delegate didReceiveCopyError:@重复的文件名和版本或目标文件已经存在或找到文件而不是文件夹];
}



}
CFURLRef theURL = CFURLCreateFromFSRef(kCFAllocatorDefault,currentItem);

NSString * currentPath = [(NSURL *)theURL path];
// NSLog(@currentPath%@,currentPath);
//如果状态字典有效,我们可以获取当前值为
//显示状态更改,或者在我们的情况下更新进度指示器。

if(statusDictionary)
{

CFNumberRef bytesCompleted;

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

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

// NSLog(@到目前为止已复制%d字节。,
//(unsigned long long)floatBytesCompleted);

if(info)
[delegate didReceiveCurrentPath:currentPath bytesCompleted:floatBytesCompleted currentStageOfFileOperation:stage];

}
NSLog(@stage%d,stage);
if(stage == kFSOperationStageComplete){

NSLog(@完成复制文件);
if(info)
[delegate didCopyOperationComplete:YES];

//想在这里调用Cocoa方法...
}
if(!copy){
FSFileOperationCancel(fileOp);
}

}


- (void)cancelAllAsyncCopyOperation
{
copy = NO;
}



- (OSStatus)copySource:(NSString *)aSource ToDestination:(NSString *)aDestDir setDelegate:(id)object
{

NSLog(@copySource);
copied = YES;
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
NSLog(@%@,runLoop);
FSFileOperationRef fileOp = FSFileOperationCreate(kCFAllocatorDefault);
require(fileOp,FSFileOperationCreateFailed);
OSStatus status = FSFileOperationScheduleWithRunLoop(fileOp,
runLoop,kCFRunLoopDefaultMode);
if(status){
NSLog(@无法使用运行循环调度操作:%@,status);
return status;
}
require_noerr(status,FSFileOperationScheduleWithRunLoopF​​ailed);

if(status){
NSLog(@无法使用运行循环调度操作:%@,status);
// return NO;
}

//为源和目标创建文件系统引用结构,
//用我们的NSTextFields中的相应路径填充它们。

FSRef source;
FSRef destination;

//使用FSPathMakeRefWithOptions而不是FSPathMakeRef
//因为我需要使用kFSPathMakeRefDefaultOptions
//通过/卷引用处理远程文件夹的文件路径

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);
//需要改变从原来的使用CFStringRef所以我可以将
//从NSString(aDestFile)转换为CFStringRef(targetFilename)

FSFileOperationClientContext clientContext;


// FSFileOperation将从传递的clientContext中复制数据,所以使用
//在操作过程中超出作用域的基于堆栈的记录。
if(object)
{
clientContext.version = 0;
clientContext.info =(void *)object;
clientContext.retain = CFRetain;
clientContext.release = CFRelease;
clientContext.copyDescription = CFCopyDescription;
}


//启动异步复制。

status = FSCopyObjectAsync(fileOp,
& source,
& destination,//到目的地的完整路径dir
NULL,//使用相同的文件名source
kFSFileOperationDefaultOptions,
statusCallback,
1.0,
object!= NULL?& clientContext:NULL);

// CFRelease(fileOp);
NSLog(@无法开始异步对象复制:%d,status);

if(status){

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

NSLog(@无法开始异步对象复制:%d,status);
}
if(object)
{
[object release];
}
FSFileOperationScheduleWithRunLoopF​​ailed:
CFRelease(fileOp);
FSPathMakeRefWithOptionsaSourceFailed:
FSPathMakeRefWithOptionsaDestDirFailed:
FSFileOperationCreateFailed:
return status;

}

@end

FSCopyObjectAsync在OS X v10.8中已弃用



copyfile(3)是FSCopyObjectAsync的替代方法。 此处是带有进度回调的copyfile(3)的示例。


I need to copy file from one OS X volume to another OS X volume. While an *.app isn't strictly speaking a file but a folder, user expect them to be a unit. Thus, if user selects a file, the app should not show its folder's contents, but copy it as a unit.

Therefore I ask, if there exists a recommended way to copy files using pure Cocoa code.

Optional: Which command line tool provides help and could be utilized by a Cocoa application.

解决方案

You can also use FSCopyObjectAsync function. You can display file copy progress and you can also cancel file copy using FSCopyObjectAsync().
Take a look at FSFileOperation example code.

This sample shows how to copy and move both files and folders. It shows both the synchronous and asynchronous (using CFRunLoop) use of the FSFileOperation APIs. In addition, it shows path and FSRef variants of the API and how to get status out of the callbacks. The API is conceptually similar to the FSVolumeOperation APIs introduced in Mac OS X 10.2.

Example of 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 is Deprecated in OS X v10.8

copyfile(3) is alternative for FSCopyObjectAsync. Here is example of copyfile(3) with Progress Callback.

这篇关于推荐的方法来复制任意文件使用Cocoa的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆