Рассчитать md5 с NSURLSessionDownloadTask для очень большого файла
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 ответа
Если вы хотите, чтобы данные поступали небольшими кусочками NSData, которые вы можете проверить и добавить к большему количеству NSMutableData, как вы это делали с connection:didReceiveData:
, запросите задачу данных вместо задачи загрузки.
Ты звонишь 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);
}
Как сказал Мэтт, вы можете использовать задачу с данными, которая позволяет вам легко видеть данные по мере их загрузки.
Однако, если вы хотите наблюдать за задачей загрузки, вы можете выполнить аналогичную задачу, если вы готовы пойти на небольшой риск.
Я уверен, что получу миллион голосов за следующее, но просто помните... когда вам нужна отвертка, и все, что у вас есть, это молоток, вы переворачиваете молоток и используете его как отвертку.. или так сильно грохнуть винт, что он превращается в гвоздь...
Во-первых, я думаю, что API не работает. Делегаты должны предоставить хотя бы одну из этих двух вещей. Если вы согласны, подайте радар. Делегат должен предоставить временный файл (гораздо менее предпочтительный - я думаю, что он должен оставаться непрозрачным), или он должен предоставить NSData
что пишется в URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:
- это правильный ответ.
Во всяком случае, если вы хотите использовать недокументированный, неофициальный подход...
Временные файлы хранятся в Library/Caches/com.apple.nsnetworkd/
так что вы можете легко найти там и определить, какие файлы используются в качестве временного места назначения.
Или вы можете, опять же неофициально, определить временный файл, отменив загрузку с помощью cancelByProducingResumeData:
а затем разархивировать блок данных резюме - блок данных резюме в настоящее время является заархивированным словарем - и получить путь к файлу из словаря. Затем вы можете возобновить загрузку, зная, какой временный файл используется для загрузки.
Во всяком случае, когда у вас есть файл, внутри вашего URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:
затем вы можете просто прочитать последний записанный фрагмент из файла.
Сказав это, вы можете просто использовать задачу с данными, потому что она официально предоставит вам только что загруженную часть данных... но вы можете прибегнуть к этому хаку, чтобы получить данные, которые были загружены в файл., если вы должны сделать фоновую загрузку - которая должна быть завершена с задачей загрузки.
Одна из проблем, с которой вы можете столкнуться, заключается в том, что файловый ввод-вывод может быть буферизован, поэтому то, что фактически было сброшено (и доступно из отдельного файлового дескриптора), может отличаться от того, что было сообщено в методе делегата. Возможно, вам просто нужно отслеживать последний прочитанный байт, а внутри этого делегата просто читать оттуда до текущего конца файла...
Ваш пробег наверняка будет меняться, но он даст вам доступ к данным, когда они записываются в файл.
Вам придется сделать то же самое для URLSession:downloadTask:didFinishDownloadingToURL:
чтобы получить последний кусок данных.