繁体   English   中英

如何从OS X app中的URL下载文件

[英]How to download a file from a URL in OS X app

如何从OS X应用程序中的URL下载文件? 我是Objective C新手的Unix C和Perl程序员。我一直在阅读URL加载系统编程指南 ,它建议NSURLSession用于新的OS X应用程序。 所以我更喜欢使用NSURLSession或NSURLSeesionDownloadTask的解决方案,但我愿意接受NSURLDownload或其他解决方案。

我的第一次尝试是基于这里找到的一个例子

        NSURLSessionDownloadTask *downloadTask =
        [[NSURLSession sharedSession]
         downloadTaskWithURL:[NSURL URLWithString:pdfFile]
         completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {

             // move tmp file to permanent location
             NSLog(@"Done...");
             NSLog(@"Location:   %@", location);
             NSLog(@"Response:   %@", response);
             NSLog(@"Error       %@", error);

             NSFileManager *fileManager = [NSFileManager defaultManager];
             BOOL fileCopied = [fileManager moveItemAtPath:[location path] toPath:[appDir stringByAppendingString:@"/demo.pdf"] error:&error];
             NSLog(fileCopied ? @"File Copied OK" : @"ERROR Copying file.");
            }];
    [downloadTask resume];
    NSLog(@"Now we are here");

示例代码(和我的版本)没有单独的委托。 completionHandler是“在线”。 (不确定这是否是正确的术语)。 我假设在下载任务完成后将执行此代码。 那是对的吗?

我的第二次尝试是基于URL编程指南的“下载文件”部分:

    // PDF file Download, try #2
    NSError *error = nil;
    NSString *appDir = NSHomeDirectory();
    NSString *pdfFile = @"https://developer.apple.com/library/mac/documentation/IDEs/Conceptual/AppDistributionGuide/AppDistributionGuide.pdf";

    // Configure Cache behavior for default session
    NSURLSessionConfiguration
    *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSString *cachePath = [NSTemporaryDirectory()
                           stringByAppendingPathComponent:@"/pdfDownload.cache"];
    NSLog(@"Cache path: %@\n", cachePath);
    NSURLCache *myCache = [[NSURLCache alloc] initWithMemoryCapacity: 16384
                                                        diskCapacity: 268435456 diskPath: cachePath];
    defaultConfigObject.URLCache = myCache;
    defaultConfigObject.requestCachePolicy = NSURLRequestUseProtocolCachePolicy;
    defaultConfigObject.timeoutIntervalForRequest = 100;
    defaultConfigObject.timeoutIntervalForResource = 100;

    // Create a delegate-Free Session
    NSURLSession *delegateFreeSession =
        [NSURLSession sessionWithConfiguration: defaultConfigObject
                                      delegate: nil
                                 delegateQueue: [NSOperationQueue mainQueue]];

    [[delegateFreeSession downloadTaskWithURL:[NSURL URLWithString:pdfFile] completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {

        // move tmp file to permanent location
        NSLog(@"Done...");
        NSLog(@"Location:   %@", location);
        NSLog(@"Response:   %@", response);
        NSLog(@"Error       %@", error);

        NSFileManager *fileManager = [NSFileManager defaultManager];
        BOOL fileCopied = [fileManager moveItemAtPath:[location path] toPath:[appDir stringByAppendingString:@"/demo.pdf"] error:&error];
        NSLog(fileCopied ? @"File Copied OK" : @"ERROR Copying file.");
    }] resume];

    sleep(10);
    NSLog(@"After sleep...");

在这两种情况下,代码执行永远不会到达作为completionHandler一部分的NSLog语句。 我在模拟器中设置断点,永远不会到达那里。 如何创建简单的“委托免费”文件下载方法?

由于我正在编写一个“命令行”OS X应用程序,因此我没有UIViewController或其他现有的委托结构。

有关如何进行的任何建议? 我不需要下载进度条等,只需要“通过/失败”状态。

