使用 NSURLSession 时无法调用 didReceiveData
Posted
技术标签:
【中文标题】使用 NSURLSession 时无法调用 didReceiveData【英文标题】:Cannot get didReceiveData to be called when using NSURLSession 【发布时间】:2016-02-06 12:04:52 【问题描述】:我正在从这个 React Native 模块 react-native-file-upload 中获取一些代码。我正在尝试将其从使用 NSURLConnection(已弃用)更新为使用 NSURLSession 并报告上传进度。
我能够毫不费力地将代码从 NSURLConnection 切换到 NSURLSession,但我正在努力让 didReceiveData 委托被调用。为什么不调用 didReceiveData 委托?
这是我修改后的 FileUpload.m
#import <Foundation/Foundation.h>
#import <MobileCoreServices/MobileCoreServices.h>
#import <UIKit/UIKit.h>
#import <Photos/Photos.h>
#import "RCTBridgeModule.h"
#import "RCTLog.h"
@interface FileUpload : NSObject <RCTBridgeModule, NSURLSessionDataDelegate, NSURLSessionDelegate, NSURLSessionTaskDelegate>
@end
@implementation FileUpload
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(upload:(NSDictionary *)obj callback:(RCTResponseSenderBlock)callback)
NSString *uploadUrl = obj[@"uploadUrl"];
NSDictionary *headers = obj[@"headers"];
NSDictionary *fields = obj[@"fields"];
NSArray *files = obj[@"files"];
NSString *method = obj[@"method"];
if ([method isEqualToString:@"POST"] || [method isEqualToString:@"PUT"])
else
method = @"POST";
NSURL *url = [NSURL URLWithString:uploadUrl];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
[req setHTTPMethod:method];
// set headers
NSString *formBoundaryString = [self generateBoundaryString];
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", formBoundaryString];
[req setValue:contentType forHTTPHeaderField:@"Content-Type"];
for (NSString *key in headers)
id val = [headers objectForKey:key];
if ([val respondsToSelector:@selector(stringValue)])
val = [val stringValue];
if (![val isKindOfClass:[NSString class]])
continue;
[req setValue:val forHTTPHeaderField:key];
NSData *formBoundaryData = [[NSString stringWithFormat:@"--%@\r\n", formBoundaryString] dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData* reqBody = [NSMutableData data];
// add fields
for (NSString *key in fields)
id val = [fields objectForKey:key];
if ([val respondsToSelector:@selector(stringValue)])
val = [val stringValue];
if (![val isKindOfClass:[NSString class]])
continue;
[reqBody appendData:formBoundaryData];
[reqBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
[reqBody appendData:[val dataUsingEncoding:NSUTF8StringEncoding]];
[reqBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
// add files
for (NSDictionary *file in files)
NSString *name = file[@"name"];
NSString *filename = file[@"filename"];
NSString *filepath = file[@"filepath"];
NSString *filetype = file[@"filetype"];
NSData *fileData = nil;
NSLog(@"filepath: %@", filepath);
if ([filepath hasPrefix:@"assets-library:"])
NSURL *assetUrl = [[NSURL alloc] initWithString:filepath];
__block NSData * tempData = nil;
PHFetchResult *result = [PHAsset fetchAssetsWithALAssetURLs:@[assetUrl] options:nil];
PHAsset *asset = result.firstObject;
if (asset)
PHCachingImageManager *imageManager = [[PHCachingImageManager alloc] init];
// Request an image for the asset from the PHCachingImageManager.
[imageManager requestImageForAsset:asset targetSize:CGSizeMake(100.0f, 100.0f) contentMode:PHImageContentModeAspectFill options:nil resultHandler:^(UIImage *image, NSDictionary *info)
NSLog(@"IMAGE: %@", image);
tempData = UIImagePNGRepresentation(image);
];
fileData = tempData;
else if ([filepath hasPrefix:@"data:"] || [filepath hasPrefix:@"file:"])
NSURL *fileUrl = [[NSURL alloc] initWithString:filepath];
fileData = [NSData dataWithContentsOfURL: fileUrl];
else
fileData = [NSData dataWithContentsOfFile:filepath];
[reqBody appendData:formBoundaryData];
[reqBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", name.length ? name : filename, filename] dataUsingEncoding:NSUTF8StringEncoding]];
if (filetype)
[reqBody appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n", filetype] dataUsingEncoding:NSUTF8StringEncoding]];
else
[reqBody appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n", [self mimeTypeForPath:filename]] dataUsingEncoding:NSUTF8StringEncoding]];
[reqBody appendData:[[NSString stringWithFormat:@"Content-Length: %ld\r\n\r\n", (long)[fileData length]] dataUsingEncoding:NSUTF8StringEncoding]];
[reqBody appendData:fileData];
[reqBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
// add end boundary
NSData* end = [[NSString stringWithFormat:@"--%@--\r\n", formBoundaryString] dataUsingEncoding:NSUTF8StringEncoding];
[reqBody appendData:end];
// send request
[req setHTTPBody:reqBody];
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:(id)self delegateQueue:[NSOperationQueue mainQueue]];
NSURLSessionDataTask *task = [session dataTaskWithRequest:req completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
NSLog(@"response status code: %ld", (long)[httpResponse statusCode]);
callback(@[[NSNull null], [NSString stringWithFormat:@"response status code: %ld", (long)[httpResponse statusCode]]]);
];
[task resume];
- (NSString *)generateBoundaryString
NSString *uuid = [[NSUUID UUID] UUIDString];
return [NSString stringWithFormat:@"----%@", uuid];
- (NSString *)mimeTypeForPath:(NSString *)filepath
NSString *fileExtension = [filepath pathExtension];
NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)fileExtension, NULL);
NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)UTI, kUTTagClassMIMEType);
if (contentType)
return contentType;
return @"application/octet-stream";
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
completionHandler(NSURLSessionResponseAllow);
NSLog(@"didReceiveResponse");
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
NSLog(@"didReceiveData");
@end
【问题讨论】:
【参考方案1】:对于带有 NSURLSession 的委托模式,我认为您应该通过以下方式创建 NSURLSession:
[NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
delegate:self
delegateQueue:[NSOperationQueue mainQueue]];
但不是:
[NSURLSession sharedSession];
我还注意到您的 NSURLSessionDataDelegate、NSURLSessionDelegate、NSURLSessionTaskDelegate 与 UIViewController 一起使用;但是,您的 NSURLSession 和委托方法是在 FileUpload.m 中实现的。尝试编辑这一行:
@interface FileUpload : NSObject <RCTBridgeModule>
到:
@interface FileUpload : NSObject <RCTBridgeModule, NSURLSessionDataDelegate, NSURLSessionDelegate, NSURLSessionTaskDelegate>
然后创建没有完成处理程序的 NSURLSessionDataTask:
NSURLSessionDataTask *task = [session dataTaskWithRequest:req];
然后看看有什么不同。
【讨论】:
谢谢,我把代码改成了: NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:(id)self delegateQueue:[NSOperationQueue mainQueue]];但 didReceiveData 委托仍未被调用。有什么想法吗? @TomKrones 欢迎您。我认为 NSURLSessionDataDelegate、NSURLSessionDelegate、NSURLSessionTaskDelegate 应该与 FileUpload.m 一起使用,请参考修改后的答案。 感谢您帮助解决此问题。我更改了@interface,我认为我不需要 FileUpload.h 文件?我尝试在有和没有标题的情况下运行它,但它仍然没有调用委托。还有其他想法吗? @TomKrones 你有没有打印出“didReceiveResponse”,你的状态码是什么?上传过程还需要任何身份验证吗?多部分表单上传是一项困难的工作。如果这是您第一次实现 NSURLSession。我建议你先创建一个空的新项目来练习下载。他们在获得经验后实施上传过程。 @TomKrones 经过一次又一次的审查 :) 我认为 NSURLSessionDataTask 的完成处理程序是不必要的,因为带有完成处理程序的方法是一种方便的方法,它将绕过正常的委托调用。以上是关于使用 NSURLSession 时无法调用 didReceiveData的主要内容,如果未能解决你的问题,请参考以下文章
在后台使用 NSURLSession 逐个下载 100 个文件的列表