Base64 encoded image corrupted during transfer to server - iphone

For some reason i can't upload a jpeg file to a server using the post method in NSURLRequest
I have an android app that uses the same php code and can upload and download images fine by converting the image to a byte array and then using base64 encoding to send and receive.
My iPhone app downloads the image fine, The php script encodes using base64 and i use a base64 decoder in my iPhone app which I then convert into an image. This works fine.
However the uploading of the image doesn't work.
I convert the image to a base64 encoded string (I've used a few different methods for the base64 encoding and they all give the same result) then I post to the server, decode on the server side and save to file on the server.
The resulting decoded file on the server is a corrupt jpeg image. The corrupt file size is 3 times as many bytes as it should be.
The base64 encoded string generated for the upload is also very different to the base64 encoded string generated when downloading the same jpeg file from the server (ie. the valid image file that I uploaded using an ftp service).
My code is shown below along with the php script to receive the image.
I believe there is something happening with escaping characters which is causing the base64 string to become corrupted during the transfer but can't work it out.
Why is my base64 string being corrupted during transfer?
NSString* comments = #"comments to go with image";
NSData *data = UIImageJPEGRepresentation(_imageView.image, 1);
NSString *base64EncodedImage = [NSString base64StringFromData: data length: data.length];
//load the team posts so we know what items have been posted and what haven't
NSMutableDictionary *postDict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
comments, #"comment",
base64EncodedImage , #"image",
nil];
NSMutableArray *parts = [[NSMutableArray alloc] init];
for (NSString *key in postDict) {
NSString *part = [NSString stringWithFormat: #"%#=%#", key, [postDict objectForKey:key]];
[parts addObject:part];
}
NSString *encodedDictionary = [parts componentsJoinedByString:#"&"];
NSData *postData = [encodedDictionary dataUsingEncoding:NSUTF8StringEncoding];
NSString* url = #"http://www.scroppohunt.com/upload.php";
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:10.0];
[request setHTTPMethod:#"POST"];
[request setValue:[NSString stringWithFormat:#"%d", postData.length] forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (connection) {
_data = [[NSMutableData data] init];
}
and the php script that receives the image
<?php
$comments = $_REQUEST['comment'];
//do some database stuff with the comments
////////////////////////////////////////
//save the file to the images folder
///////////////////////////////////////////
$base=$_REQUEST['image'];
if(strlen($base) > 0)
{
// base64 encoded utf-8 string
$binary=base64_decode($base);
$file = fopen("postImages/test.jpg", 'wb');
fwrite($file, $binary);
fclose($file);
}
//display the bae64 encoded string taht was uploaded
echo $base;
?>

Well, it's been a while when you asked this question, but if anyone (like me) find this question, this may help:
In your Base64 encoded image, you should replace all occurrences of "+" character, with "%" character. Translated into code, it would be:
NSString* encodedImageString = [base64EncodedImage stringByReplacingOccurrencesOfString:#"+" withString:#"%2B"];
Then, instead of adding base64EncodedImage in postDict, you should add encodedImageString.
That way, your postDict will look like this:
NSMutableDictionary *postDict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
comments, #"comment",
encodedImageString , #"image",
nil];
I think this will solve the problem, at least, was for me.
Cheers.

I also had this problem. For me, what was happening was that all the "+" were replaced with "space" after being sent to the server. There was no other such corruption:

Try Changing content type.
[request setValue:#"image/jpeg" forHTTPHeaderField:#"Content-Type"];

Related

Making JSON from NSDictionary with the chars "_" and "/"

EDIT 2: I no longer think "_" and "/" in the dictionary keys are the problem.
EDIT 3: I don't think the JSON is the problem any more, or the HTTP POST request. I don't know exactly what it is yet.
EDIT 4: Solved it. The data element had to be valid base64, or else couch refuses it. Didn't know that. Can't select my own answer yet.
I have a dictionary I want to convert to a JSON string.
This is the setup of my dictionary:
- (NSDictionary*) createDictionary
{
NSMutableDictionary *room = [[NSMutableDictionary alloc] init];
[room setObject:self.roomNumber forKey:#"roomNumber"];
[room setObject:self.floor forKey:#"floor"];
[room setObject:self.comment forKey:#"comment"];
[room setObject:self.type forKey:#"type"];
[room setObject:[NSNumber numberWithInt:self.status] forKey:#"status"];
[room setObject:[[NSMutableDictionary alloc] init] forKey:#"_attachments"];
NSMutableDictionary *attachments = [room objectForKey:[NSString stringWithFormat:#"_%#", #"attachments"]];
[attachments setObject:[[NSMutableDictionary alloc] init] forKey:#"picture"];
NSMutableDictionary *picture = [attachments objectForKey:#"picture"];
[picture setObject:#"text/plain" forKey:#"content_type"];
[picture setObject:#"BASE64" forKey:#"data"];
NSLog(#"Room: %#", room);
return room;
}
The problem is the "_" and "/" chars. When I print out the dictionary, it looks like this:
Room: {
"_attachments" = {
picture = {
"content_type" = "text/plain";
data = BASE64;
};
};
comment = 12;
floor = 12;
roomNumber = 12;
status = 0;
type = room;
}
Don't know why it ends up on one line there, but anyway, thats not important. The thing is that the keys with "_" or "/" ends up surrounded with "-marks, and my web endpoint receiving JSON can't read it. Does anyone know how I can solve this? I use couchdb and need the _ in from of the attachments key.
EDIT: I am using it, I pass my dictionary to NSJSONSerialization, calling the function dataWithJSONObject:options:error:. I store what I get from that function in a NSData object, and set it as the http body in the request I make. Content-Type is application/json. I also set the Content-length etc. Here is the function that makes the HTTP request. createDictionary is the function above.
// Make a JSON object from the dictionary
NSError *err;
NSData* jsonRoom = [NSJSONSerialization dataWithJSONObject:[room createDictionary]
options:NSJSONWritingPrettyPrinted
error:&err];
// Set up the http POST request
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]
initWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#%#", _baseURL, #"/peters_hotell"]]];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setValue:[NSString stringWithFormat:#"%d", [jsonRoom length]]
forHTTPHeaderField:#"Content-length"];
[request setHTTPBody:jsonRoom];
NSLog(#"%#", jsonRoom);
// Fire the http POST request
//[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:callback];`
Right now the sendRequest call is commented out, but when I use it, the server can't accept the request.
This is the response from couchdb:
Data: {
error = badmatch;
reason = false;
}
When I leave the attachments part out of the dictionary, couch does not complain. Basically what I am trying to create is this JSON (inline attachment): http://wiki.apache.org/couchdb/HTTP_Document_API#Inline_Attachments
The thing is that the keys [...] end up being surrounded with quotation marks, and my web endpoint receiving JSON can't read it.
No, it's the opposite: what you think is JSON isn't really JSON. JSON requires the keys to be quoted, it separates keys and values using colons (:) and not an equal sign, etc.
What you currently print out is the description of the NSDictionary object and it is not JSON. It's in the legacy NeXTSTEP-style property list format.
What you want to use is the NSJSONSerialization class.
When you are posting the url and Json Object, before posting it to server encode the URL string using the
stringByAddingPercentEscapesUsingEncoding
or
NSUTF8Encoding technique..
Thanks for the help! It led me to the answer. As it turns out, my POST request was refused because my data entry wasn't valid base64, so couch refused it.
Code:
#implementation NSString (NSString_Extended)
- (NSString *)urlEncoded
{
CFStringRef urlString = CFURLCreateStringByAddingPercentEscapes(
NULL,
(CFStringRef)self,
NULL,
(CFStringRef)#"!*'\"();:#&=+$,/?%#[]% ",
kCFStringEncodingUTF8 );
return [(NSString *)urlString autorelease];
}
#end

iOS Unable to store URL contents into array

I have to connect to a URL to check whether the records is empty. The response looks something like this:
<?xml version = "1.0" encoding = "UTF-8"?>
<find>
<record_id>1234</record_id>
<no_record>00001</no_record>
<entry_num>00001</entry_num>
<session-id>aijheifaohqrihelrkqn324tlejaofjaf</session-id>
</find>
My codes:
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init]
autorelease];
[request setURL:[NSURL URLWithString: finalSearchURL]];
// Content-Type related.
[request setValue:#"application/x-www-form-urlencoded"
forHTTPHeaderField:#"Content-Type"];
// Create Connection.
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (conn) {
// The connection was established.
NSMutableData *receivedData = [[NSMutableData alloc] initWithContentsOfURL:[NSURL URLWithString:request]];
NSLog( #"Data will be received from URL: %#", request.URL );
NSLog(#"Recieved Data 2: %#", receivedData);
}
else
{
// The download could not be made.
NSLog( #"Data could not be received from: %#", request.URL );
}
But it returns me:
Recieved Data : <3c3f786d 6c207665 7273696f 6e203d20 22312e30 2220656e 636f6469 6e67203d 20225554 462d3822 3f3e0a3c 66696e64 3e0a3c73 65745f6e 756d6265 723e3031 39303633 3c2f7365 745f6e75 6d626572 3e0a3c6e 6f5f7265 636f7264 733e3030 30303030 3030313c 2f6e6f5f 7265636f 7264733e 0a3c6e6f 5f656e74 72696573 3e303030 30303030 30313c2f 6e6f5f65 6e747269 65733e0a 3c736573 73696f6e 2d69643e 4d505843 33323433 58564336 4534454a 41464232 45473541 39374237 584e3832 43554631 4e314234 584e4c37 424c5947 4e533c2f 73657373 696f6e2d 69643e0a 3c2f6669 6e643e0a 20>
Can anyone help to tell me what am I doing wrong? This is my first attempt for getting response from a url please help thanks!
See the data as a string this way:
NSString *string = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
NSLog(#"the xml string is %#", string);
If the parsing goal is simple enough - like just to find the value of one tag - you can use string methods to parse. Otherwise, NSXMLParser or several other options are available.
To see if the string contains a substring, you can do something like this:
if (string) {
NSRange range = [string rangeOfString:#"<session-id>"];
if (range.location != NSNotFound) {
// session-id tag is at index range.location, so we know it's there
}
}
The method you used is to get the raw data from the url. You need a parser to convert the raw data to the understandable structure (probably NSDictionary rather than NSArray).
Apple has provided NSXMLParser for you to retrieve the xml structure from the url or you can find other xml parser libraries.
Actually, your code is returning the correct data. Since NSData can hold any kind of data, it will just display the hex value. If you convert the hex data to a sting, you'll see that it has the correct text.
Now, your code can be simplified a lot. All the code for setting up the NSURLConnection is not needed at all. All you need is the following line.
NSString *recievedText = [NSString stringWithContentsOfFile:finalSearchURL encoding:NSUTF8StringEncoding error:NULL];

adding + before using %2b using url encoding in iphone

I have an application where on a button click i am passing my server api method which calls the JSON post method and saves data to server database.Here i am saving my mobile number and emergency no to server database.My mobile number is in string format.In my mobile number string variable, my mobile number is getting saved which is in this format '+90-9491491411'.I am entering + and then code and then- and then number but when i send to the server database i am removing the - and sending the no to database but problem is in my server database + of mobile is not getting entered which i am entering .What may be the problem .I am using POST method to send the request .This is my code
-(void)sendRequest
{
NSString *newstring = txtMobile.text;
mobileValue = [newstring stringByReplacingOccurrencesOfString:#"-" withString:#""];
NSLog(#"%#",mobileValue);
NSString *newString1 = txtemergencyprovider.text;
emergencyNumber = [newString1 stringByReplacingOccurrencesOfString:#"-" withString:#""];
NSLog(#"%#",emergencyNumber);
if ([txtEmail.text isEqualToString:#""])
{
post = [NSString stringWithFormat:#"CommandType=new&ApplicationType=%d&FullName=%#&Mobile=%#&EmergencymobileNumber=%#&Latitude=%f&Longitude=%f&City=%#&MobileModel=Apple",applicationtype,txtFullname.text,mobileValue,emergencyNumber,latitude,longitude,txtCity.text];
NSLog(#"%#",post);
}
else {
post = [NSString stringWithFormat:#"CommandType=new&ApplicationType=%d&FullName=%#&Mobile=%#&EmergencymobileNumber=%#&Latitude=%f&Longitude=%f&City=%#&EmailAddress=%#&MobileModel=Apple",applicationtype,txtFullname.text,mobileValue,emergencyNumber,latitude,longitude,txtCity.text,txtEmail.text];
NSLog(#"%#",post);
}
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%d", [postData length]];
NSLog(#"%#",postLength);
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:#"http://myapi?RequestType=NEW"]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (theConnection) {
webData = [[NSMutableData data] retain];
NSLog(#"%#",webData);
}
else
{
}
}
//In my mobile number and emrgency number variable my no is in this format '+91986444711' but when the value is entered in server database + gets removed off .What may be the prob.
Unfortunately, NSString's -stringByAddingPercentEscapesUsingEncoding: will not convert the plus (+) sign into %2B because the plus sign is a valid URL character that is used to separate query parameters. What this usually means is that it gets converted to a space character by the web server.
The easiest way to replace the plus sign would be using NSString's stringByReplacingOccurrencesOfString:withString: to replace the + with %2B. For example:
mobileValue = [mobileValue stringByReplacingOccurrencesOfString:#"+" withString:#"%2B"];
The plus sign ("+") in URL means an encoded space (" ") and most likely your server will interpret it as a space. Change the plus character to %2B in the string before posting it. For a complete solution on URL encoding see this post: http://madebymany.com/blog/url-encoding-an-nsstring-on-ios
The plus sign means space in a post request. You need to convert the plus to a percent escape character. The easiest way to do this is as follows:
NSString* escapedMobileValue = [mobileValue stringByReplacingOccurencesOfString: #"+" withString: #"%2b"];
This will turn the + into %2b. Probably the server will automatically reverse the encoding for you.
(Edited in line with mttrb's comment)

iphone download image with variable using ASIHTTPRequest

I got stuck something annoying.
I uploaded images to server it's ok.
However, when i try to download image from server i want to get image with string variable. here is my php code server side
....
echo "image_getting;";
$fullPath = $_POST['download_image'];
echo file_get_contents($fullPath);
....
if i remove first line from my server [request responseData] working correctly.
However, i want to also get "image_getting;" because i am also downloading some different types.
if i can clear, iphone side
when i say
- (void)requestFinished:(ASIHTTPRequest *)request
{
NSString *responseString = [request responseString];
NSLog(#"%#",responseString);
NSArray *resultResponseArray = [responseString componentsSeparatedByString:#";"];
if([[resultResponseArray objectAtIndex:0] isEqualToString:#"image_getting"])
{
NSData *responseData = [request responseData];
UIImage * image = [UIImage imageWithData:aData];
.......
}
else if...
....
}
as you can see above response data also taking echo "image_getting" how do i split it.
I tried to convert and split them it didn't work.
I should say, i am very bad with php.
I will be appreciated, if someone help me.
Thanks
EDIT:Solution Php side
header('X-Return: image_getting;');
$fullPath = $_POST['download_image'];
echo file_get_contents($fullPath);
iphone side
NSLog(#"header %#",[[request responseHeaders] objectForKey:#"X-Return"]);
Why don't you put the "image_getting" into a HTTP header in the response instead? That would make the parsing much easier - you can then retrieve the header:
NSString *whatever = [[request responseHeaders] objectForKey:#"X-Whatever"];
and then [request responseData] will only contain the image.

iPhone encoding of non latin characters

I am trying to parse a JSON response of a GET request. When the characters, are latin no problem.
However when they are not latin the message doesn't come out correctly. I tried greek and instead of "πανος" i get "& pi; & alpha; & nu; & omicron; & sigmaf;"
The code I use for parsing the response is:
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSLog(#"response %#", responseString);
// array from the JSON string
NSArray *results = [responseString JSONValue];
When I try to read the response from a website using ajax, everything is fine. The same applies when trying to send a GET request to the application servers with data from iphone. So when i transmit data to the server and read it from the website everything is fine. When i try to show the same data in the app, "Houston we have a problem".
Any clues?
EDIT: To avoid misunderstandings, it's not an issue of HTML, I just point out that for some readon utf-8 characters here are encoded correctly and automatically eg. "&pi" will be converted to "π", however objective c doesn't seem to do this on its own
There is a confusion I think.
π is an HTML entity which is unrelated to text encoding like UTF8 / Latin.
Read wikipedia for details about...
You need a parser to decode these entities like the one previously mentioned by Chiefly Izzy:
NSString+HTML category and method stringByReplacingHTMLEntities
Look at Cocoanetics NSString+HTML category and method stringByReplacingHTMLEntities method. You can find it at:
https://github.com/Cocoanetics/NSAttributedString-Additions-for-HTML/blob/master/Classes/NSString%2BHTML.m
Here's a pretty decent list of lot of HTML entities and their corresponding unicode characters.
Try to use this snippet of code:
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSString *decodedString = [NSString stringWithUTF8String:[responseString cStringUsingEncoding:[NSString defaultCStringEncoding]]];
NSLog(#"response %#", decodedString);
// array from the JSON string
NSArray *results = [decodedString JSONValue];
I have faced the same problem, but I solved it by changing the JSON parser. I have started using the SBJSONParser, and now I am getting the appropriate results. This is the code snippet, I have used
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
[request setURL:[NSURL URLWithString:urlString]];
[request setHTTPMethod:#"POST"];
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSString *returnString = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
SBJSON *parser=[[SBJSON alloc]init];
NSArray *JSONData = (NSArray*)[parser objectWithString:returnString error:nil];