我尝试将数据库放在我的公司服务器上,然后将其下载到NSData对象中.然后我将该数据对象保存到我的应用程序的Documents目录中.当我重新启动我的应用程序时,我收到一条错误消息“路径中的文件似乎不是sqlite数据库”.代码如下.
// Download data NSURL *url = [NSURL URLWithString:@"http://www.mysite.net/download/myDatabase.sqlite"]; NSData *fileData = [[NSData alloc] initWithContentsOfURL:url]; NSLog(@"%@",fileData); // Writes a bunch of 8 character (hex?) strings // Delete old database NSError *error; NSURL *destination = [app applicationDocumentsDirectory]; destination = [destination URLByAppendingPathComponent:@"myDatabase"]; destination = [destination URLByAppendingPathExtension:@"sqlite"]; NSLog(@"destination = %@",destination); [[NSFileManager defaultManager] removeItemAtPath:[destination path] error:&error]; // Save into correct location [fileData writeToURL:destination atomically:YES]; //[NSFileManager defaultManager] createFileAtPath:[destination path] contents:fileData attributes:nil]; // I also tried this,it doesn't work either.
是否有更新应用程序将使用的大型数据库的标准过程?我觉得我正在尝试做的是不正确的,并且有一种完全不同的方式来做到这一点,但我无法弄清楚是什么.
更新:
我尝试了几件事来试图找到问题,但没有什么能让我更接近.我把我想要从我的服务器上下载的数据库放到USB驱动器上,然后放到我正在开发的mac上,然后放入模拟器中我的应用程序的Documents文件夹中.当我通过这种方式传输数据库来运行我的应用程序时,它运行没有任何问题,我可以看到我的所有数据.
相反,我完全从模拟器中删除了我的应用程序并重新运行它,因此它将从头开始创建我的数据库.我使用USB驱动器将这个新制作的数据库从我的mac传输到我的服务器上.一旦文件在我的服务器上,我试图在上面的代码块中运行我的“下载更新”,但是下次我启动我的应用时它仍然会崩溃“路径中的文件似乎不是sqlite数据库”错误信息.
从这些测试中,我确信问题不在于我正在尝试下载的数据库.我的“下载更新”代码中的某些内容正在破坏数据库,但我仍然无法弄清楚原因,也没有找到一种可接受的替代方法,以便在我的应用启动后将更新发送给我的用户.
更新2:
我一直在做更多的搜索,我已经了解到用户需要输入用户名和密码才能访问我服务器上的任何文件.我现在相信我从上面的代码回来的NSData实际上是一个身份验证挑战,或者与之相关的东西,这就是为什么当我用NSFileManager保存它时它不会被识别为sqlite DB.
我发现通过身份验证的最简单的解决方案是here.当我尝试执行NSData * fileData = [NSData dataWithContentsOfURL:url options:NSDataReadingMapped error:& error]; (当然按照链接中的建议更改我的URL后),我收到错误NSCocoaErrorDomain Code = 256,这只是未知错误.这样做后我的fileData为零.我注意到这篇文章很老了,所以我不知道它是否仍然受到支持.
我还使用NSURLConnection而不是dataWithContentsOfURL找到了与验证问题here不同的解决方案.这是更近期的,但在该示例中使用了一些我以前没有见过的更先进的语法.我能够从示例中复制粘贴,我的项目将构建没有错误,但我不知道使用fetchURL的正确语法:withCompletion:failure:来自我的UIViewController的例程.我试过了
NSError *error; NSData *fileData; ExampleDelegate *download = [[ExampleDelegate alloc] init]; [download fetchURL:url withCompletion:fileData failure:&error];
但是这会导致发送不兼容指针的构建错误.我想我不能分别传递一个直接的NSData和NSError作为ExampleDelegateSuccess和ExampleDelegateFailure对象,即使它们是从typedefed中得到的.我可能不得不对fileData和错误做一些转换它们,但是typedefing是我上面提到的“高级外观语法”之一,我真的不知道该怎么做.
它没有包含在他的示例中,但是我找到了另一个名为connection:didReceiveAuthenticationChallenge:here的NSURLConnectionDelegate方法,我认为我可以添加到示例中的代码中,这应该可以解决我的身份验证问题.我觉得如果我知道如何调用fetchURL我会被设置.有没有人有什么建议我需要做什么来获取fileData和错误到该调用?
解决方法
我建议使用流行的AFNetworking库来获取HTTP调用的帮助.您可以将AFHTTPClient子类化为提供身份验证详细信息,如this answer所示.
然后在您的情况下,您可以使用AFXMLRequestOperation
直接下载XML.然后,您可以将其写入磁盘.请注意,您需要阅读iOS Data Storage Guidelines以查看Documents文件夹是否适合您的使用(我认为它不是;缓存可能会更好).
现在,您的ExampleDelegate代码(看起来它来自here)不起作用的原因是ExampleDelegateSuccess和ExampleDelegateFailure是块类型,因此您无法传递NSData和NSError *指针.您需要提供符合相应类型签名的块对象.
例如:
ExampleDelegate *download = [[ExampleDelegate alloc] init]; [download fetchURL:url withCompletion:^(NSData *thedata) { NSLog(@"Success! Data length: %d",theData.length); // Delete old database NSError *error; // You need to declare 'app' here so you can use it in the line below. NSURL *destination = [app applicationDocumentsDirectory]; destination = [destination URLByAppendingPathComponent:@"myDatabase"]; destination = [destination URLByAppendingPathExtension:@"sqlite"]; NSLog(@"destination = %@",destination); [[NSFileManager defaultManager] removeItemAtPath:[destination path] error:&error]; // Save into correct location BOOL success = [fileData writeToURL:destination atomically:YES]; NSLog(@"File written successfully? %d",success); } failure:^(NSError *theError){ NSLog(@"Failure: %@",[theError localizedDescription]} ];
这些块将根据需要为您提供NSData和NSError实例,因此您无需声明自己的实例.您可以了解有关块here的更多信息.
希望有所帮助:D