2013-04-22 134 views
6

我的应用程序需要来自sqlite数据库的数据。它将附带该数据库的一个版本,但我需要定期更新(最有可能每月一次)。通常情况下,我一直在通过我设置的一堆web服务将XML应用程序的其他部分作为XML发送,但现在我正在使用的这个特定数据库非常大(大约20-30 MB)当我尝试以这种方式发送超时错误。更新没有XML的sqlite数据库

我试着把数据库放在我的公司服务器上,然后将它下载到一个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数据库。

我发现通过身份验证的最简单的解决方案是here。当我尝试执行NSData *fileData = [NSData dataWithContentsOfURL:url options:NSDataReadingMapped error:&error];(当然,如链接所示,更改我的url后),我得到一个错误NSCocoaErrorDomain Code=256,这只是未知错误。这样做后我的fileDatanil。我注意到这篇文章很旧,所以我不知道它是否仍然支持。

我还发现了不同的解决方案,以鉴定问题here使用的NSURLConnection代替dataWithContentsOfURL。这是最近的,但是在那个例子中使用了一些更先进的语法,这是我以前从未见过的。我能够从该示例中复制粘贴,并且我的项目将无错地构建,但我不知道使用我的UIViewController中的fetchURL:withCompletion:failure:例程的正确语法。我试过

NSError *error; 
NSData *fileData; 
ExampleDelegate *download = [[ExampleDelegate alloc] init]; 
[download fetchURL:url withCompletion:fileData failure:&error]; 

但是,这给发送一个不兼容的指针的构建错误。我认为我不能分别通过NSDataNSError作为ExampleDelegateSuccessExampleDelegateFailure对象,尽管它们分别是typedef。我可能不得不对fileDataerror进行转换,但这是我上面提到的“高级外观语法”之一,我不知道该怎么做。

它不包括在他的例子,但我发现了另一个NSURLConnectionDelegate方法叫connection:didReceiveAuthenticationChallenge:here,我认为我可以再补充到从示例代码,这应该解决我的身份验证问题。我感觉如果我只是知道如何打电话fetchURL我会被设置。有没有人有任何建议,我需要做什么来获得fileDataerror进入该调用?

回答

3

我最终最终使用了AFNetworking来解决我的问题,正如@silver_belt所建议的那样。我能够做到这一点,但没有对AFHTTPClient进行子类化(请参阅我的回答here,我作为此问题的后续文章发布)。

最后,我不得不#include "AFHTTPRequestOperation.h"在我的视图控制器的.h文件中,然后在.M我用

NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"ftp://myUsername:[email protected]/myfile.sqlite"]]; 
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; 
NSURL *path = [[[app applicationDocumentsDirectory] URLByAppendingPathComponent:@"myfile"] URLByAppendingPathExtension:@"sqlite"]; 
operation.outputStream = [NSOutputStream outputStreamWithURL:path append:NO]; 

[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) 
    { 
     NSLog(@"Successfully downloaded file to path: %@",path); 
    } 
           failure:^(AFHTTPRequestOperation *operation, NSError *error) 
    { 
     NSLog(@"Error in AFHTTPRequestOperation in clickedButtonAtIndex on FlightInfoController.m"); 
     NSLog(@"%@",error.description); 
    }]; 

[operation start]; 

当我真正想开始我的下载。希望这会让那些希望在未来做到这一点的人更容易!

3

我们有定期更新的大型数据库。我不确定“大”是多大,但我们正在更新兆字节的数据。我们更新我们的JSON数据。 JSON比XML少一点开销,并且有很多现成的库(如JSONKit)。我们有些用户的数据连接很差,有时会遇到困难。

我们一直在测试不同的更新方式。一种方法将数据分解为多个JSON文件。每个文件都分别下载,然后分开存储。使用多个文件的一个优点是,如果出现故障,您只需重新发送该特定文件。

此外,我们使用多个线程一次处理多达5个请求/下载。这需要更多的管理,但是可以帮助我们为用户提供更好的体验。 AFHTTPClient真的很擅长处理这些东西。我讨厌不使用它。

+0

感谢您的建议。我在这里看到很多关于JSON的内容,但还没有使用它 - 我会看看它(和你提到的AFHTTPClient)。我认为把我的数据库分成多个文件,但它保证不断增长,所以我应该把它分成多少部分是很难弄清的。我希望找到一条不同的路线,但这可能是唯一的途径。 – GeneralMike 2013-04-23 12:15:24

+0

我希望它适合你。 – HalR 2013-04-23 14:33:59

+0

+1:很好的建议 - 将它分解成碎片可能会有所帮助... – 2013-05-04 06:48:50

1

每次都是一个全新的独立数据库还是同一个数据库的更新版本?

如果是后者,是否考虑过只发送更改(通过明确的添加/更新/删除列表或使用类似Zumero的自动化更新)?

+0

这是一个更新的版本,但会有很多自上一版本以来已更改的记录。我相信我记得在某个地方读到这样更新个别字段的计算会非常昂贵,而且通常不鼓励,但我可能是错的。我不熟悉Zumero,我会研究这一点。 – GeneralMike 2013-04-30 19:34:17

+0

+1:很好的建议:只发送更改可能意味着MB的下载节省与整个发送这样的大型数据库再次... – 2013-05-04 06:50:18

+0

它确实倾向于花费更多(CPU和文件系统I/O)来更新“a一堆“与批发替代品,但”一堆“可能是一个相当大的比例。你应该测量它。发送增量几乎总是会减少网络流量,而iOS设备往往在慢速蜂窝网络上远远超过其他平台,所以花费更多的CPU来处理20秒的下载会感觉快很多,然后花费更少的CPU来处理30秒的下载。再次,我会敦促一些测试(包括WiFi开/关,LTE开/关等) – Stripes 2013-05-04 15:38:32

4

我认为你的问题源于需要与你的服务器验证。

我推荐流行的AFNetworking库来帮助进行HTTP调用。您需要子类AFHTTPClient以提供您的验证详细信息,如this answer

然后在你的情况下,你可以使用AFXMLRequestOperation直接下载你的XML。然后你可以将它写入磁盘。请注意,您需要仔细阅读iOS Data Storage Guidelines以查看Documents文件夹是否适合您的使用(我认为这不适用;缓存可能会更好)。现在

,您ExampleDelegate代码(这看起来像是来自here)的原因不能正常工作是ExampleDelegateSuccessExampleDelegateFailure是块类型,所以你不能传递你NSDataNSError *指针。您需要提供符合相应类型签名的块对象。

例如:

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]} 
]; 

块将为您提供所需的NSDataNSError的情况下,所以你并不需要声明自己。你可以了解更多关于块here

希望有帮助:D

+0

很好的答案。关于块的部分正是我正在寻找的。我无法证实这一点,但由于赏金在我再次看到它之前可能会过期,我现在将奖励给你。不过,我可能会在后面提出更多的问题。 – GeneralMike 2013-05-06 17:45:36

+0

很高兴我能帮到你。如果它没有按预期工作,肯定会联系,我很乐意与你一起工作。 – 2013-05-07 02:01:58

+0

你绝对会让我走上正轨。我最终做了一个完整的180,并从'ExampleDelegate'代码中移除(它在后面创建了其他错误),并使用AFNetworking代替。由于我不打算做一个XML下载,我不得不做一些与你所建议的不同的事情(请参阅我的答案)。感谢你的帮助! – GeneralMike 2013-05-08 17:54:52