异步上传与NSURLSession将无法正常工作,但是同步NSURLConnection的呢 [英] asynchronous upload with NSURLSession will not work but synchronous NSURLConnection does

查看:236
本文介绍了异步上传与NSURLSession将无法正常工作,但是同步NSURLConnection的呢的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

编辑:
我需要从一个iPhone异步上传文件到Python服务器端进程。我想这样做异步请求,这样我可以,而它的显示工作繁忙的动画。

edit: I need to upload a file asynchronously from an iPhone to a Python server-side process. I'd like to do the request asynchronously so that I can display a busy animation while it's working.

请求需要包括用户名,密码和文件作为'的multipart / form-data的。

The request needs to include the username, password and file as 'multipart/form-data'.

我可以得到它使用NSURLConnection的与code看着这样的:

I can get it working synchronously using NSURLConnection with the code looking like this::

-(void) uploadDatabase{

Database *databasePath = [[Database alloc] init];
NSString *targetPath = [databasePath getPathToDatabaseInDirectory];

NSData *dbData = [NSData dataWithContentsOfFile:targetPath];
NSString *url = @"http://mydomain.com/api/upload/";
//NSString *username = [[NSUserDefaults standardUserDefaults] stringForKey:USERNAME];
NSString *username = @"user";
NSString *password = @"pass";
NSMutableURLRequest *request = [self createRequestForUrl:url withUsername:username andPassword:password andData:dbData];

NSURLResponse *response;
NSError *error;

NSData *result = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

NSString *stringResult = [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];

NSLog(@"**server info %@", stringResult);}

//请求建设

    -(NSMutableURLRequest*) createRequestForUrl: (NSString*)urlString withUsername:(NSString*)username andPassword:(NSString*)password andData:(NSData*)dbData
    {NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60.0];
[request setHTTPMethod:@"POST"];

NSString *boundary = @"BOUNDARY_STRING";
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
[request addValue:contentType forHTTPHeaderField:@"Content-Type"];

NSMutableData *body = [NSMutableData data];

if(dbData != NULL)
{
    //only send these methods when transferring data as well as username and password
    [body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"file\"; filename=\"dbfile\"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[@"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[NSData dataWithData:dbData]];
}

[body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"username\"\r\n\r\n%@", username] dataUsingEncoding:NSUTF8StringEncoding]];

[body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"password\"\r\n\r\n%@", password] dataUsingEncoding:NSUTF8StringEncoding]];

[body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];

[request setHTTPBody:body];

return request;}

然而,当我尝试这样做异步使用NSURLSession它似乎并没有正常工作。在code。与NSURLSession看起来是这样的:

However, when I try to do this asynchronously using NSURLSession it doesn't seem to work properly. The code with NSURLSession looks like this:

    -(void)uploadDatabase{
    Database *databasePath = [[Database alloc] init];
    NSString *targetPath = [databasePath getPathToDatabaseInDirectory];
    NSURL *phonedbURL = [NSURL URLWithString:targetPath];

    NSString *url = @"http://mydomain.com/api/upload/";
    NSString *username = @"user";
    NSString *password = @"pass";
    NSMutableURLRequest *request = [self createRequestForUrl:url withUsername:username andPassword:password andData:NULL];

NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];

self.uploadSession = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:Nil];
NSLog(@"the url = %@",url);
NSURLSessionUploadTask *uploadTask = [self.uploadSession uploadTaskWithRequest:request fromFile:phonedbURL];

[uploadTask resume];}

我挣扎看到我在做什么不同,虽然它看起来这应该工作。

I'm struggling to see what I'm doing differently though as it seems this should work.

时使用NSURLSession做异步请求的正确方法?我是新来的NSURLSession所以我必须改变我的NSURLSession请求,而不是NSURLConnection的NSURLMutableRequest?

Is using NSURLSession the right way to do asynchronous requests? and I'm new to NSURLSession so do I have to change my NSURLMutableRequest for NSURLSession requests rather than NSURLConnection?

在此先感谢您的帮助!

推荐答案

您是正确的,如果你只想让你的要求是异步的,你应该退休 sendSynchronousRequest 。虽然我们曾经会推荐 sendAsynchronousRequest ,有效的iOS 9, NSURLConnection的是正式pcated德$ P $和一个应该青睐 NSURLSession

You are correct, that if you just want to make your request asynchronous, you should retire sendSynchronousRequest. While we once would have recommended sendAsynchronousRequest, effective iOS 9, NSURLConnection is formally deprecated and one should favor NSURLSession.

一旦你开始使用 NSURLSession ,你可能会发现自己吸引到它。例如,可以使用一个 [NSURLSessionConfiguration backgroundSessionConfiguration:] ,然后让应用程序已经进入后台即使上传进度。 (你必须写一些委托方法,所以为了简单起见,我住下面一个简单的前台上传。)这只是您的业务需求的问题,抵消了新的 NSURLSession 功能与iOS的7+限制它需要。

Once you start using NSURLSession, you might find yourself drawn to it. For example, one can use a [NSURLSessionConfiguration backgroundSessionConfiguration:], then have uploads progress even after the app has gone into background. (You have to write a few delegate methods, so for simplicity's sake, I've stayed with a simple foreground upload below.) It's just a question of your business requirements, offsetting the new NSURLSession features versus the iOS 7+ limitation it entails.

