如何从OS X应用程序中的URL下载文件?我是一名Unix C和Perl程序员,他是Objective C的新成员。我一直在阅读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");
示例代码(和我的版本)没有单独的委托。 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吗?
任何帮助都将不胜感激!!
答案 0 :(得分:1)
一旦我得到解决方案,忘了关闭它。正如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;
}