使用NSURLSessionDownloadTask为非常大的文件计算md5

时间:2014-03-30 00:15:45

标签: objective-c md5 nsurl

NSURLConnection可用于实时计算md5:

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)theData
    // theData is a small piece

NSURLSessionDownloadTask是NSURLConnection的“升级”。但是如何在下载之后再检查md5而不再读取整个文件?它的界面如下:

NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request
                                                     completionHandler:
^(NSURL *location, NSURLResponse *response, NSError *error) {
    // the whole file is downloaded and saved at location.
}];

这里的关键要求是内存占用量低,文件必须完全下载。

2 个答案:

答案 0 :(得分:2)

如果您希望数据到达小的NSData片段,您可以检查并逐渐附加到更大的NSMutableData,就像使用connection:didReceiveData:一样,请求data task而不是下载任务。

您致电dataTaskWithRequest:,提供代表,并启动数据任务(使用resume) - 代表接收URLSession:dataTask:didReceiveData:,与旧的NSURLConnection日期完全相同。

这是一个完整的工作示例(除非我不会告诉您在数据到达时如何处理数据):

- (NSURLSession*) configureSession {
    NSURLSessionConfiguration* config =
    [NSURLSessionConfiguration ephemeralSessionConfiguration];
    config.allowsCellularAccess = NO;
    NSURLSession* session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    return session;
}

- (IBAction) doHTTP: (id) sender {
    if (!self.session)
        self.session = [self configureSession];

    NSString* s = // some URL string
    NSURL* url = [NSURL URLWithString:s];
    NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL:url];
    NSURLSessionDataTask* task = [[self session] dataTaskWithRequest:req];
    self.task = task;
    [task resume];
}

-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
    NSLog(@"received %lu bytes of data", (unsigned long)data.length);
    // do something with the data here!
}

-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
    NSLog(@"completed; error: %@", error);
}

答案 1 :(得分:1)

正如Matt所说,您可以使用数据任务,这样您就可以在下载数据时轻松查看数据。

但是,如果您想观察下载任务,如果您愿意承担一些小风险,则可以完成类似的事情。

我确定我会为以下内容获得一百万张选票,但请记住......当你需要一把螺丝刀,而你所拥有的只是一把锤子时,你将锤子翻过来,把它当作螺丝刀使用......或者用螺丝钉敲击它以致它变成钉子......

首先,我认为API已被破坏。代表们应该提供这两件事中的至少一件。如果您同意,请提交雷达。代理人应该提供临时文件(更不用说首选 - 我认为它应该保持不透明),或者它应该提供NSData中写的URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite: - 这是正确答案。

无论如何,如果您愿意使用无证件非官方方法......

临时文件存储在Library/Caches/com.apple.nsnetworkd/中,因此您可以轻松查看并确定哪些文件用作临时目标。

或者,您可以再次非正式,通过使用cancelByProducingResumeData:取消下载然后取消归档恢复数据blob来确定临时文件 - 恢复数据blob当前是归档字典 - 并从字典中获取文件路径。然后,您可以恢复下载,知道正在使用哪个临时文件进行下载。

无论如何,一旦你有了这个文件,你可以在URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:里面只读取文件中最近写的块。

现在,说到这里,您可能只想使用数据任务,因为它会正式为您提供刚下载的数据...但您可以诉诸此hack获取下载到文件的数据,如果你必须进行后台下载 - 必须关闭下载任务。

您可能遇到的一个问题是文件IO可能是缓冲的,因此实际刷新的内容(可从单独的文件描述符中获取)可能与委托方法中报告的内容不同。您可能只需要跟踪您读取的最后一个字节,并在该代理中,只需从那里读取到文件的当前末尾...

您的里程肯定会有所不同,但它会让您在写入文件时访问数据。

您必须为URLSession:downloadTask:didFinishDownloadingToURL:做同样的事情才能获得最终的数据。