Как я могу проверить, что большой двоичный объект NSData действителен как возобновление данных для NSURLSessionDownloadTask?

У меня есть приложение, использующее фоновые загрузки с новыми NSURLSession API. Когда загрузка отменяется или завершается сбоем таким образом, что предоставляется NSURLSessionDownloadTaskResumeData, я сохраняю большой двоичный объект данных, чтобы его можно было возобновить позже. Очень небольшое количество времени я замечаю сбой в дикой природе:

Fatal Exception: NSInvalidArgumentException
Invalid resume data for background download. Background downloads must use http or https and must download to an accessible file.

Ошибка возникает здесь, где resumeData — это большой двоичный объект NSData, а session — экземпляр NSURLSession:

if (resumeData) {
    downloadTask = [session downloadTaskWithResumeData:resumeData];
    ...

Данные предоставляются API-интерфейсами Apple, сериализуются, а затем десериализуются в более поздний момент времени. Он может быть поврежден, но никогда не равен нулю (как проверяет оператор if).

Как я могу заранее проверить, что resumeData недействителен, чтобы не допустить сбоя приложения?


person Ian Terrell    schedule 20.02.2014    source источник


Ответы (3)


Это обходной путь, предложенный Apple:

- (BOOL)__isValidResumeData:(NSData *)data{
    if (!data || [data length] < 1) return NO;

    NSError *error;
    NSDictionary *resumeDictionary = [NSPropertyListSerialization propertyListWithData:data options:NSPropertyListImmutable format:NULL error:&error];
    if (!resumeDictionary || error) return NO;

    NSString *localFilePath = [resumeDictionary objectForKey:@"NSURLSessionResumeInfoLocalPath"];
    if ([localFilePath length] < 1) return NO;

    return [[NSFileManager defaultManager] fileExistsAtPath:localFilePath];
}

Редактировать (iOS 7.1 больше не является NDA): я получил это из обмена Twitter с инженером Apple, он предложил, что делать, и я написал приведенную выше реализацию.

person rustyshelf    schedule 03.03.2014
comment
Поскольку 7.1 больше не находится под соглашением о неразглашении, не могли бы вы указать мне источник слухов? У нас сейчас много сбоев в 7.1, и try/catch, который отлично работал в 7.0, больше не работает. - person Soph; 17.03.2014
comment
Источником был обмен сообщениями в Твиттере между мной и инженером Apple. Это был его последний твит: twitter.com/atnan/status/431571791799005184 - person rustyshelf; 17.03.2014
comment
Спасибо, что нашли этот обходной путь. Это довольно плохой API от Apple, если он не дает вам возможности проверить непрозрачные данные, которые он возвращает. - person George; 12.06.2014
comment
Это самая полезная информация, которую я нашел по этой теме. Спасибо! Я столкнулся с другой проблемой, когда ключ plist @NSURLSessionResumeBytesReceived возвращает @0, даже если существует временный файл с фактическим размером файла. В качестве теста я скопировал словарь с измененным ключом @NSURLSessionResumeBytesReceived (равным размеру временного файла), а затем повторно сериализовал обратно в объект NSData. Передача моих измененных данных обратно в -downloadTaskWithResumeData: работает без проблем, но, конечно, я не решаюсь отправить это. Любая другая информация или опыт, связанные с этой проблемой, будут полезны. - person hyperspasm; 22.09.2014
comment
По какой-то причине мои загрузки никогда не возобновятся. Это действительно, но я получаю сообщение об ошибке Неверные данные резюме для фоновой загрузки. Фоновые загрузки должны использовать http или https и должны загружаться в доступный файл. даже если URL-адрес имеет http в начале и доступен. это супер раздражает. - person Maximilian Litteral; 28.10.2014
comment
так кто-нибудь знает, что решение для iOS 9? - person Walid Ibrahim; 24.02.2016
comment
Для тех, кто интересуется этой темой, структура resumeDictionary изменилась на iOS9. NSURLSessionResumeInfoLocalPath удаляется и делает невозможным получение фактических данных. В iOS 10 эта структура (как-то ожидаемо) снова изменилась, став намного сложнее. Мой выбор — просто вернуть YES на iOS 9 и 10. - person skyline75489; 20.09.2016

Я не нашел ответа на вопрос, как заранее определить, действительны ли данные.

Однако в настоящее время я работаю над проблемой следующим образом:

NSData *resumeData = ...;
NSURLRequest *originalURLRequest = ...;
NSURLSessionDownloadTask *downloadTask = nil;

@try {
    downloadTask = [session downloadTaskWithResumeData:resumeData];
}
@catch (NSException *exception) {
    if ([NSInvalidArgumentException isEqualToString:exception.name]) {
        downloadTask = [session downloadTaskWithRequest:originalURLRequest];
    } else {
        @throw exception; // only swallow NSInvalidArgumentException for resumeData
    }
}
person Ian Terrell    schedule 24.02.2014

на самом деле данные резюме представляют собой файл plist. он содержит следующий ключ:

  • NSURLSessionDownloadURL
  • NSURLSessionResumeBytesReceived
  • NSURLSessionResumeCurrentRequest
  • NSURLSessionResumeEntityTag
  • NSURLSessionResumeInfoTempFileName
  • NSURLSessionResumeInfoVersion
  • NSURLSessionResumeOriginalRequest
  • NSURLSessionResumeServerDownloadDate, поэтому вам нужно выполнить следующие шаги:

    1. check the data is a valid plist;
    2. проверьте, есть ли в plist ключи, как указано выше;
    3. проверьте, существует ли временный файл;
person user1939383    schedule 20.11.2015