【问题标题】:How can I check that an NSData blob is valid as resumeData for an NSURLSessionDownloadTask?如何检查 NSData blob 作为 NSURLSessionDownloadTask 的 resumeData 是否有效?
【发布时间】:2014-02-20 00:46:10
【问题描述】:

我有一个应用使用新的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.

此处出现错误,其中resumeDataNSData blob,sessionNSURLSession 的一个实例:

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

数据由 Apple API 提供,经过序列化,然后在稍后的时间点反序列化。它可能已损坏,但它永远不会为 nil(如 if 语句所检查的那样)。

如何提前检查resumeData 是否无效,以免应用崩溃?

【问题讨论】:

    标签: ios ios7 nsurlsession nsurlsessiondownloadtask


    【解决方案1】:

    这是 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 不再是保密协议):我从 Twitter 与一位 Apple 工程师的交流中得到了这个,他建议了该怎么做,我编写了上面的实现

    【讨论】:

    • 由于 7.1 不再受 NDA 保护,您能否指出谣言的来源?我们现在在 7.1 下遇到了很多崩溃,而在 7.0 下运行良好的 try/catch 不再工作了。
    • 来源是我和一位 Apple 工程师之间的 Twitter 交流。这是他的最后一条推文:twitter.com/atnan/status/431571791799005184
    • 感谢您找到此解决方法。如果它没有为您提供验证它返回的不透明数据的方法,那么这是来自 Apple 的一个非常糟糕的 API。
    • 这是我在这个主题上找到的最有用的信息。谢谢!我遇到了一个不同的问题,其中 plist 键 @"NSURLSessionResumeBytesReceived" 返回 @0,即使临时文件存在且具有实际文件大小。作为测试,我使用修改后的 @"NSURLSessionResumeBytesReceived" 键(等于临时文件大小)复制了字典,然后重新序列化回 NSData 对象。将我修改后的数据传回 -downloadTaskWithResumeData: 没有问题,但我当然会犹豫是否要发货。您与此问题相关的任何其他信息或经验都会有所帮助。
    • 对于这个话题感兴趣的人,resumeDictionary的结构在iOS9上已经改变了。 NSURLSessionResumeInfoLocalPath 被删除,无法获取实际数据。在 iOS 10 上,这种结构(不知何故)再次发生了变化,变得更加复杂。我的选择是在 iOS 9 和 10 上简单地返回 YES
    【解决方案2】:

    我还没有找到如何提前判断数据是否有效的答案。

    但是,我目前正在解决这个问题:

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

    【讨论】:

      【解决方案3】:

      其实简历数据是一个plist文件。 它包含以下键:

      • NSURLSessionDownloadURL
      • NSURLSessionResumeBytesReceived
      • NSURLSessionResumeCurrentRequest
      • NSURLSessionResumeEntityTag
      • NSURLSessionResumeInfoTempFileName
      • NSURLSessionResumeInfoVersion
      • NSURLSessionResumeOriginalRequest
      • NSURLSessionResumeServerDownloadDate 所以你需要做的步骤是:

        1. 检查数据是否为有效的 plist;
        2. 检查 plist 是否有上述键;
        3. 检查临时文件是否存在;

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-05-23
        • 2019-11-11
        • 2012-01-15
        • 2012-10-28
        • 1970-01-01
        • 1970-01-01
        • 2017-10-28
        • 2010-12-20
        相关资源
        最近更新 更多