2013-04-22 132 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