0

I need to upload an image to my ASP.Net website, i have got it to work with NSURLConnection but when i needed to upload extremely big files, it crashes due to the lack of ram to transfer the file to NSData then upload it. So I found out about the new API NSURLSession and the method uploadTaskWithRequest:withFile to allow bigger files to be transferred. I have changed the max file limit on the ASP.NET server to allow huge files, and tested it and it works with large files (100MB+), but at some point the phone does not have enough ram to allocate the NSData of the file, when I use NSURLConnection. I have tried NSURLSession numerous times with no success.

Heres an example of what i've tried:

    NSString *urlString = [NSString stringWithFormat:@"https://examplesecuresite.com/Index.aspx?username=%@", username];
    NSMutableURLRequest *request =
    [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:urlString]];
    [request setHTTPMethod:@"PUT"];

    // 3
    NSData *noteContents = UIImageJPEGRepresentation(image, 1);

    // 4
    NSURLSession *session = [[NSURLSession alloc] init];
    NSURLSessionUploadTask *uploadTask = [session
                                          uploadTaskWithRequest:request
                                          fromData:noteContents];
    [uploadTask resume];
Aaron
  • 7,055
  • 2
  • 38
  • 53
  • By the way, there are a whole bunch of possible issues around the nature of the request that we can't answer on the basis of what's been provided thus far. If you provided a copy of the working `NSURLConnection` source, that might be helpful in our confirming that no problems were introduced in the process of converting to `NSURLSession`. – Rob Mar 09 '14 at 05:53

1 Answers1

1

A couple of reactions:

  1. This is not how you create a NSURLSession. You should either use the shared session:

    NSURLSession *session = [NSURLSession sharedSession];
    

    Or create a configuration with a delegate (obviously, implementing the NSURLSessionTaskDelegate and NSURLSessionDelegate protocols) and use that:

    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
    NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request
                                                               fromData:noteContents];
    

    Or create a configuration without a delegate (but create upload task with completion block) and use that:

    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
    NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:noteContents completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        if (error) {
            NSLog(@"%@", error);
            return;
        }
    
        // do whatever other checking you want of the response here
    }];
    
  2. I don't know why you're retrieving the NSData from the UIImage. If this is a UIImage that you created dynamically (e.g. a screen snapshot), then fine, that's what you have to do. But if you can go back to the original asset (e.g. the original asset from ALAssetLibrary, the file we used in imageWithContentsOfFile or imageWithContentsOfURL, etc.), often that will be more efficient (both smaller asset as well as you can upload directly from persistent storage).

    Unfortunately, using UIImageJPEGRepresentation with a quality setting of 1.0 often retrieves a much larger NSData than the original asset. (Plus we preserve any meta data, if any, its far more memory efficient, etc.) You can use a factor of 0.9 or 0.8, which is lossy, but will result in a much smaller file. If you really want something lossless, often UIImagePNGRepresentation will generate a smaller file than UIImageJPEGRepresentation with a quality setting of 1.0.

  3. If you're worried about a memory efficient approach, this seems no more efficient. You're uploading a NSData in RAM. Any approach that consists of loading the data into a NSData will be no more memory efficient than your other approach, most likely.

    If you want to be more memory efficient, avoid loading the image into an NSData at all (if you can), and just use the uploadTaskWithRequest:fromFile: with the original asset.

  4. By the way, if you had the image in a file in persistent storage, it doesn't strike me that you have to make the shift to NSURLSession solely to address the memory issues. Strikes me that you could have use the delegate-based NSURLConnection, returning a NSInputStream from connection:needNewBodyStream: rather than loading the image into a NSData.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Before when I was uploading using a NSURLConnection I had a multi-part document when i was uploading, but in this case i'm just uploading the raw file, which of course is throwing an error on my asp.net document, is there a way to send the body data and the file when using `uploadTaskWithRequest:fromFile:`, or just fix asp.net to accept the raw file? Thanks for the help :) – user3001058 Mar 09 '14 at 06:25
  • @user3001058 If your server code is expecting a multi-part document, then you have to send a multi-part document. If you want to change it to accept a raw file, then you have to change _both_ the server code and the client code to accept and send raw file. I'm not sure, but it seems to me that sending the raw image file is risky, because I'm not sure how the server would know it got it all. – Rob Mar 09 '14 at 06:39
  • @user3001058 If your main concern is RAM, I'd be inclined to keep the message format the same, but fix your client code to stream it, rather than staging it in a `NSData`. Can be done in either `NSURLConnection` or `NSURLSession` (but I think you'd need to either stage your own file with the full multipart document/payload, or perhaps write your own `NSInputStream` subclass that builds the message body dynamically). But all of this seems somewhat moot if you're loading image with `UIImageJPEGRepresentation`. – Rob Mar 09 '14 at 06:40
  • well i'm also doing videos, uploading the raw video (500MB +) – user3001058 Mar 09 '14 at 07:10