HTML5   发布时间:2022-04-27  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了ios – AFNetworking 2.0将NSURLSessionDataTask转换为NSURLSessionDownloadTask不会将所有文件数据写入磁盘大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
@H_874_0@
在将NSURLSessionDataTask转换为NSURLSessionDownloadTask时,我们遇到了数据丢失问题.具体来说,对于大于16K的文件,我们将丢失前16K字节(正好是16384字节).写入磁盘的文件短于初始响应的长度….

很长的帖子,感谢阅读和任何建议.

更新2014-09-30 – 最终修复

所以我最近又遇到了同样的行为,并决定深入挖掘.事实证明,Matt T(AFNetworking的作者)发布了一个修改AFURLSessionManager -respondsToSELEctor方法的提交,如果任何OPTIONAL委托调用未设置为Blocks,它将返回NO.提交在这里(问题#1779)https://github.com/AFNetworking/AFNetworking/commit/6951a26ada965edc6e43cf83a4985b88b0f514d2.

因此,您支持使用可选委托的方式是使用覆盖-URLSession的块INSTEAD调用-setTaskDidReceiveAuthenticationChALLENgeBlock:方法(调用您要使用的可选委托的方法):dataTask:didReceiveResponse:completionHandler:method in你的子类.这样做会产生预期的结果.

建立:

我们正在编写一个从Web服务器下载文件的iOS应用程序.这些文件PHP脚本保护,该脚本验证来自iOS客户端的请求.

我们正在使用AFNetworking 2.0并且正在对发送用户凭据等的API执行初始POST(NSURLSessionDataTask)操作.这是最初的要求:

NSURLSessionDataTask * task = [self POST:API_FULL_SYNC_GETFILE_PATH参数:正文成功:^(NSURLSessionDataTask * task,id responSEObject){..}];

我们有一个自定义类,它继承自AFhttpSessionManager类,其中包含此问题中的所有iOS代码.

服务器接收此请求并对用户进行身份验证.其中一个POST参数是客户端尝试下载的文件.服务器找到文件并将其吐出.为了简单起见,我已经删除了身份验证和一些缓存控制头,但这里是运行的服务器PHP脚本:

$file_name = $callparams['FILename'];
$requested_file = "$sync_data_dir/$file_name";

@apache_setenv('no-gzip',1);
@ini_set('zlib.output_compression','Off');
set_time_limit(0);`

$file_size = filesize($requested_filE);
header("Content-Type: application/gzip");
header("Content-transfer-encoding: Binary");
header("Content-Length: {$file_sizE}");
header("Content-Disposition: attachment; filename=\"{$file_name}\"");

$read_bytes = readfile($requested_filE);

这些文件总是.gz文件.

回到客户端,收到响应并调用NSURLSessionDataDelegate的-URLSession:dataTask:didReceiveResponse:completionHandler:方法.我们检测MIME类型并将任务切换到下载任务:

-(void)URLSession:(NSURLSession *)session
         dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
    [super URLSession:session dataTask:dataTask didReceiveResponse:response completionHandler:completionHandler];

    /*
     This transforms a data task into a download taks for certain API calls. check the headers to determine what to do
     */
    if ([response.MIMEType isEqualToString:@"application/gzip"]) {
        // Convert to download task
        completionHandler(NSURLSessionResponseBecomeDownload);
        return;
    }
    // conTinue as-is
    completionHandler(NSURLSessionResponseAllow);

}

-URLSession:dataTask:didBecomeDownloadTask:方法调用.我们使用此方法使用id关联数据任务和下载任务.这样做是为了跟踪数据任务完成处理程序中的下载任务的结果.这个问题不是很重要,但这里是代码

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
{
    [super URLSession:session dataTask:dataTask didBecomeDownloadTask:downloadTask];

    // Relate the data task with the download task.
    if (!_downloadTaskIdToDownloadIdTaskMap) {
        _downloadTaskIdToDownloadIdTaskMap = [NSMutableDictionary Dictionary];
    }
    [_downloadTaskIdToDownloadIdTaskMap setObject:@(dataTask.taskIdentifier) forKey:@(downloadTask.taskIdentifier)];
}

出现问题的地方:

在-URLSession:downloadTask:didFinishDownloadingToURL:方法中,写入的临时文件的大小小于content-length.

我们发现了什么:

A)如果我们实现NSURLSessionTaskDelegate类的URLSession:dataTask:didReceiveData:方法,我们会为我们尝试下载的每个文件准确地观察1次调用.如果文件大于16384字节,则生成的临时文件将缩短该数量.将日志条目放入此方法,我们看到对于大于该文件文件,数据参数的长度为16384字节.

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
    [super URLSession:session dataTask:dataTask didReceiveData:data];

    NSMutableDictionary *dataTaskDetails = [_dataTaskDetails objectForKey:@(dataTask.taskIdentifier)];
    NSString *filename  = dataTaskDetails[@"FILename"];

    DDLogDebug(@"Data recieved for file '%@'. Data length %d",filename,data.length);
}

B)将日志条目放入URLSession:downloadTask:didWriteData:@R_652_10586@lBytesWritten:@R_652_10586@lBytesExpectedToWrite:NSURLSessionDownloadDelegate类的方法,我们观察对我们尝试下载的每个文件进行一次或多次此方法调用.如果文件<16K,则只显示单个呼叫.如果文件大于16K,我们会收到更多电话.这是方法

- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
      didWriteData:(int64_t)bytesWritten
 @R_652_10586@lBytesWritten:(int64_t)@R_652_10586@lBytesWritten
@R_652_10586@lBytesExpectedToWrite:(int64_t)@R_652_10586@lBytesExpectedToWrite
{
    [super URLSession:session downloadTask:downloadTask didWriteData:bytesWritten @R_652_10586@lBytesWritten:@R_652_10586@lBytesWritten @R_652_10586@lBytesExpectedToWrite:@R_652_10586@lBytesExpectedToWrite];

    id dataTaskId = [_downloadTaskIdToDownloadIdTaskMap objectForKey:@(downloadTask.taskIdentifier)];
    NSMutableDictionary *dataTaskDetails = [_dataTaskDetails objectForKey:dataTaskId];
    NSString *filename  = dataTaskDetails[@"FILename"];
    DDLogDebug(@"File '%@': Wrote %lld bytes. @R_652_10586@l %lld of %lld bytes written.",bytesWritten,@R_652_10586@lBytesWritten,@R_652_10586@lBytesExpectedToWritE);
}

例如,下面是单个文件’members.json.gz’的控制台输出.我添加评论以突出重要的一句话.

[2014-02-24 00:54:16:290][main][I][APIClient.m:syncFullGetFile:withSyncToken:andUserName:andpassword:andCompletedBlock:][Line: 184] API Client requesTing file 'members.json.gz' for session with token 'MToxMzkzMjIxMjM4'. <-- This is the initial request for the file.
[2014-02-24 00:54:17:448][NSOperationQueue 0x14eb6380][?][APIClient.m:URLSession:dataTask:didReceiveData:][Line: 542] Data recieved for file 'members.json.gz'. Data length 16384 <-- Initial response,seems to fire BEFORE the conversion to a download task.
[2014-02-24 00:54:17:487][NSOperationQueue 0x14eb6380][?][APIClient.m:URLSession:downloadTask:didWriteData:@R_652_10586@lBytesWritten:@R_652_10586@lBytesExpectedToWrite:][Line: 521] File 'members.json.gz': Wrote 16384 bytes. @R_652_10586@l 16384 of 92447 bytes written. <-- Now the data task is a download task.
[2014-02-24 00:54:17:517][NSOperationQueue 0x14eb6380][?][APIClient.m:URLSession:downloadTask:didWriteData:@R_652_10586@lBytesWritten:@R_652_10586@lBytesExpectedToWrite:][Line: 521] File 'members.json.gz': Wrote 16384 bytes. @R_652_10586@l 32768 of 92447 bytes written.
[2014-02-24 00:54:17:533][NSOperationQueue 0x14eb6380][?][APIClient.m:URLSession:downloadTask:didWriteData:@R_652_10586@lBytesWritten:@R_652_10586@lBytesExpectedToWrite:][Line: 521] File 'members.json.gz': Wrote 16384 bytes. @R_652_10586@l 49152 of 92447 bytes written.
[2014-02-24 00:54:17:550][NSOperationQueue 0x14eb6380][?][APIClient.m:URLSession:downloadTask:didWriteData:@R_652_10586@lBytesWritten:@R_652_10586@lBytesExpectedToWrite:][Line: 521] File 'members.json.gz': Wrote 16384 bytes. @R_652_10586@l 65536 of 92447 bytes written.
[2014-02-24 00:54:17:568][NSOperationQueue 0x14eb6380][?][APIClient.m:URLSession:downloadTask:didWriteData:@R_652_10586@lBytesWritten:@R_652_10586@lBytesExpectedToWrite:][Line: 521] File 'members.json.gz': Wrote 10527 bytes. @R_652_10586@l 76063 of 92447 bytes written. <-- @R_652_10586@l is short by same 16384 - same number as the initial response.
[2014-02-24 00:54:17:573][NSOperationQueue 0x14eb6380][?][APIClient.m:URLSession:downloadTask:didFinishDownloadingToURL:][Line: 472] Temp file size for 'members.json.gz' is 76063
[2014-02-24 00:54:17:573][NSOperationQueue 0x14eb6380][?][APIClient.m:URLSession:downloadTask:didFinishDownloadingToURL:][Line: 485] File 'members.json.gz' downloaded. Reported 92447 of 92447 bytes received.
[2014-02-24 00:54:17:574][NSOperationQueue 0x14eb6380][?][APIClient.m:URLSession:downloadTask:didFinishDownloadingToURL:][Line: 490] File size after move for 'members.json.gz' is 76063
[2014-02-24 00:54:17:574][NSOperationQueue 0x14eb6380][E][APIClient.m:URLSession:downloadTask:didFinishDownloadingToURL:][Line: 497] Expected size of file 'members.json.gz' is 92447 but size on disk is 76063. Temp file size is 0.

救命:

我们认为我们做错了什么.也许我们从服务器发送的标头与数据到下载任务开关不一致.也许我们没有正确使用AFNetworking.

有没有人对这种行为有所了解?我们是否应该在URLSession中捕获初始响应主体:dataTask:didReceiveData:在任务切换到下载任务之前?

真正奇怪的是,如果文件低于16K,则没有问题.整个文件都是写的.

所有文件请求都作为数据任务启动,并转换为下载任务.

@H_489_63@解决方法
我可以将NSURLSessionDataTask转换为NSURLSessionBACkgroundTask,并且(a)文件大小合适,(b)我没有看到对didReceiveData的任何调用.

我注意到你正在调用这些各种委托方法的超级实例.这有点好奇.我想知道你的didReceiveResponse的超级实现是否正在调用完成处理程序本身,导致你两次调用这个完成处理程序.值得注意的是,如果我故意调用处理程序两次,我可以重现您的问题,一次使用NSURLSessionResponseAllow,然后再使用NSURLSessionResponseBecomeDownload再次调用它.

确保只调用一次完成处理程序,并对这些超级方法中的内容非常小心(或者只是删除对它们的引用).

大佬总结

以上是大佬教程为你收集整理的ios – AFNetworking 2.0将NSURLSessionDataTask转换为NSURLSessionDownloadTask不会将所有文件数据写入磁盘全部内容,希望文章能够帮你解决ios – AFNetworking 2.0将NSURLSessionDataTask转换为NSURLSessionDownloadTask不会将所有文件数据写入磁盘所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。