2013-04-22 4 views
6

Мое приложение требует данных из базы данных sqlite. Он будет поставляться с версией этой базы данных, но мне нужно обновлять ее на регулярной основе (чаще всего раз в месяц). Как правило, я отправлял обновления для других частей своего приложения в виде XML через кучу веб-сервисов, которые я создал, но эта конкретная база данных, над которой я работаю сейчас, довольно большой (около 20-30 МБ) m получать ошибки таймаута, когда я пытаюсь отправить его таким образом.Обновление базы данных sqlite без XML

Я попытался поместить базу данных на сервер своих компаний, а затем загрузить ее в объект NSData. Затем я сохранил этот объект данных в каталоге Документов приложения. Когда я перезапускаю приложение, я получаю сообщение об ошибке «Файл по пути не является базой данных 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. 

Существует ли стандартная процедура обновления большой базы данных, которую приложение будет использовать? У меня возникает ощущение, что то, что я пытаюсь сделать, неверно и что есть совершенно другой способ сделать это, но я не могу понять, что.


UPDATE: Я попробовал несколько вещей, чтобы попытаться найти проблему, но ничего не получает мне ближе. Я помещаю базу данных, которую я пытаюсь загрузить с моего сервера на USB-накопитель, а затем на Mac, который я разрабатываю, и в папку «Документы» для моего приложения в Simulator. Когда я запускаю свое приложение, передавая базу данных таким образом, он работает без каких-либо проблем, и я могу видеть все свои данные.

И наоборот, я полностью удалил свое приложение с симулятора и перезапустил его, чтобы он создавал мою базу данных с нуля. Я передал эту недавно созданную базу данных с моего mac на мой сервер с USB-накопителем. После того, как файл был на моем сервере, я попытался запустить мое «обновление загрузки» в приведенном выше блоке кода, но он по-прежнему сбой при следующем запуске моего приложения с ошибкой «Файл по пути не является ошибкой базы данных SQLite» сообщение.

Из этих тестов я уверен, что проблема не в базе данных, которую я пытаюсь загрузить. Что-то в моем «загружаемом обновлении» кода искажает базу данных, но я до сих пор не смог понять, почему, и я не нашел приемлемого альтернативного метода для получения моих обновлений для моих пользователей после запуска моего приложения.


UPDATE 2: Я делал несколько больше поиска, и я узнал, что пользователю нужно будет ввести в имя пользователя и пароль, чтобы получить доступ к любому файлу на моем сервере. Теперь я верю, что NSData Я возвращаюсь из своего кода выше - это вызов аутентификации или что-то, связанное с этим, и поэтому, когда я сохраняю его с помощью NSFileManager, он не распознается как SQL-база данных.

Простейшее решение, которое я нашел для проверки подлинности, - here. Когда я пытаюсь сделать NSData *fileData = [NSData dataWithContentsOfURL:url options:NSDataReadingMapped error:&error]; (после изменения моего url, как предложено в ссылке, конечно), я получаю сообщение об ошибке NSCocoaErrorDomain Code=256, что является просто неизвестной ошибкой. Мой fileData - nil после этого. Я заметил, что эта почта довольно старая, поэтому я не знаю, поддерживается ли она.

Я также нашел другое решение проблемы аутентификации here с использованием NSURLConnection вместо dataWithContentsOfURL. Это гораздо более свежий, но в этом примере используется еще более синтаксис, который я раньше не видел. Я смог в значительной степени скопировать-вставить из примера, и мой проект будет строиться без ошибок, но я не знаю правильного синтаксиса для использования процедуры fetchURL:withCompletion:failure: из моего UIViewController.Я пробовал

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

, но это дает ошибку сборки для отправки несовместимого указателя. Я думаю, что я не могу пройти прямо NSData и NSError как ExampleDelegateSuccess и ExampleDelegateFailure объектов, соответственно, хотя это то, что они typedef из. Я, вероятно, должен что-то сделать для fileData и error, чтобы преобразовать их, но это typedef ing является одним из «синтаксиса расширенного поиска». Я имел в виду выше, и я действительно не знаю, что делать.

Это не включено в его пример, но я нашел еще один метод NSURLConnectionDelegate под названием connection:didReceiveAuthenticationChallenge:here, который, я думаю, могу просто добавить в код из примера, и это должно решить мою проблему аутентификации. Я чувствую, что если бы я просто знал, как позвонить fetchURL, я бы установил. Кто-нибудь имеет какие-либо советы о том, что мне нужно сделать, чтобы получить fileData и error в этот звонок?

ответ

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

+0

Надеюсь, это сработает. – HalR

+0

+1: хорошее предложение - разбить его на куски, может помочь ... –

1

Является ли это полностью новой/отдельной базой данных каждый раз или является обновленной версией одной базы данных?

Если последнее, вы считаете, что отправляете только изменения (либо через явный список добавлений/обновлений/удалений, либо используя что-то вроде Zumero для автоматизации обновлений)?

+0

Это обновленная версия, но будет много записей, которые изменились с последней версии. Кажется, я помню, как я читал где-то, что обновление отдельных полей таким образом было бы очень дорогостоящим, и, как правило, не рекомендуется, но я могу ошибаться. Я не знаком с Зумеро, я займусь этим. – GeneralMike

+0

+1: хорошее предложение: отправка только изменений может означать, что MBs сэкономить на загрузке по сравнению с отправкой такой большой базы данных целиком снова ... –

+0

Это, как правило, стоит больше (CPU и файловая система ввода/вывода) для обновления «a пучок "записей против оптовой замены, НО" кучка "может быть довольно большим процентом. Вы должны это измерить. Отправляя дельта, почти всегда будет меньше сетевого трафика, хотя и устройства iOS, как правило, находятся на медленных сотовых сетях гораздо больше, чем другие платформы, поэтому расходы большего количества процессоров на обработку 20-секундной загрузки будут намного быстрее, чем затраты на процессор 30 секунд скачать. Снова я бы попросил некоторые тесты (включая включение/выключение WiFi, включение/выключение LTE и т. Д.) – Stripes

4

Я думаю, что ваша проблема связана с необходимостью аутентификации на вашем сервере.

Я рекомендую популярную библиотеку AFNetworking для помощи в создании HTTP-вызовов. Для предоставления данных аутентификации вы должны подклассифицировать AFHTTPClient, как в this answer.

Тогда в вашем случае вы можете использовать AFXMLRequestOperation, чтобы загрузить XML напрямую. Затем вы можете записать его на диск.Обратите внимание, что вам нужно будет прочитать по адресу iOS Data Storage Guidelines, чтобы узнать, подходит ли папка «Документы» для вашего использования (что, я думаю, не так, кэш, вероятно, будет лучше).

Теперь причина вашего 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

+0

Отличный ответ. Часть о блоках - это то, что я искал. Я не могу подтвердить, что это работает пока, но, поскольку щедрость, вероятно, истечет, прежде чем я смогу снова взглянуть на нее, я вручу ее вам сейчас. Возможно, позже у меня появятся дополнительные вопросы. – GeneralMike

+0

Рад, что я мог помочь. Определенно быть в контакте, если он не работает должным образом, и я счастлив работать с ним вместе с вами. –

+0

Вы определенно поставили меня на правильный путь. Я закончил работу с полным 180 и отошел от кода «ПримерDelegate» (он создавал другие ошибки позже по строке) и вместо этого использовал AFNetworking. Поскольку я не хотел делать загрузку XML, мне приходилось делать что-то по-другому, чем вы предлагали (см. Мой ответ). Спасибо за вашу помощь! – GeneralMike

Смежные вопросы