2013-04-22 46 views
6

Ứng dụng của tôi yêu cầu dữ liệu từ cơ sở dữ liệu sqlite. Nó sẽ xuất xưởng với một phiên bản của cơ sở dữ liệu này, nhưng tôi cần phải cập nhật nó một cách thường xuyên (rất có thể mỗi tháng một lần). Thông thường tôi đã gửi các bản cập nhật cho các phần khác của ứng dụng dưới dạng XML thông qua một loạt các dịch vụ web mà tôi đã thiết lập, nhưng cơ sở dữ liệu cụ thể mà tôi đang làm bây giờ khá lớn (khoảng 20-30 MB) và tôi ' m nhận được lỗi thời gian chờ khi tôi cố gắng gửi nó theo cách đó.Cập nhật cơ sở dữ liệu sqlite mà không cần XML

Tôi đã thử đặt cơ sở dữ liệu trên máy chủ công ty của mình rồi tải xuống đối tượng NSData. Sau đó tôi đã lưu đối tượng dữ liệu đó vào thư mục Tài liệu của ứng dụng của tôi. Khi tôi khởi động lại ứng dụng của mình, tôi nhận được thông báo lỗi "Tệp ở đường dẫn dường như không phải là cơ sở dữ liệu SQLite". Mã cho điều này là dưới đây.

// 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. 

Có quy trình chuẩn để cập nhật cơ sở dữ liệu lớn mà ứng dụng sẽ sử dụng không? Tôi có cảm giác rằng những gì tôi đang cố gắng làm là không chính xác và rằng có một cách hoàn toàn khác để làm điều này, nhưng tôi không thể hiểu được điều gì.


CẬP NHẬT: Tôi đã thử một vài cách để cố gắng tìm ra vấn đề, nhưng không có gì khiến tôi gần gũi hơn. Tôi đặt cơ sở dữ liệu Tôi đang cố tải xuống từ máy chủ của mình vào một ổ USB và sau đó vào mac tôi đang phát triển và vào thư mục Documents cho ứng dụng của tôi trong Trình mô phỏng. Khi tôi chạy ứng dụng của tôi bằng cách chuyển cơ sở dữ liệu theo cách này, nó chạy mà không có bất kỳ vấn đề gì và tôi có thể xem tất cả dữ liệu của mình.

Ngược lại, tôi đã xóa hoàn toàn ứng dụng của mình khỏi trình mô phỏng và chạy lại ứng dụng, vì vậy nó sẽ tạo cơ sở dữ liệu của tôi từ đầu. Tôi đã chuyển cơ sở dữ liệu mới được tạo từ máy mac của tôi lên máy chủ của tôi bằng ổ USB. Khi tệp đã ở trên máy chủ của tôi, tôi đã cố gắng chạy "bản cập nhật tải xuống" trong khối mã ở trên, nhưng nó vẫn bị lỗi trong lần tiếp theo tôi khởi động ứng dụng của mình với lỗi "Tệp ở đường dẫn không phải là cơ sở dữ liệu SQLite" thông điệp.

Từ các thử nghiệm này, tôi tự tin rằng vấn đề không phải với cơ sở dữ liệu mà tôi đang cố tải xuống. Cái gì đó trong mã "cập nhật tải xuống" của tôi đang làm hỏng cơ sở dữ liệu, nhưng tôi vẫn chưa thể tìm ra lý do, cũng như tôi không tìm thấy phương thức thay thế có thể chấp nhận để cập nhật cho người dùng của mình khi ứng dụng của tôi khởi chạy.


UPDATE 2: Tôi đã làm một số tìm kiếm nhiều hơn, và tôi đã học được rằng người dùng sẽ cần phải đặt trong một tên người dùng và mật khẩu để truy cập vào bất kỳ tập tin trên máy chủ của tôi. Bây giờ tôi tin rằng NSData Tôi nhận được từ mã của tôi ở trên thực sự là một thách thức xác thực, hoặc một cái gì đó liên quan đến điều đó, và đó là lý do tại sao khi tôi lưu nó với NSFileManager nó không được công nhận là một sqlite DB.

Giải pháp đơn giản nhất mà tôi đã nhận được thông qua xác thực là here. Khi tôi cố gắng thực hiện NSData *fileData = [NSData dataWithContentsOfURL:url options:NSDataReadingMapped error:&error]; (sau khi thay đổi số url như được đề xuất trong liên kết, tất nhiên), tôi gặp lỗi NSCocoaErrorDomain Code=256, đây chỉ là lỗi không xác định. My fileDatanil sau khi thực hiện việc này. Tôi đã nhận thấy bài đăng này khá cũ nên tôi không biết liệu bài đăng đó có được hỗ trợ hay không.