顺便说一句,了解iOS / MacOS的网络请求任何谈话可能是无 AFNetworking 参考不完整的。它极大地简化了这些多部分请求,绝对值得调查的创建。他们有 NSURLSession 的支持,太(但我没有用他们的会议上包装,所以不能把它说)。但AFNetworking无疑是值得你考虑。您可以享受一些代表基API的丰富性(如进度更新,取消请求,操作之间的依赖关系等),提供更大的控制与提供方便的方法(如 sendSynchronousRequest ),但没有拖你通过委托方法杂草自己。

By the way, any conversation about network requests in iOS/MacOS is probably incomplete without a reference to AFNetworking. It greatly simplifies creation of these multipart requests and definitely merits investigation. They have NSURLSession support, too (but I haven't used their session wrappers, so can't speak to it). But AFNetworking is undoubtedly worthy of your consideration. You can enjoy some of the richness of the delegate-base API (e.g. progress updates, cancelable requests, dependencies between operations, etc.), offering far greater control that available with convenience methods (like sendSynchronousRequest), but without dragging you through the weeds of the delegate methods themselves.

无论如何,如果你在如何做 NSURLSession 上传真的很感兴趣,请参阅下文。

Regardless, if you're really interested in how to do uploads with NSURLSession, see below.

如果您想通过 NSURLSession 上传,它是在思考一个轻微的转变,即在<$ C分离请求的配置(和它的头) $ C> NSMutableURLRequest 从创建请求的身体(你现在的 NSURLSessionUploadTask 的实例化过程中指定)的。请求你现在指定为上传任务的一部分,该机构可以是的NSData ,文件或流(我用的是 NSData的下面,因为我们正在构建一个多部分请求):

If you want to upload via NSURLSession, it is a slight shift in thinking, namely, separating the configuration of the request (and its headers) in the NSMutableURLRequest from the creation of the the body of the request (which you now specify during the instantiation of the NSURLSessionUploadTask). The body of the request that you now specify as part of the upload task can be either a NSData, a file, or a stream (I use a NSData below, because we're building a multipart request):

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:@"POST"];
NSString *boundary = [self boundaryString];
[request addValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary] forHTTPHeaderField:@"Content-Type"];

NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];

NSData *fileData = [NSData dataWithContentsOfFile:path];
NSData *data = [self createBodyWithBoundary:boundary username:@"rob" password:@"password" data:fileData filename:[path lastPathComponent]];

NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData:data completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    NSAssert(!error, @"%s: uploadTaskWithRequest error: %@", __FUNCTION__, error);

    // parse and interpret the response `NSData` however is appropriate for your app
}];
[task resume];

和创作的的NSData 发送很像你现有的code:

And the creation of the NSData being sent is much like your existing code:

- (NSData *) createBodyWithBoundary:(NSString *)boundary username:(NSString*)username password:(NSString*)password data:(NSData*)data filename:(NSString *)filename
{
    NSMutableData *body = [NSMutableData data];

    if (data) {
        //only send these methods when transferring data as well as username and password
        [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
        [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"file\"; filename=\"%@\"\r\n", filename] dataUsingEncoding:NSUTF8StringEncoding]];
        [body appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", [self mimeTypeForPath:filename]] dataUsingEncoding:NSUTF8StringEncoding]];
        [body appendData:data];
        [body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    }

    [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"username\"\r\n\r\n%@\r\n", username] dataUsingEncoding:NSUTF8StringEncoding]];

    [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"password\"\r\n\r\n%@\r\n", password] dataUsingEncoding:NSUTF8StringEncoding]];

    [body appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];

    return body;
}

您辛苦codeD的边界和MIME类型,这是很好的,但上面的情况使用下面的方法:

You hardcoded the boundary and the mime type, which is fine, but the above happens to use the following methods:

- (NSString *)boundaryString
{
    NSString *uuidStr = [[NSUUID UUID] UUIDString];

    // If you need to support iOS versions prior to 6, you can use
    // Core Foundation UUID functions to generate boundary string
    //
    // adapted from http://developer.apple.com/library/ios/#samplecode/SimpleURLConnections
    //
    // NSString  *uuidStr;
    //
    // CFUUIDRef uuid = CFUUIDCreate(NULL);
    // assert(uuid != NULL);
    // 
    // NSString  *uuidStr = CFBridgingRelease(CFUUIDCreateString(NULL, uuid));
    // assert(uuidStr != NULL);
    // 
    // CFRelease(uuid);

    return [NSString stringWithFormat:@"Boundary-%@", uuidStr];
}

- (NSString *)mimeTypeForPath:(NSString *)path
{
    // get a mime type for an extension using MobileCoreServices.framework

    CFStringRef extension = (__bridge CFStringRef)[path pathExtension];
    CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, extension, NULL);
    assert(UTI != NULL);

    NSString *mimetype = CFBridgingRelease(UTTypeCopyPreferredTagWithClass(UTI, kUTTagClassMIMEType));
    assert(mimetype != NULL);

    CFRelease(UTI);

    return mimetype;
}

这篇关于异步上传与NSURLSession将无法正常工作,但是同步NSURLConnection的呢的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