Loading large images using Base64 and JSON

I use this function to upload an image to the server using JSON . To do this, I first convert the image to NSData and then to NSString using Base64 . The method works fine when the image is not very large, but when I try to upload a 2 MB image, it crashes.

The problem is that the server does not receive my image, even if the didReceiveResponse method is didReceiveResponse , as well as didReceiveData , which returns (null) . At first I thought it was a timeout problem, but even when configured to 1000.0 it still doesn't work. Any ideas? Thank you for your time!

Here is my current code:

  - (void) imageRequest { NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.myurltouploadimage.com/services/v1/upload.json"]]; NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; NSString *path = [NSString stringWithFormat:@"%@/design%i.png",docDir, designNum]; NSLog(@"%@",path); NSData *imageData = UIImagePNGRepresentation([UIImage imageWithContentsOfFile:path]); [Base64 initialize]; NSString *imageString = [Base64 encode:imageData]; NSArray *keys = [NSArray arrayWithObjects:@"design",nil]; NSArray *objects = [NSArray arrayWithObjects:imageString,nil]; NSDictionary *jsonDictionary = [NSDictionary dictionaryWithObjects:objects forKeys:keys]; NSError *error; NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonDictionary options:kNilOptions error:&error]; [request setHTTPMethod:@"POST"]; [request setValue:[NSString stringWithFormat:@"%d",[jsonData length]] forHTTPHeaderField:@"Content-Length"]; [request setValue:@"application/json" forHTTPHeaderField:@"Accept"]; [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; [request setHTTPBody:jsonData]; [[NSURLConnection alloc] initWithRequest:request delegate:self]; NSLog(@"Image uploaded"); } - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { NSLog(@"didReceiveResponse"); } - (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { NSLog(@"%@",[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]); } 
+6
source share
2 answers

Finally, I decided to load the Base64 image, breaking it into smaller substrings. To do this, and since I needed a lot of NSURLConnections , I created a subclass called TagConnection , which gives a tag for each connection so that there is no possible confusion between them.

Then I created the TagConnection property in MyViewController in order to access it from any function. As you can see, there is a function -startAsyncLoad:withTag: that selects and inserts the TagConnection and -connection:didReceiveData: , which remove it when I receive a response from the server.

Referring to the -uploadImage function, firstly, it converts the image to a string, and then splits it and puts the pieces inside the JSON request. It is called until the variable offset is greater than the length of the string, which means that all the pieces have been loaded.

You can also prove that each piece was successfully loaded by checking the server response every time and only calling the -uploadImage function when it returns success.

Hope this was a helpful answer. Thank you

TagConnection.h

 @interface TagConnection : NSURLConnection { NSString *tag; } @property (strong, nonatomic) NSString *tag; - (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately tag:(NSString*)tag; @end 

TagConnection.m

 #import "TagConnection.h" @implementation TagConnection @synthesize tag; - (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately tag:(NSString*)tag { self = [super initWithRequest:request delegate:delegate startImmediately:startImmediately]; if (self) { self.tag = tag; } return self; } - (void)dealloc { [tag release]; [super dealloc]; } @end 

Myviewcontroller.h

 #import "TagConnection.h" @interface MyViewController : UIViewController @property (strong, nonatomic) TagConnection *conn; 

Myviewcontroller.m

 #import "MyViewController.h" @interface MyViewController () @end @synthesize conn; bool stopSending = NO; int chunkNum = 1; int offset = 0; - (IBAction) uploadImageButton:(id)sender { [self uploadImage]; } - (void) startAsyncLoad:(NSMutableURLRequest *)request withTag:(NSString *)tag { self.conn = [[[TagConnection alloc] initWithRequest:request delegate:self startImmediately:YES tag:tag] autorelease]; } - (void) uploadImage { NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.mywebpage.com/upload.json"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:1000.0]; NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; NSString *path = [NSString stringWithFormat:@"%@/design%i.png", docDir, designNum]; NSLog(@"%@",path); NSData *imageData = UIImagePNGRepresentation([UIImage imageWithContentsOfFile:path]); [Base64 initialize]; NSString *imageString = [Base64 encode:imageData]; NSUInteger length = [imageString length]; NSUInteger chunkSize = 1000; NSUInteger thisChunkSize = length - offset > chunkSize ? chunkSize : length - offset; NSString *chunk = [imageString substringWithRange:NSMakeRange(offset, thisChunkSize)]; offset += thisChunkSize; NSArray *keys = [NSArray arrayWithObjects:@"design",@"design_id",@"fragment_id",nil]; NSArray *objects = [NSArray arrayWithObjects:chunk,@"design_id",[NSString stringWithFormat:@"%i", chunkNum],nil]; NSDictionary *jsonDictionary = [NSDictionary dictionaryWithObjects:objects forKeys:keys]; NSError *error; NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonDictionary options:kNilOptions error:&error]; [request setHTTPMethod:@"POST"]; [request setValue:[NSString stringWithFormat:@"%d",[jsonData length]] forHTTPHeaderField:@"Content-Length"]; [request setValue:@"application/json" forHTTPHeaderField:@"Accept"]; [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; [request setHTTPBody:jsonData]; [self startAsyncLoad:request withTag:[NSString stringWithFormat:@"tag%i",chunkNum]]; if (offset > length) { stopSending = YES; } } - (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { NSError *error; NSArray *responseData = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error]; if (!responseData) { NSLog(@"Error parsing JSON: %@", error); } else { if (stopSending == NO) { chunkNum++; [self.conn cancel]; self.conn = nil; [self uploadImage]; } else { NSLog(@"---------Image sent---------"); } } } @end 
+8
source

Please do not think that this is the last option, this is just my observation.

I think you should send NSData to chunks instead of full Data. I saw such a methodology in the YouTube Video Uploading app. They send a large set of NSData (NSData video file) to pieces of many NSData.

They use the same methodology to load big data.

So what do google have to do about uploading Youtube API data. And you should look for this method, YouTube Uploader Uses.

I hope this can help you.

+2
source

All Articles