Tôi cũng tìm thấy giải pháp khác cho vấn đề xác thực here bằng cách sử dụng NSURLConnection thay vì dataWithContentsOfURL. Điều này là gần đây hơn nhiều, nhưng có một số cú pháp tìm kiếm nâng cao hơn được sử dụng trong ví dụ mà tôi chưa từng thấy trước đây. Tôi đã có thể sao chép khá nhiều từ ví dụ, và dự án của tôi sẽ xây dựng mà không có lỗi, nhưng tôi không biết cú pháp thích hợp để sử dụng quy trình fetchURL:withCompletion:failure: từ UIViewController của tôi.Tôi đã thử

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

nhưng điều đó tạo ra lỗi xây dựng khi gửi con trỏ không tương thích. Tôi nghĩ rằng tôi không thể chuyển thẳng một đối tượng NSDataNSError như ExampleDelegateSuccessExampleDelegateFailure tương ứng, mặc dù chúng là những gì được typedef chỉnh sửa. Tôi có thể phải làm một cái gì đó để fileDataerror để chuyển đổi chúng, nhưng typedef ing là một trong những "cú pháp tìm kiếm nâng cao" tôi đã đề cập ở trên, và tôi không thực sự biết phải làm gì.

Nó không được bao gồm trong ví dụ của anh ấy, nhưng tôi đã tìm thấy một phương pháp NSURLConnectionDelegate gọi là connection:didReceiveAuthenticationChallenge:here mà tôi nghĩ mình có thể thêm vào mã từ ví dụ và điều đó sẽ giải quyết vấn đề xác thực của tôi. Tôi cảm thấy nếu tôi chỉ biết cách gọi số fetchURL tôi sẽ được thiết lập. Có ai có lời khuyên nào về những gì tôi cần làm để nhận được fileDataerror vào cuộc gọi đó không?

Trả lời

3

Tôi cuối cùng đã kết thúc bằng cách sử dụng AFNetworking để giải quyết vấn đề của mình, như được đề xuất bởi @silver_belt. Tôi đã có thể làm điều đó mà không cần phân lớp AFHTTPClient mặc dù (xem câu trả lời của tôi here, mà tôi đã đăng để theo dõi câu hỏi này).

Cuối cùng, tôi chỉ phải #include "AFHTTPRequestOperation.h" trong file .h điều khiển quan điểm của tôi, sau đó trong .m tôi đã sử dụng

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

khi tôi thực sự muốn bắt đầu tải về của tôi. Hy vọng điều này sẽ làm cho nó dễ dàng hơn cho bất cứ ai tìm cách để làm điều này trong tương lai!

3

Chúng tôi có cơ sở dữ liệu lớn mà chúng tôi thường xuyên cập nhật. Tôi không chắc làm thế nào "lớn" là lớn, nhưng chúng tôi đang cập nhật megabyte dữ liệu. Chúng tôi Cập nhật dữ liệu của chúng tôi dưới dạng dữ liệu JSON. JSON có chi phí thấp hơn một chút so với XML và có rất nhiều thư viện có sẵn (như JSONKit). Một số người dùng của chúng tôi có kết nối dữ liệu kém và đôi khi khó khăn xảy ra.

Chúng tôi đã thử nghiệm các cách cập nhật khác nhau. Một phương thức tách dữ liệu thành nhiều tệp JSON. Mỗi tệp được tải xuống một cách riêng biệt và sau đó chúng được lưu trữ riêng biệt. Một lợi thế khi sử dụng nhiều tệp, là nếu một tệp không thành công, bạn chỉ cần gửi lại tệp cụ thể đó.

Ngoài ra, chúng tôi sử dụng nhiều luồng để xử lý tối đa 5 yêu cầu/lượt tải xuống cùng một lúc. Điều này đòi hỏi quản lý nhiều hơn, nhưng giúp chúng tôi cung cấp trải nghiệm tốt hơn cho người dùng. AFHTTPClient thực sự tốt trong việc xử lý những thứ này. Tôi ghét không sử dụng nó.

+0

Cảm ơn đề xuất. Tôi đã nhìn thấy rất nhiều về JSON ở đây, nhưng chưa sử dụng nó - Tôi sẽ xem xét nó (và AFHTTPClient bạn đã đề cập). Tôi xem xét việc phá vỡ cơ sở dữ liệu của mình thành nhiều tệp, nhưng nó được đảm bảo khá nhiều để tiếp tục phát triển, vì vậy chỉ cần bao nhiêu phần tôi nên cắt nó thành một chút khó khăn để tìm ra. Tôi đã hy vọng tìm được một con đường khác để đi, nhưng đây có thể là cách duy nhất. – GeneralMike

+0

Tôi hy vọng nó sẽ tốt cho bạn. – HalR

+0

