如何从 OS X 应用程序中的 URL 下载文件?我是一个刚接触 Objective C 的 Unix C 和 Perl 程序员。我一直在阅读 URL Loading System Programming Guide ,它建议 NSURLSession 用于新的 OS X 应用程序。所以我更喜欢使用 NSURLSession 或 NSURLSeesionDownloadTask 的解决方案,但我对 NSURLDownload 或其他解决方案持开放态度。
我的第一次尝试是基于找到的一个例子 here
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");
示例代码(和我的版本)没有单独的委托(delegate)。 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 语句。我在模拟器中设置了断点,但从未到达那里。如何创建一个简单的“无委托(delegate)”文件下载方法?
因为我正在编写“命令行”OS X 应用程序,所以我没有 UIViewController 或其他现有的委托(delegate)结构。
关于如何进行的任何建议?我不需要下载进度条等,只需要“通过/失败”状态即可。
另外,我这里的示例是针对单个 PDF 文件的。我的应用程序有一组我想要下载的 URL。我不需要并行下载它们。我可以在循环中重复使用单个 NSURLSession 吗?
如有任何帮助,我们将不胜感激!
最佳答案
得到解决方案后忘记关闭它。正如 Zev 所指出的,我选择的 PDF 文件很大,需要更多时间来下载。 10 秒的“ sleep (10)”没有提供足够的时间来下载 39 MB 的文件。
通知应该用于指示 completionHandler 何时完成。但是现在,我正在使用一个简单的轮询循环。我改变了 sleep (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 设置它:
__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;
}
关于objective-c - 如何从 OS X 应用程序中的 URL 下载文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28418122/