In order to improve the client/server behaviour, I'm looking for adapt my iphone client code to proceed ziped responses.
The server adapt the SOAP response ziped.
I was looking how to uncompress the response but didn't work for me.
The first solution I studied was the ZipArchive, explained here, solution (from minizip) but it is focus on filesystem compression.
And I just need to uncompress a NSString.
After that I checked this second approach:
NSData *decodedGzippedData = [NSData dataFromBase64String:encodedGzippedString];
NSData* unGzippedJsonData = [ASIHTTPRequest uncompressZippedData:decodedGzippedData];
NSString* unGzippedJsonString = [[NSString alloc] initWithData:unGzippedJsonData encoding:NSASCIIStringEncoding];
But didn't work for me, because in the actual version the NSData dataFromBase64String didn't exists.
Now I'm working with the third response of the previous question, anybody knows which library or framework I need to install in order to import Base64.h and NSData+Compression.h ¿? Used in this other potencial solution
The solution was the next.
Install the next libraries to your project:
Base64.h // You can find it here
NSData+Compression.h // You can find it here
Use the code of one of the previous solutions
#import "Base64.h"
#import "NSData+compression.h"
...
// decoding the base64 ziped message
Byte inputData[[stringValue lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];
[[stringValue dataUsingEncoding:NSUTF8StringEncoding] getBytes:inputData];
size_t inputDataSize = (size_t)[stringValue length];
size_t outputDataSize = EstimateBas64DecodedDataSize(inputDataSize);
Byte outputData[outputDataSize];//prepare a Byte[] for the decoded data
Base64DecodeData(inputData, inputDataSize, outputData, &outputDataSize);
// inflate the original string using gzip
NSData *theData = [[NSData alloc] initWithBytes:outputData length:outputDataSize];
NSData* result = [theData gzipInflate];//make bigger==gunzip
// Return the result
return [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];
Related
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"];
Hello I would like to run a thread and check the current downloaded size of a file.
This is what I use
UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://lasp.colorado.edu/home/wp-content/uploads/2011/03/suncombo1.jpg"]]];
NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *jpegFilePath = [NSString stringWithFormat:#"%#/test.jpeg",docDir];
NSData *data2 = [NSData dataWithData:UIImageJPEGRepresentation(image, 1.0f)];//1.0f = 100% quality
[data2 writeToFile:jpegFilePath atomically:YES];
downloadStatus.text =[NSString stringWithFormat:#"size: %zd", malloc_size(data2)];
[image release];
I have also tried to change malloc_size(data2) into image but again it is not the real result. I know this does not have thread and do not check during the download process but what am I supposed to use here to see the file size?
A couple of observations:
Your question presumed that your attempts to retrieve the size of the NSData were failing. They are not. The correct way to get the size of a NSData is via length.
Your confusion, though, stems from a faulty assumption that taking an externally generated JPEG on a roundtrip through UIImage and UIImageJPEGRepresentation would yield the identical NSData. This would have been extraordinarily unlikely. There are too many different JPG settings that could have changed (see the JPEG Wikipedia page). We certainly don't know what settings that original file used. I know that UIImage and/or UIImageJPEGRepresentation changed the color space of the file. I'd wager it's doing a lot of other things, too.
So your results are correct. The original file was 2.6mb and the resulting file was 4.5mb. If you change the compressionQuality from 1.0 to 0.99, the resulting file is only 1.4mb! But if you want the original file, just save it first (like I do below).
Consider the following code which downloads the image file, saves it, loads it into a UIImage, re-extracts it via UIImageJPEGRepresentation, and saves another copy of the image:
// let's make filenames where we'll store the files
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *suncomboOrig = [documentsPath stringByAppendingPathExtension:#"suncombo1-orig.jpg"];
NSString *suncomboReprocessed = [documentsPath stringByAppendingPathExtension:#"suncombo1-reprocessed.jpg"];
// let's download the original suncombo1.jpg and save it in Documents and display the size
NSURL *url = [NSURL URLWithString:#"http://lasp.colorado.edu/home/wp-content/uploads/2011/03/suncombo1.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
NSLog(#"original = %d", [data length]);
[data writeToFile:suncomboOrig atomically:NO];
// let's load that into a UIImage
UIImage *image = [UIImage imageWithData:data];
// let's extract data out of the image and write that to Documents, too, also logging the size of that
NSData *data2 = UIImageJPEGRepresentation(image, 1.0);
NSLog(#"reprocessed = %d", [data2 length]);
[data2 writeToFile:suncomboReprocessed atomically:NO];
What that does is it reports:
2012-12-13 22:30:39.576 imageapp[90647:c07] original = 2569128
2012-12-13 22:30:40.141 imageapp[90647:c07] reprocessed = 4382876
So the first file I saved (which I suspect is identical to what's on your server) was 2.5mb, and the file after doing a roundtrip to a UIImage and re-extracted via 4.3mb. If I look at the two files that the above code saved, I can confirm that these NSData sizes are correct.
My original answer was predicated on the presumption that the OP was either unable to retrieve the size of a NSData or that there was some subtle issue underlying the simple question (such as wanting to get the size before the download commenced). Anyway, I've expanded my answer above, but I'll keep my original answer for historical purposes:
Original Answer:
The NSData property length tells you how many bytes were downloaded. E.g. [data2 length].
If it's really big, you can use NSURLConnection to download it asynchronously, which, depending upon your web server, may provide total file size before the download commences in the method didReceiveResponse (with the expectedContentLength property in the NSHTTPURLResponse *response parameter).
The other nice thing about NSURLConnection downloading is that you don't have to load the entire file in memory as you're downloading it, but rather you can stream it directly to persistent storage, which is especially useful if you're downloading multiple large files at the same time. If you're downloading a reasonably sized file, using NSURLConnection to download is overkill, but it can be nice when downloading large files and you want a progress indicator (or want to get the file size before the download commences).
But if you just want to know how many bytes were downloaded to your NSData, use length.
You can just use the C FILE class to get the file size.
FILE * handle = fopen([jpegFilePath UTF8String], "r");
fseek(handle, EOF); // seek to end of file
int size = ftell(handle); // returns position in bytes
fclose();
Because you say "current" size and mention a thread, I'm guessing you're trying to determine the file size as it is received. In that case, you can get the thread for free from an NSURLConnection, and you can get the data size from the delegate methods as it's received...
Create an instance variable for the downloaded data:
#property (nonatomic, strong) NSMutableData *data;
Create and launch a connection:
NSURL *url = [NSURL URLWithString:#"http://lasp.colorado.edu/home/wp-content/uploads/2011/03/suncombo1.jpg"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
self.data = [NSMutableData data];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
Implement the required methods in NSURLConnectionDataDelegate. For your question, the special part is this:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.data appendData:data];
NSUInteger bytesSoFar = [self.data length];
// you're on the app main thread here, so you can do something
// to the UI to indicate progress
downloadStatus.text = [NSString stringWithFormat#"size: %d", bytesSoFar];
}
A good doc on the rest of the protocol is here. When the connection is complete, you can create the image as you did with the dataWithContentsOfURL...
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
UIImage *image = [[UIImage alloc] initWithData:self.data];
downloadStatus.text = [NSString stringWithFormat#"size: %d", [self.data length]];
}
using iphone sdk 4.0. The callback for an http request gives data as an NSData object
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// Append the data received to our data
[theData appendData:data];
}
In my php script on the server i am returning an array as follows
var_dump($array).
How do i get my array back from the NSMutableData object 'theData' obove on my iphone.
Thanks
You have a string describing your array (or maybe several arrays?) stored as a sequence of bytes in your NSMutableData object. In order to turn it back into an array you're going to need to parse the var_dump output, which is likely to be arduous.
If you can find a library (or roll your own code) to return your data in Apple plist format, your task will be much easier: you can use
[NSPropertyListSerialization propertyListFromData:mutabilityOption:format:errorDescription:]
which takes an NSData (or NSMutableData) pointer as its first argument. Try http://code.google.com/p/cfpropertylist/ for a starting point.
From the example code at the cfpropertylist page:
$plist = new CFPropertyList();
$td = new CFTypeDetector();
$guessedStructure = $td->toCFType( $array );
$plist->add( $guessedStructure );
// and then return the plist content with
$plist->toXML()
and in your iOS code:
NSString *errorString = nil;
NSArray *array = [[NSPropertyListSerialization
propertyListFromData:theData
mutabilityOption:NSPropertyListImmutable
format:nil
errorDescription:&errorString] retain];
I would likely use YAJL on iOS, and $var = json_encode($array); in the PHP. Then in the iOS, I would parse that content from the NSData input like:
YAJLParser *parser = [[YAJLParser alloc] initWithParserOptions:YAJLParserOptionsAllowComments | YAJLParserOptionsCheckUTF8];
parser.delegate = [[[MyArrayParserDelegate alloc] init] autorelease];
[parser parse:data];
NSArray *thePhpArrayReceived = parser.delegate.resultantArray;
Please check out how to structure the delegate, and get YAJL here : Get YAJL + Readme
PHP outputs text so you will have to read that NSData as NSString and then parse out the array data according to the format specified by var_dump. As a starting point, the following code snippet should print out the array (as text) to your console:
NSString * dump = [[NSString alloc] initWithData:theData
encoding:NSUTF8StringEncoding];
NSLog(#"%#", dump);
[dump release];
As Seamus Campbell points out, there are better ways of doing this. Another option would be to output XML from your PHP script, and then use Cocoa's XML parsing methods to retreive the array.
I access a RESTFUL url and get back results. The results are in JSON. I turn the response into a string via:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *json = [[NSString alloc] initWithBytes:[self.receivedData mutableBytes] length:[self.receivedData length] encoding:NSUTF8StringEncoding];
The json variable has a value of 0x0. When I mouse over it, I see <Invalid CFStringRef>. How can I debug this to tell why it is invalid? I render the JSON given back through the browser in A JSON parser. That checks out fine.
Results are given back by entering an ID in the URL. Other IDs return results without issue. The result set is fairly large.
First I would use initWithData:encoding: to setup the NSString. Small difference, but that method is there for a reason.
Then, I would do a hexdump of self.receivedData to see what is actually in there. If that data is not properly UTF8 encoded then the initWithData:encoding: will fail.
(Google for NSData hex dump to find other people's utility functions to do this)
I have found that sometimes web services are sloppy with their encoding. So I usually implement a fallback like this:
NSString* html = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
if (html == nil) {
html = [[NSString alloc] initWithData: data encoding: NSISOLatin1StringEncoding];
if (html == nil) {
html = [[NSString alloc] initWithData: data encoding: NSMacOSRomanStringEncoding];
}
}
It is kind of sad that this is required but many web services are not written or configured properly.
Use NSLog to look at the bytes.
Now I know that xCode automaticly does the GZip decrompession for you within:
NSData *data = [NSData dataWithContentsOfURL:URL];
And it does work if I point to a Gzip file on my server. But since my content is dynamic, I have a PHP script that rather then create a gzip file like so:
$zp = gzopen($file, "r");
$data = gzread($zp, $filesize);
gzclose($zp);
I encode my own data with:
echo gzencode($data, 9);
With this I add the following headers:
header("Content-Type: application/x-gzip");
header("Content-Encoding: gzip");
header("Accepts-Encoding: gzip");
When I browse to the URL, my browser wants to download the file automatically and I am able to unzip it on my Mac and view it's content. However when I try to read it through xCode it won't work.
NSData *data = [NSData dataWithContentsOfURL:URL];
NSString *content = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog (content); //returns only data when pointed directly to a Gzip file
Am I forgetting something?
If you download something with content type application/x-gzip, the url loading system will not decompress it for you. I think the data that you received is still gzip encoded.
You can use my NSData additions to deal with this. See http://github.com/st3fan/cocoa-utils/blob/master/src/NSDataGZipAdditions.m