NSURLSession确保上传工作

时间:2014-05-08 18:22:10

标签: objective-c ios7 upload nsurlsession

确保上传有效的最佳方法是什么?

我需要将图片上传到服务器并确保上传工作正常。如果由于某种原因上传不起作用,我将不得不稍后重试。

目前,我正在使用NSUrlSession上传图片:

- (void)didCreateSignature:(UIImage *)image {

    BLog();

    NSArray   *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString  *documentsDirectory = [paths objectAtIndex:0];
    NSString  *imageFile = [NSString stringWithFormat:@"%@/%@", documentsDirectory,@"test.png"];


    NSData *imageData = UIImagePNGRepresentation(image);
    [imageData writeToFile:imageFile atomically:YES];

    while (![[NSFileManager defaultManager] fileExistsAtPath:imageFile]) {
        [NSThread sleepForTimeInterval:.5];
    }


    NSURLSession *session = [self backgroundSession];

    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:self.uploadUrl]];
    [request setHTTPMethod:@"POST"];
    [request addValue:@"image/png" forHTTPHeaderField:@"Content-Type"];

    NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromFile:[NSURL fileURLWithPath:signatureFile]];

    [uploadTask resume];

}

现在让我们假设用户没有互联网连接,然后会触发委托:

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    BLog();

    if (error == nil)
    {
        NSLog(@"Task: %@ completed successfully", task);
    }
    else
    {
        NSLog(@"Task: %@ completed with error: %@", [task originalRequest], [error localizedDescription]);


        dispatch_async(dispatch_get_main_queue(), ^{
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reachabilityChanged:) name:kReachabilityChangedNotification object:nil];
            self.internetReachability = [Reachability reachabilityForInternetConnection];
            [self.internetReachability startNotifier];
        });


    }

}

根据Apple文档,如果Reachability状态已更改,您应稍后重试。所以我想把这些任务添加到一个数组,并在互联网连接可用后再次启动每个任务。

这是正确的方法吗?

如果用户关闭应用程序(通过TaskManager)会发生什么。我怎样才能确保这些任务能够恢复?

1 个答案:

答案 0 :(得分:2)

与此同时,我通过使用自定义UploadManager类解决了这个问题,并将下载存储在Core Data中,直到上传成功为止:( Code下方未完成,只显示基本概念。如果有人对完整实现感兴趣,请让我知道)

@implementation UploadManager

- (void) uploadImage:(UIImage *)image toUrl:(NSString *)uploadUrl insertIntoDB:(BOOL)insertIntoDB

{
    if(insertIntoDB) {
        [self insertUploadTaskIntoDBWithImage:image andUploadUrl:uploadUrl];
    }


    // Background upload only works with files
    NSString *fileName = [NSString stringWithFormat:@"%@%@", [uploadUrl sha256], @".png"];
    NSString *signatureFile = [NSString stringWithFormat:@"%@/%@",  NSTemporaryDirectory(), fileName];

    NSData *imageData = UIImagePNGRepresentation(image);
    [imageData writeToFile:signatureFile atomically:YES];

    while (![[NSFileManager defaultManager] fileExistsAtPath:signatureFile]) {
        [NSThread sleepForTimeInterval:.5];
    }

    NSURLSession *session = [self backgroundSession];

    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:uploadUrl]];
    [request setHTTPMethod:@"POST"];
    [request addValue:@"image/png" forHTTPHeaderField:@"Content-Type"];

    NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromFile:[NSURL fileURLWithPath:signatureFile]];

    [self showNetworkActivity:YES];

    self.taskCount++;
    [uploadTask resume];
}


-(void) uploadTasksInDatabase
{
    NSArray* uploadTasks = [self getAllUploadTasksFromDatabase];

    for(UploadTask *task in uploadTasks) {
        UIImage *image = [UIImage imageWithData:task.signature];
        [self uploadImage:image toUrl:task.uploadUrl insertIntoDB:NO];
    }
}


/*
 If an application has received an 
 application:handleEventsForBackgroundURLSession:completionHandler: message, the session
 delegate will receive this message to indicate that all messages previously enqueued for this
 session have been delivered. At this time it is safe to invoke the previously stored completion
 handler, or to begin any internal updates that will result in invoking the completion handler.
 */
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
{
    AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
    if (appDelegate.backgroundSessionCompletionHandler) {
        void (^completionHandler)() = appDelegate.backgroundSessionCompletionHandler;
        appDelegate.backgroundSessionCompletionHandler = nil;
        completionHandler();
    }

    self.taskCount = 0;
    NSDebug(@"All tasks are finished");

    [self clearDatabase];

}



- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    BLog();


    self.taskCount--;
    [[NSNotificationCenter defaultCenter] postNotificationName:[task.originalRequest.URL description] object:self];

    if(self.taskCount == 0) {
        [self showNetworkActivity:NO];
    }


    if (error == nil)
    {
        NSLog(@"Task: %@ completed successfully", [task.originalRequest.URL description]);
        [self deleteUploadTaskWithUploadUrl:[task.originalRequest.URL description]];
    }
    else
    {
        NSLog(@"Task: %@ completed with error: %@", [task.originalRequest.URL description], [error localizedDescription]);

    }

}

在App委托中插入以下内容:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Try to reupload all tasks in DB
    dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(backgroundQueue, ^{
            [self.uploadManager uploadTasksInDatabase];
    });


    return YES;
}

这样,只要再次启动应用程序,它就会尝试重新上载所有未完成的任务。