+1: gợi ý tốt - chia nhỏ thành nhiều phần có thể giúp ... –

1

Đây có phải là cơ sở dữ liệu hoàn toàn mới/khác biệt mỗi lần hoặc là phiên bản cập nhật của cùng một cơ sở dữ liệu không?

Nếu sau này, bạn đã xem xét chỉ gửi các thay đổi (hoặc thông qua danh sách rõ ràng thêm/cập nhật/xóa hoặc sử dụng một cái gì đó như Zumero để tự động cập nhật)?

+0

Đây là phiên bản cập nhật nhưng sẽ có nhiều bản ghi đã thay đổi kể từ phiên bản trước. Tôi tin rằng tôi nhớ đọc một nơi nào đó mà cập nhật các lĩnh vực cá nhân theo cách này sẽ rất tốn kém tính toán, và nói chung là nản lòng, nhưng tôi có thể sai. Tôi không quen thuộc với Zumero, tôi sẽ xem xét điều đó. – GeneralMike

+0

+1: gợi ý tốt: chỉ gửi những thay đổi có thể có nghĩa là MBs tiết kiệm tải xuống so với gửi cơ sở dữ liệu lớn như vậy một lần nữa ... –

+0

Nó có xu hướng chi phí nhiều hơn (CPU & hệ thống tập tin I/O) để cập nhật "a bó "của các mục so với thay thế bán buôn, NHƯNG" một bó "có thể là một tỷ lệ phần trăm khá lớn. Bạn nên đo nó. Tuy nhiên, việc sử dụng các thiết bị iOS có xu hướng ở các mạng di động chậm hơn nhiều so với các nền tảng khác, do đó chi tiêu nhiều CPU hơn để xử lý việc tải xuống 20 giây sẽ nhanh hơn rất nhiều, sau đó chi tiêu ít CPU hơn để xử lý 30 giây tải xuống. Một lần nữa tôi sẽ yêu cầu một số thử nghiệm (bao gồm WiFi on/off, LTE on/off, vv) – Stripes

4

Tôi nghĩ rằng sự cố của bạn bắt nguồn từ việc yêu cầu xác thực với máy chủ của bạn.

Tôi khuyên bạn nên sử dụng thư viện AFNetworking phổ biến để được trợ giúp thực hiện cuộc gọi HTTP. Bạn muốn phân lớp AFHTTPClient để cung cấp chi tiết xác thực của bạn, như trong this answer.

Sau đó, trong trường hợp của bạn, bạn có thể sử dụng AFXMLRequestOperation để tải xuống XML trực tiếp. Sau đó bạn có thể ghi nó vào đĩa.Lưu ý bạn sẽ cần phải đọc qua iOS Data Storage Guidelines để xem thư mục Documents có phù hợp cho việc sử dụng của bạn (mà tôi không nghĩ là nó không; Caches có lẽ sẽ tốt hơn).

Bây giờ, lý do ExampleDelegate mã của bạn (mà hình như nó đến từ here) không hoạt động là ExampleDelegateSuccessExampleDelegateFailure là loại khối, vì vậy bạn không thể vượt qua NSDataNSError * của bạn gợi ý. Bạn cần cung cấp các đối tượng khối phù hợp với chữ ký của các loại tương ứng.

Ví dụ:

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

Các khối sẽ cung cấp cho bạn với NSDataNSError trường hợp theo quy định, do đó bạn không cần phải khai báo của riêng bạn. Bạn có thể tìm hiểu thêm về các khối here.

Hy vọng rằng sẽ giúp: D

+0

Câu trả lời hay. Phần về các khối chỉ là những gì tôi đang tìm kiếm. Tôi không thể xác minh rằng điều này hoạt động được nêu ra, nhưng kể từ khi tiền thưởng có thể sẽ hết hạn trước khi tôi có thể nhìn vào nó một lần nữa, tôi sẽ trao nó cho bạn ngay bây giờ. Tôi có thể có thêm câu hỏi về nó sau này mặc dù. – GeneralMike

+0

Vui vì tôi có thể giúp đỡ. Chắc chắn là liên lạc nếu nó không hoạt động như mong đợi và tôi rất sẵn lòng làm việc với bạn. –

+0

Bạn chắc chắn đã cho tôi đi đúng hướng. Tôi đã hoàn thành một 180 hoàn thành và di chuyển ra khỏi mã 'ExampleDelegate' (nó đã tạo ra các lỗi khác sau đó xuống dòng) và sử dụng AFNetworking thay thế. Vì tôi không tìm cách tải xuống XML, tôi phải làm những việc hơi khác một chút so với bạn đã đề xuất (xem câu trả lời của tôi). Cảm ơn tất cả sự giúp đỡ của bạn! – GeneralMike

Các vấn đề liên quan