使用 AFNetworking 2.0 上传进度

Posted

技术标签:

【中文标题】使用 AFNetworking 2.0 上传进度【英文标题】:Upload progress with AFNetworking 2.0 【发布时间】:2014-06-17 11:22:23 【问题描述】:

我知道这个问题在这个网站上很受欢迎,但是在搜索了大部分答案后,我找不到解决方案。 我的应用程序是将图像上传到远程 url。我尝试了几种上传方式,都成功了: (1) 使用 AFURLSessionManager

AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
[(AFHTTPResponseSerializer*)manager.responseSerializer setAcceptableContentTypes:[NSSet setWithObjects:@"multipart/form-data", nil]];
NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithStreamedRequest:request progress:&progress completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) 
    if (error) 
        NSDictionary *userInfo=[error userInfo];
        id responseError=[userInfo objectForKey:@"AFNetworkingOperationFailingURLResponseErrorKey"];
        if ([responseError isKindOfClass:[NSHTTPURLResponse class]]) 
            NSHTTPURLResponse *reponse=(NSHTTPURLResponse*)responseError;
            if (reponse.statusCode==200) 
                result(YES);
             else 
                result(NO);
            
         else 
            result(NO);
        
     else 
        result(YES);
    
];
[uploadTask resume];

为此,我使用 KVO 来监控进度,但没有运气。

(2) 使用AFHTTPRequestOperation

AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    [operation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) 
        NSLog(@"total bytes written: %lld",totalBytesWritten);
    ];
    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) 
        result(YES);
     failure:^(AFHTTPRequestOperation *operation, NSError *error) 
        if (operation.response.statusCode==200) 
            result(YES);
         else 
            result(NO);
        
    ];
    [operation start];

仅在开始上传时立即显示日志“写入的总字节数:”一次,文件体积为 100%。

(3) 使用 AFHTTPRequestOperationManager

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    manager.requestSerializer = [AFHTTPRequestSerializer serializer];
    manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    AFHTTPRequestOperation *operation =
    [manager HTTPRequestOperationWithRequest:request
                                     success:^(AFHTTPRequestOperation *operation, id responseObject) 
                                         NSLog(@"Success %@", responseObject);
                                         result(YES);
                                      failure:^(AFHTTPRequestOperation *operation, NSError *error) 
                                         if (operation.response.statusCode==200) 
                                             result(YES);


               else 
                                         result(NO);
                                     
                                 ];

// 4. Set the progress block of the operation.
[operation setUploadProgressBlock:^(NSUInteger __unused bytesWritten,
                                    long long totalBytesWritten,
                                    long long totalBytesExpectedToWrite) 
    double percentDone = (double)totalBytesWritten / (double)totalBytesExpectedToWrite;
    NSLog(@"progress updated(percentDone) : %f", percentDone);
];

// 5. Begin!
[operation start];

=> 与第二个解决方案的结果相同。

请注意,所有解决方案都已成功上传,但进度未更新(NSProgress 或阻止)。

【问题讨论】:

【参考方案1】:
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:@"<your upload url>" parameters:@<parameters dict>  constructingBodyWithBlock:^(id<AFMultipartFormData> formData) 
         _STRONGREF(strongSelf);

            NSString  *name=@"file_data_name";
            [formData appendPartWithFileURL:[NSURL fileURLWithPath:path] name:name fileName:<file Path> mimeType:@"image/png" error:nil];//u can use data as well with other method
        

     error:nil];
    [request setTimeoutInterval:180];
    [request setValue:@"VAlue_your" forHTTPHeaderField:@"feild"];//set headers
    AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
    NSProgress *progress = nil;
    NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithStreamedRequest:request progress:&progress completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) 
        if (error) 
            NSLog(@"Error: %@", error);
         else 
            NSLog(@"%@ %@", response, responseObject);
        
    ];

    NSLog(@"%@",progress);
    [uploadTask resume];

我的宏是

#define  _WEAKREF(obj)         __weak typeof(obj)ref=obj;
#define  _STRONGREF(name)      __strong typeof(ref)name=ref;\
                               if(!name)return ;\

【讨论】:

【参考方案2】:

在你的 NSProgress 上设置一个观察者。

类似

[uploadTask resume];
[progress addObserver:self
                   forKeyPath:@"fractionCompleted"
                      options:NSKeyValueObservingOptionNew
                      context:NULL];

然后执行

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context

if ([keyPath isEqualToString:@"fractionCompleted"]) 
    NSProgress *progress = (NSProgress *)object;
    NSLog(@"progress = %f", progress.fractionCompleted);

 else 
    [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    

上传任务完成后别忘了移除观察者。

注意:progress.fractionCompleted 返回一个百分比值。以 MB 为单位由您决定。

【讨论】:

谢谢,但我在我的项目中使用了 bove 代码(KVO-Key 值观察器)。我不知道为什么观察者不开火。【参考方案3】:

最后,我找到了没有调用进度块的原因:我上传了一个非常小的图像,大约只有几十 Kb。我尝试上传更大尺寸的文件并调用进度块。根据我的经验,带有 NSProgress 的 KVO 不起作用。取而代之的是,我们可以使用 AFHTTPRequestOperation 或 AFHTTPRequestOperationManager。如果您喜欢手动上传,您可以使用How can I upload a photo to a server with the iPhone? 建议的 NSURLConnection 以及委托方法: - (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite NSLog(@"percent: %d",100*totalBytesWritten/totalBytesExpectedToWrite);

【讨论】:

我按照您的建议上传了一个更大的文件,并且正在调用委托连接:didSendBodyData 也打印了日志。但是 1) 委托中的 self.uploadProgress 为 nil,因此未调用回调块 setUploadProgressBlock。 2) AFURLConnectionOperation.m 中的调试描述了 self.uploadProgress 的内容,而 setUploadProgressBlock 在委托中变为 nil。

以上是关于使用 AFNetworking 2.0 上传进度的主要内容,如果未能解决你的问题,请参考以下文章

AFNetworking 显示上传进度

AFNetworking 上传进度发送 SOAP NSData

通过 AFNetworking 2.0 上传 iOS 图像

在单个 PHP 中使用 AFNetworking 2.0 上传图像和信息(两者)

如何在上传带参数的图像时使用 AFNetworking 2.0 设置 cookie?

AFNetworking 2.0 spring MVC 上传文件