Allocated data of NSMutableRequest object's body not being released - ios

In my app I have to perform a multipart POST request. In the request I have to send a file that is stored in the device's photo Library. After it is finished uploading and received the returned data, I send the request a release message, but Instruments shows me the data persists in the device's memory.
This is how I create the request object:
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
[request setHTTPShouldHandleCookies:YES];
[request setTimeoutInterval:30];
[request setHTTPMethod:#"POST"];
NSString *boundary = #"---------------------------14737809831466499882746641449";
// set Content-Type in HTTP header
NSString *contentType = [NSString stringWithFormat:#"multipart/form-data; boundary=%#", boundary];
[request setValue:contentType forHTTPHeaderField: #"Content-Type"];
// post body
NSMutableData *body = [[NSMutableData alloc] init];
// add parts (all parts are strings) `parts` is a NSDictionary object
for(id key in parts) {
[body appendData:[[NSString stringWithFormat:#"--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"%#\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"%#\r\n", [parts objectForKey:key]] dataUsingEncoding:NSUTF8StringEncoding]];
}
// add files data - `file` is a ALAssetRepresentation object
NSData *fileData;
NSString *filename = [file filename];
Byte *buffer = (Byte*)malloc(file.size);
NSUInteger buffered = [file getBytes:buffer fromOffset:0.0 length:file.size error:nil];
fileData = [[NSData alloc] initWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];
if (fileData) {
[body appendData:[[NSString stringWithFormat:#"--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
if ([requestType isEqualToString:PUBLISH_TYPE_VC_MANDA]) {
[body appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"attachment\"; filename=\"%#\"\r\n", filename] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"Content-Type: image/jpeg\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
} else {
[body appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"file\"; filename=\"%#\"\r\n", filename] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
}
[body appendData:fileData];
[body appendData:[[NSString stringWithFormat:#"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
}
[fileData release];
[body appendData:[[NSString stringWithFormat:#"--%#--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
// setting the body of the post to the reqeust
[request setHTTPBody:body];
[body release];
// set the content-length
NSString *postLength = [NSString stringWithFormat:#"%d", [body length]];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"Keep-Alive" forHTTPHeaderField:#"Connection"];
// set URL `adress` is a NSURL object
[request setURL:address];
//set cookie
if (storedCookie) {
NSDictionary *properties = [NSDictionary dictionaryWithObjectsAndKeys:[storedCookie name], NSHTTPCookieName, myDomain, NSHTTPCookieDomain,#"/",NSHTTPCookiePath, [storedCookie value], NSHTTPCookieValue, nil];
NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:properties];
NSArray* cookies = [NSArray arrayWithObjects: cookie, nil];
NSDictionary * headers = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
[request setAllHTTPHeaderFields:headers];
}
After this, and after I received the response, i perform [request release] but the allocated bytes stay in memory.
I ran Instruments in the Allocations mode, and it is claiming that there is an object of category Malloc 91MB (the size of the ALAsset I'm uploading). By looking at the Call tree, the Symbol that's taking up all the memory is [NSConcreteMutableData appendBytes:length:] inside the method described above.
Why isn't it freeing the bytes ? And more importantly, why does it have to move the data to the device's memory ? I've seen apps like Facebook upload very large videos from the gallery without any memory problems.
Thank you very much for your attention, any help or directions would be greatly appreciated
EDIT: Further investigation showed me that when I use [request release] the request object is released, but it's body isn't. Pretty weird behaviour...
EDIT2: I explicitly used [request.HTTPBody release]; and the bytes were released. I still cannot believe that [request release] wasn't freeing it's body. Could this be a bug ?

Your request has a retainCount of 2:
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init]; // 1
...
[request retain]; // 2
Then if you do [request release], it's decreased by one.
The solution is not to do [request retain].

After further investigating, the memory wasn't being released because the request's property HTTPBody needed to manually be released (see my second edit).
As to sending large chunks of data in a NSRequest, there is an easier way that won't consume the device's memory. You have to write the file to the disk, and then map it. I have described it in details in this SO answer. Hope it helps someone

Related

Upload multipart image with NSData in JSON (HTTP body)

I'm trying to upload a multipart image to server where image is converted to NSData and this NSdata is sent a parameter in HTTP body in a JSON string. Unfortunately the app crashes with this message:
**'NSInvalidArgumentException', reason: 'Invalid type in JSON write (NSConcreteMutableData)'**
I understand that the problem is with JSON. The sample code:
-(void) uploadmultipartimage {
NSString * urlimageupload = [NSString stringWithFormat:#"%#api/mobile_profiles/avatar_upload",URLPrefixCertintell];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:urlimageupload]];
NSData *imageData = UIImageJPEGRepresentation(_profileimage, 1.0);
[request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
[request setHTTPShouldHandleCookies:NO];
[request setTimeoutInterval:60];
[request setHTTPMethod:#"PUT"];
NSString *boundary = #"unique-consistent-string";
// set Content-Type in HTTP header
NSString *contentType = [NSString stringWithFormat:#"multipart/form-data; boundary=%#", boundary];
[request setValue:contentType forHTTPHeaderField: #"Content-Type"];
// post body
NSMutableData *body = [NSMutableData data];
// add params (all params are strings)
[body appendData:[[NSString stringWithFormat:#"--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=%#\r\n\r\n", #"imageCaption"] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"%#\r\n", #"Some Caption"] dataUsingEncoding:NSUTF8StringEncoding]];
// add image data
if (imageData) {
[body appendData:[[NSString stringWithFormat:#"--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=%#; filename=imageName.jpg\r\n", #"imageFormKey"] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"Content-Type: image/jpeg\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:imageData];
[body appendData:[[NSString stringWithFormat:#"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
}
[body appendData:[[NSString stringWithFormat:#"--%#--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
NSDictionary *jsonString = #{#"user":#{#"user_token":[[NSUserDefaults standardUserDefaults] stringForKey:#"user_token"]},#"api_key":APIKey,#"profile":body};
//***Code Crashes here**//
NSData *postData = [NSJSONSerialization dataWithJSONObject:jsonString options:0 error:nil];
//
// setting the body of the post to the reqeust
[request setHTTPBody:postData];
// set the content-length
NSString *postLength = [NSString stringWithFormat:#"%lu", (unsigned long)[body length]];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue currentQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if(data.length > 0) {
//success
}
}];
}
You need to convert your data to NSString:
NSString *g=[[NSUserDefaults standardUserDefaults] stringForKey:#"user_token"];
if (!g)
g=#"Default Token";
NSString *base64Body = [body base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
NSDictionary *jsonString = #{#"user":#{#"user_token":g},#"api_key":APIKey,#"profile":base64Body};
Then you can always argue about the possibility of nil when reading the "user_token".
Take care,
/Anders.

I am uploading one file successfully by using following code how can i use the same code upload 3 file. I do not want to use code again again

NSString *filenames = [NSString stringWithFormat:#"front_img"];
NSString *urlString = #"http://ccccc.com/upfile.php";
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:urlString]];
[request setHTTPMethod:#"POST"];
NSString *boundary = #"---------------------------14737809831466499882746641449";
NSString *contentType = [NSString stringWithFormat:#"multipart/form-data; boundary=%#",boundary];
[request addValue:contentType forHTTPHeaderField: #"Content-Type"];
NSMutableData *body = [NSMutableData data];
[body appendData:[[NSString stringWithFormat:#"\r\n--%#\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"filenames1\"\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[filenames1 dataUsingEncoding:NSUTF8StringEncoding]];
/*Sending First Image start here*/
[body appendData:[[NSString stringWithFormat:#"\r\n--%#\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithString:[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"userfile\"; filename=\"%#.jpg\"\r\n", filenames]] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[NSData dataWithData:imageData1]];
[body appendData:[[NSString stringWithFormat:#"\r\n--%#--\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
/*Sending First Image ends here*/
// setting the body of the post to the reqeust
[request setHTTPBody:body];
// now lets make the connection to the web
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSString *returnString = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
NSLog(#"%#",returnString);
A typical upload http body will look like:
--BOUNDARY_TEXT
Content-Type/Content-Disposition info key values
Base64 encoded file data ...
--BOUNDARY_TEXT
Content-Type/Content-Disposition info key values
Base64 encode file data.
--BOUNDARY_TEXT
You've done the first one, just continue append files according to the format seperatored by the BOUNDARY.

how to upload image to server in ios?

I want to upload one image to server using http post method.
the requirement is like in the body data should be attached as byte stream.
I converted image to NSData and attached that data to body of NSUrlrequest.
but i am getting 404 in status code of response.
NSMutableData *body = [NSMutableData data];
[body appendData:[NSData dataWithData:UIImageData]];
[request setHTTPBody:body];
but i am getting 404 error in status code.
is byte stream is same as NSData ?
if not then how to send byte stream data to server using NSURLConnection ?
Thanks in advance.
What about the URL you're using to send the image to?
A 404 error code translates to Not Found (http://en.wikipedia.org/wiki/HTTP_404), so the first step should be making sure yore using a valid URL.
Try to make the upload that way:
NSData *yourData = //load your data here;
NSMutableURLRequest *request= [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:UPLOAD_URL]];
[request setHTTPMethod:#"POST"];
NSString *boundary = #"---------------------------14737809831466499882746641449";
NSString *contentType = [NSString stringWithFormat:#"multipart/form-data; boundary=%#",boundary];
[request addValue:contentType forHTTPHeaderField: #"Content-Type"];
NSMutableData *postbody = [NSMutableData data];
[postbody appendData:[[NSString stringWithFormat:#"\r\n--%#\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[postbody appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"%#\"; filename=\"%#\"\r\n", UPLOAD_FILE, UPLOAD_FILE] dataUsingEncoding:NSUTF8StringEncoding]];
[postbody appendData:[#"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[postbody appendData:[NSData dataWithData:yourData]];
[postbody appendData:[[NSString stringWithFormat:#"\r\n--%#--\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[request setHTTPBody:postbody];
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
[connection start];

Upload image file ios - Multipart format

Here is the webserice call
URL: http://mywedshare.com/wp-admin/admin-ajax.php?action=app_photoupload
gallery_ID,guestName, guestEmail, guestPasscode,photo_file (Multipart File Format)
I need to upload an UIImage. Im getting my NSData using the following line -
imageData = UIImageJPEGRepresentation([info objectForKey:UIImagePickerControllerOriginalImage], 1);
and uploading it using
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
//Set Params
[request setHTTPShouldHandleCookies:NO];
[request setTimeoutInterval:60];
[request setHTTPMethod:#"POST"];
//Create boundary, it can be anything
NSString *boundary = #"------VohpleBoundary4QuqLuM1cE5lMwCy";
// set Content-Type in HTTP header
NSString *contentType = [NSString stringWithFormat:#"multipart/form-data; boundary=%#", boundary];
[request setValue:contentType forHTTPHeaderField: #"Content-Type"];
// post body
NSMutableData *body = [NSMutableData data];
//Populate a dictionary with all the regular values you would like to send.?action=app_photoupload?
NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init];
[parameters setValue:#"app_photoupload" forKeyPath:#"action"];
[parameters setValue:galleryID forKey:#"gallery_ID"];
[parameters setValue:galleryName forKey:#"guestName"];
[parameters setValue:guestEmail forKey:#"guestEmail"];
[parameters setValue:guestPasscode forKey:#"guestPasscode"];
// add params (all params are strings)
for (NSString *param in parameters) {
[body appendData:[[NSString stringWithFormat:#"--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"%#\"\r\n\r\n", param] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"%#\r\n", [parameters objectForKey:param]] dataUsingEncoding:NSUTF8StringEncoding]];
}
NSString *FileParamConstant = #"photo_file";
NSData *imageData2 = imageData;
//Assuming data is not nil we add this to the multipart form
if (imageData2)
{
[body appendData:[[NSString stringWithFormat:#"--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"%#\"; filename=\"wedshare.jpg\"\r\n", FileParamConstant] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"Content-Type:image/jpeg\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:imageData2];
[body appendData:[[NSString stringWithFormat:#"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
}
//Close off the request with the boundary
[body appendData:[[NSString stringWithFormat:#"--%#--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
// setting the body of the post to the request
[request setHTTPBody:body];
// set URL
NSString *url = #"http://mywedshare.com/wp-admin/admin-ajax.php";
[request setURL:[NSURL URLWithString:url]];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
if ([httpResponse statusCode] == 200) {
[HUD showUIBlockingIndicatorWithText:#"Success"];
[HUD hideUIBlockingIndicator];
NSLog(#"success");
}
}];
Im getting Success but the image itself is not being uploaded onto the server. Please help me.
I'd suggest:
Run the app on the simulator and run Charles (or something like it) at the same time, and make sure the request looks ok. I don't see anything obviously wrong here from the client side. Still, Charles can tell us if the request looks ok.
I obviously cannot speak to the server side of it (e.g. the choice of the photo_file field name, the other properties, etc.). How confident are you regarding the API?
You are declaring success if the statusCode is 200. The 200 is a HTTP level status code that means is that the request was received and the server responded successfully, but it doesn't necessarily mean that the upload was successful. It looks like that URL returns some code, and you might want to examine that value before you start declaring success.

Uploading image to server via node.js from an iOS app

I am developing an iOS app and I am using node.js for server side scripting. I am facing problem in uploading image to server from iOS app. It is working fine if I am uploading image from webpage form. But if uploading from app side it is not working.
//test file
h3 Pic Upload
form(action='/pic_upload', method='post',enctype='multipart/form-data')
| user_pic:
input(type='file', name='user_pic')
input(type='submit')
//app.js
var userlogin =require('./routes/userlogin');
app.post('/pic_upload', userlogin.picUpload);
//userlogin.js
//picUpload function
exports.picUpload = function(req, res) {
console.log(req.files); // showing undefined, when called from IOS app
// pic upload script...
});
I have tried sending image from app side as data or file parameter but it did not work. How to send the file parameter from app side so that I can easily upload the image to server? Kindly suggest a way to tackle the problem.
This code works in my app. Try this.
NSData *imgData = UIImageJPEGRepresentation(newImg, 0.2);
NSString *str = #"displayImage";
NSString *urlString = #"http://some.url.com/post";
// create request
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
[request setHTTPShouldHandleCookies:NO];
[request setTimeoutInterval:30];
[request setURL:[NSURL URLWithString:urlString]];
[request setHTTPMethod:#"POST"];
NSString *boundary = [NSString stringWithFormat:#"---------------------------14737809831464368775746641449"];
// set Content-Type in HTTP header
NSString *contentType = [NSString stringWithFormat:#"multipart/form-data; boundary=%#", boundary];
[request setValue:contentType forHTTPHeaderField: #"Content-Type"];
// post body
NSMutableData *body = [NSMutableData data];
// add params (all params are strings)
[body appendData:[[NSString stringWithFormat:#"\r\n--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"currentEventID\"\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"52344457901000006" dataUsingEncoding:NSUTF8StringEncoding]];
// add image data
if (imgData) {
[body appendData:[[NSString stringWithFormat:#"\r\n--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
// [body appendData:[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"displayImage\"; filename=\"image.jpg\"\r\n"]];
[body appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"%#\"; filename=\"image.jpg\"\r\n", str] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"Content-Type: image/jpeg\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:imgData];
[body appendData:[[NSString stringWithFormat:#"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
}
[body appendData:[[NSString stringWithFormat:#"\r\n--%#--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
// setting the body of the post to the reqeust
[request setHTTPBody:body];
// set URL
[request setURL:[NSURL URLWithString:urlString]];
NSString *bodyStr = [[NSString alloc]initWithData:body encoding:NSUTF8StringEncoding];
NSLog(#" %#",bodyStr);
NSURLConnection *lot_Connection=[[NSURLConnection alloc]initWithRequest:request delegate:self];
if(lot_Connection)
{
webdata = [[NSMutableData alloc] init];
}

Resources