另外,我的例子是单个PDF文件。 我的应用程序有一系列我想下载的网址。 我不需要并行下载它们。 我可以在循环中重用一个NSURLSession吗?

任何帮助,将不胜感激!!

一旦得到解决方案,忘了关闭它。 正如Zev指出的那样,我选择的PDF文件很大,需要更多时间下载。 10秒'睡眠(10)'没有足够的时间下载39 MB文件。

应使用通知来指示completionHandler何时完成。 但就目前而言,我正在使用一个简单的轮询循环。 我改变了睡眠(10);

int  timeoutCounter = 60;
while ((timeoutCounter > 0) && !downloadDone) {
    sleep(1);
    NSLog(@"Timout: %d", timeoutCounter--);
}

if (!downloadDone) {
    NSLog(@"ERROR: Timeout occurred before file completed downloading");
}

这需要声明downloadDone以允许completionHandler块设置它:

    __block BOOL downloadDone = FALSE;

在NSFileManger移动文件后,必须设置此信号量:

     downloadDone = TRUE;

再次感谢Zev的帮助!

我仍在进行错误检查/处理,但这里是完整的更正方法:

+ (NSError *)           downloadFileAtURL:(NSString *)fileURL
                                toDir:(NSString *)toDir
                              timeout:(NSNumber *)timeout {

// completion handler semaphore to indicate download is done
__block BOOL downloadDone = FALSE;

// same PDF file name for later
NSString *downloadFileName = [fileURL lastPathComponent];
NSString *finalFilePath = [toDir stringByAppendingPathComponent:downloadFileName];
NSLog(@"PDF file name: '%@'", downloadFileName);
NSLog(@"Final file path: '%@'", finalFilePath);

// Configure Cache behavior for default session
NSURLSessionConfiguration
*defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSString *cachePath = [NSTemporaryDirectory()
                       stringByAppendingPathComponent:@"/downloadFileCache"];
NSLog(@"Cache path: %@\n", cachePath);
NSURLCache *myCache = [[NSURLCache alloc] initWithMemoryCapacity: 16384
                                                    diskCapacity: 268435456 diskPath: cachePath];
defaultConfigObject.URLCache = myCache;
defaultConfigObject.requestCachePolicy = NSURLRequestUseProtocolCachePolicy;
defaultConfigObject.timeoutIntervalForRequest = 100;
defaultConfigObject.timeoutIntervalForResource = 100;

// Create a delegate-Free Session
//      delegateQueue: [NSOperationQueue mainQueue]];
NSURLSession *delegateFreeSession =
[NSURLSession sessionWithConfiguration: defaultConfigObject
                              delegate: nil
                         delegateQueue: nil];

[[delegateFreeSession downloadTaskWithURL:[NSURL URLWithString:fileURL] completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {

    // move tmp file to permanent location
    NSLog(@"In the completion handler...");
    NSLog(@"Location:   %@", location);
    NSLog(@"Response:   %@", response);
    NSLog(@"Error       %@", error);
    NSLog(@"Location path: %@", [location path]);

    // Verify temp file exists in cache
    NSFileManager *fileManager = [[NSFileManager alloc] init];
    if ( [fileManager fileExistsAtPath:[location path]]) {
        NSLog(@"File exists.  Now move it!");

        error = nil;
        BOOL fileCopied = [fileManager moveItemAtPath:[location path]
                                               toPath:finalFilePath
                                                error:&error];
        NSLog(@"Error: %@", error);
        NSLog(fileCopied ? @"File Copied OK" : @"ERROR Copying file.");
    }
    downloadDone = TRUE;

}] resume];

// use timeout argument
int  timeoutCounter = [timeout intValue];
while ((timeoutCounter > 0) && !downloadDone) {
    sleep(1);
    NSLog(@"Timout: %d", timeoutCounter--);
}

if (!downloadDone) {
    NSLog(@"ERROR: Timeout occurred before file completed downloading");
}

NSError *error = nil;
return error;

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM