Waiting for multipart image sending get completed

2019-02-28 02:33发布

问题:

I'm impementing an application in iOS7, it's kind of a social network app with posts with images and a backend that saves all of the data sent form the client. The iOS client is sending the information of the post via json and after the info is sent, it starts to send the image via multipart form using AFNetworking.

I need to be notified when the image is sent, so that I can refresh the main view of the app with the new posts, including the recently posted by the client. In the practice if I request the backend for the last posts and the multipart hasn't finished, the sending of the image gets interruped and fails to send the image.

The backend is develop in WCF and is a RESTful JSON web service.

Here is the method that sends the post to the backend:

+(void)addPostToServerAddtext:(NSString *)text addimage:(UIImage *)image addbeach:(NSString *)beach location:(NSString*)location;
{
    NSLog(@"entro a addPost");
    NSString *urlBackend = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"URLBackend"];

    NSData* dataImage = UIImageJPEGRepresentation(image, 1.0);
    NSString* ImageName = [NSString stringWithFormat:@"%@_%@.jpg",idUser ,dateToServer];
    NSString *jsonRequest = [NSString stringWithFormat:@"{\"Date\":\"%@\"...."];

    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@newPost",urlBackend]];

    NSMutableURLRequest *request = [ [NSMutableURLRequest alloc] initWithURL:url];
    NSData *requestData = [NSData dataWithBytes:[jsonRequest UTF8String] length:[jsonRequest length]];

    [request setHTTPMethod:@"POST"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    [request setValue:[NSString stringWithFormat:@"%d", [requestData length]] forHTTPHeaderField:@"Content-Length"];
    [request setHTTPBody:requestData];

    NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    [connection start];

    if (image != nil) {

        AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
        [manager POST:[NSString stringWithFormat:@"%@FileUpload",urlBackend]
           parameters:nil
constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
            [formData appendPartWithFileData:dataImage name:@"image" fileName:ImageName mimeType:@"image/jpg" ];
        }
              success:^(AFHTTPRequestOperation *operation, id responseObject) {
            NSLog(@"Success: %@", responseObject);
        }
              failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            NSLog(@"Error: %@", error);
        }];
    }
}

回答1:

A couple of thoughts:

  1. You say:

    The iOS client is sending the information of the post via json and after the info is sent, it starts to send the image via multipart form using AFNetworking.

    Technically, you're not waiting for the information to be sent, but you're doing these concurrently. Do you want these to be concurrent? Or sequential? Or why not just a single request that posts the information as well as the image?

  2. I'd suggest using AFNetworking for both requests. You've got a powerful framework for managing network requests, and it feels awkward to see hairy NSURLConnection code in there.

    If you keep the NSURLConnection code in there, note that you do not want to start a NSURLConnection, unless you used initWithRequest:delegate:startImmediately: with NO for that last parameter. You're effectively starting it twice, which can cause problems. I'd suggest removing the start call.

  3. Setting all of that aside, what you want to do is to add a completion block parameter to your method, e.g., something like:

    + (void)addPostToServerAddtext:(NSString *)text addimage:(UIImage *)image addbeach:(NSString *)beach location:(NSString*)location completion:(void (^)(id responseObject, NSError *error))completion
    {
        // ...
    
        if (image != nil) {
    
            AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
            [manager POST:[NSString stringWithFormat:@"%@FileUpload",urlBackend] parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
                [formData appendPartWithFileData:dataImage name:@"image" fileName:ImageName mimeType:@"image/jpg" ];
            } success:^(AFHTTPRequestOperation *operation, id responseObject) {
                if (completion) completion(responseObject, nil);
            } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                if (completion) completion(nil, error);
            }];
    
        }
    }
    

    You'd then invoke that like so:

    [Persistence addPostToServerAddtext:text addimage:image addbeach:nil location:annotation completion:^(id responseObject, NSError *error) {
        if (error) {
            // handle error
            return
        }
    
        // otherwise use the responseObject
    }];
    

    Now, I don't know what parameters you want to return in your completion block (I'm assuming you wanted to return what the AFHTTPRequestOperationManager did), but just change the parameters for that completion block as suits your needs.

  4. Unrelated to your original question, but I notice that you're building jsonRequest like so:

    NSString *jsonRequest = [NSString stringWithFormat:@"{\"Date\":\"%@\"...."];
    

    That's a little risky if any of those fields include user supplied information (e.g. what if the user used double quotes in the information provided). I'd suggest you build a dictionary, and then build the jsonRequest from that. It will be more robust. Thus:

    NSDictionary *dictionary = @{@"Date"    : date,
                                 @"Message" : message};
    NSError *error = nil;
    NSData *request = [NSJSONSerialization dataWithJSONObject:dictionary options:0 error:&error];
    if (error)
        NSLog(@"%s: dataWithJSONObject error: %@", __FUNCTION__, error);
    

    Or, if you use AFNetworking, I believe it will do this JSON conversion of your dictionary for you. But, bottom line, be very wary about creating JSON strings yourself, at least if the request might include any user supplied information.