how to store data in NS-Mutable-data in did-receive-data and than write to file - iphone

I am stuck from 2 days i want to display downlaod progress bar.
I am sending json in post to server and in response server send me video data.
To display progress bar i write some logic code like
In didreceivedata method of ASIhttep i am appending receive data with global NSmutabledata and in request done method i write that global Nsmutalbedata into file.
but file is blank it wont get store into file.
I know ASIHttprequest is old library but everyone suggest me to use AFnetworking but I dont want to change the code because it will take so much time and i have to read documents again.
anybody can help me how can i append data and after downlaod done write that appended data to file??
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[[NSURL alloc] initWithString:#"http://testing.io/dev.php/video/verifyReceipt"]];
[request setDidReceiveDataSelector:#selector(request:didReceiveData:)];
[request setPostValue:resultAsString forKey:#"verify"];
[request setDidFinishSelector:#selector(requestDone:)];
[request setTimeOutSeconds:120];
[request setDelegate:self];
[request setNumberOfTimesToRetryOnTimeout:2];
[request setDownloadProgressDelegate:progressBar];
request.showAccurateProgress = YES;
[request startSynchronous];
}
-(void)request:(ASIHTTPRequest *)request didReceiveData:(NSData *)data
{
[videoData appendData:data];
NSLog(#"data is %#",data);
}
- (void)requestDone:(ASIHTTPRequest *)request
{
//[MBProgressHUD hideHUDForView:self.view animated:YES];
// SAVED PDF PATH
// Get the Document directory
NSString *documentDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
// Add your filename to the directory to create your saved pdf location
NSString* movLocation = [documentDirectory stringByAppendingPathComponent:[fileName stringByAppendingString:#".mov"]];
if(request.responseStatusCode==200)
{
[videoData writeToFile:movLocation atomically:NO];
NSLog(#"in request done sucsessfully downlaod and store in database %d",request.responseStatusCode);
[DBHelper savePurchaseId:fileName];
[self movieReceived];
}
else
{
NSLog(#"in request downlaod and store in database failed %#",request.responseHeaders);
}
}

It's better to use asynchronous requests for the task like this. You can use the same ASIHTTPRequest class, but with block approach. Try to write the code similar to this:
-(void) verifyReceipt {
NSURL *theURL = [NSURL URLWithString:#"http://testing.io/dev.php/video/verifyReceipt"];
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:theURL cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10.0f];
[theRequest setHTTPMethod:#"POST"];
NSString *param1 = [self getParam1]; // getParam1 - some method to get useful data for request's body
NSNumber *param2 = [self getParam2];
NSString *postString = [NSString stringWithFormat:#"param1=%#&param2=%#", param1, param2];
[theRequest setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:theRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
if ([data length] > 0 && error == nil) {
//[delegate receivedData:data]; // - if you want to notify some delegate about data arrival
NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#/fileArrived.ext", rootPath];
//try to access that local file for writing to it...
NSFileHandle *hFile = [NSFileHandle fileHandleForWritingAtPath:filePath];
//did we succeed in opening the existing file?
if (!hFile)
{ //nope->create that file!
[[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil];
//try to open it again...
hFile = [NSFileHandle fileHandleForWritingAtPath:filePath];
}
//did we finally get an accessable file?
if (!hFile)
{ //nope->bomb out!
NSLog(#"could not write to file %#", filePath);
return;
}
//we never know - hence we better catch possible exceptions!
#try
{
//seek to the end of the file
[hFile seekToEndOfFile];
//finally write our data to it
[hFile writeData:data];
}
#catch (NSException * e)
{
NSLog(#"exception when writing to file %#", filePath);
}
[hFile closeFile];
} else if ([data length] == 0 && error == nil) {
// [delegate emptyReply];
} else if (error != nil && error.code == NSURLErrorTimedOut) {
// [delegate timedOut];
} else if (error != nil) {
// [delegate downloadError:error];
}
[queue release];
}];
}
This will append every arrived chunk of your big data into a file as you wanted.
Customize the request POST body for your needs, and this should work. Asynchronously :)

Ok, first check your file path, I usually prefer to refer file path in this way:
you need to get the root of your application in this way:
NSString* rootPath = NSHomeDirectory();
and save the the data in one of the sub folder as specified by Apple file system guide line
NSString* fullPath = [rootPath stringByAppendingPathComponent:#"subFoldeder/file.extension"];
About append new data to old data a very quick solution could be initialize your videoData in this way:
NSMutableData *videoData = [[NSMutableData alloc] initWithContentsOfFile:#"filePath"];
After that you can procede like you are already duing appending data when you receive it, and write the complete file at the end
The correct thing to do, to don't use too much memory should be open a file seek it to the end and append data to the file

Related

iOS - Download Video

I'd like to download a video from a remote URL and save it to a file in an iPhone app.
I know the video link works, since I have used it from AVPlayer, however, I am unable to download it. The response is always (null).
What is wrong with the following code?
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:someURLString]];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:someFilePath append:NO];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Successfully downloaded file to %#", [NSURL fileURLWithPath:someFilePath]);
NSLog(#"THE RESPONSE: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[operation start];
Update
I commented out the operation.outputStream line, and this time I got a response. Does this mean that there is something wrong with the file path?
just create a link to that file, then use NSURLConnection to download.
Create a URL connection to download:
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:strFileUrl]]; //strFileURL is url of your video/image
NSURLConnection *conection = [[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO] autorelease];
[conec start];
[request release];
Get path of file to save data:
strFilePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:strFileName];
Your class must adopt 3 methods of NSURLConnectionDelegate protocol: (please read about Protocol and Delegate)
- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
// create
[[NSFileManager defaultManager] createFileAtPath:strFilePath contents:nil attributes:nil];
file = [[NSFileHandle fileHandleForUpdatingAtPath:strFilePath] retain];// read more about file handle
if (file) {
[file seekToEndOfFile];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)receivedata
{
//write each data received
if( receivedata != nil){
if (file) {
[file seekToEndOfFile];
}
[file writeData:receivedata];
}
}
- (void)connectionDidFinishLoading:(NSURLConnection*)theConnection {
//close file after finish getting data;
[file closeFile];
}
- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
//do something when downloading failed
}
If you want to review you file, use a UIWebview to load it:
NSURL *fileURL = [NSURL fileURLWithPath:strFilePath];
[wvReview loadRequest:[NSURLRequest requestWithURL:fileURL]];
This is what was going wrong.
I was using the url of the video, and as part of the file path.
Why is this wrong? It has backslashes, so I assume iOS was getting confused.
Lesson learned: make sure that the string that you add to a directory to create a file does not have backslashes.
I hope this helps anyone else who makes this silly mistake. :P

NSData writeToFile works on simulator but not on device

In my iphone app I am downloading some number of images from the web. It doesn't matter if it blocks the UI thread, in fact it needs to block UI thread till fully downloaded. Once done, I notify the UI to wake up and display them.
My (simplified) code goes like this:
for (int i=0; i<10; i++)
{
//call saveImageFromURL (params)
}
//Call to Notify UI to wake up and show the images
+(void) saveImageFromURL:(NSString *)fileURL :(NSString *)destPath :(NSString *)fileName
{
NSData * data = [NSData dataWithContentsOfURL:[NSURL URLWithString:fileURL]];
NSFileManager * fileManager = [NSFileManager defaultManager];
BOOL bExists, isDir;
bExists = [fileManager fileExistsAtPath:destPath isDirectory:&isDir];
if (!bExists)
{
NSError *error = nil;
[fileManager createDirectoryAtPath:destPath withIntermediateDirectories:YES attributes:nil error:&error];
if (error)
{
NSLog(#"%#",[error description]);
return;
}
}
NSString *filePath = [destPath stringByAppendingPathComponent:fileName];
[data writeToFile:filePath options:NSAtomicWrite error:nil];
}
When I am done with my for loop, I am pretty sure that all images are stored locally. And it works fine in simulator.
However it does not work well on my device. UI wakes up before images are stored. And almost all images seem empty.
What am I doing wrong?
Check that if your device can download those images, visit the image URLs in Mobile Safari to test. dataWithContentsOfURL: will return nil OR it's not a correct image data, like 404 not found
Log errors of [data writeToFile:filePath] to see the details of saving .
After some research, I used AFHttpClient enqueueBatchOfHTTPRequestOperations to accomplish multiple file downloads.
Here is how it goes:
//Consider I get destFilesArray filled with Dicts already with URLs and local paths
NSMutableArray * opArray = [NSMutableArray array];
AFHTTPClient *httpClient = nil;
for (id item in destFilesArray)
{
NSDictionary * fileDetailDict = (NSDictionary *)item;
NSString * url = [fileDetailDict objectForKey:#"fileURL"];
if (!httpClient)
httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:url]];
NSString * filePath = [photoDetailDict objectForKey:#"filePath"];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:filePath append:NO];
[opArray addObject:operation];
}
[httpClient enqueueBatchOfHTTPRequestOperations:opArray progressBlock:nil completionBlock:^(NSArray *operations)
{
//gets called JUST ONCE when all operations complete with success or failure
for (AFJSONRequestOperation *operation in operations)
{
if (operation.response.statusCode != 200)
{
NSLog(#"operation: %#", operation.request.URL);
}
}
}];

how to display progress bar while downloading file from request response in Asihttp

I am downloading video file from response.
i want to display downloading progress bar of HUD-progress.
but how can i do that.
I am sending verify json to server and server verify that send back the video file bytes. i want to display how much percentage of downloading is done by using HUD-progreasse bar.
If i call [request setDidReceiveDataSelector:#selector(request:didReceiveBytes:)]; than it display how much bytes i got but it doesn't store the bytes into cache file ( it doesn't not store file into phone)
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[[NSURL alloc] initWithString:#"http://testing.io/dev.php/video/verifyReceipt"]];
[request setPostValue:resultAsString forKey:#"verify"];// sending json in post
[request setDidReceiveDataSelector:#selector(request:didReceiveBytes:)];
[request setDidFinishSelector:#selector(requestDone:)];
[request setTimeOutSeconds:120];
[request setDelegate:self];
[request setNumberOfTimesToRetryOnTimeout:2];
[request setDownloadProgressDelegate:self];
request.showAccurateProgress = YES;
[request startSynchronous];
}
-(void)request:(ASIHTTPRequest *)request didReceiveData:(NSData *)data
{
[videoData appendData:data];// appending data with global NSmutabledata
NSLog(#"data is %#",data);
}
- (void)requestDone:(ASIHTTPRequest *)request{
//[MBProgressHUD hideHUDForView:self.view animated:YES];
// SAVED Video PATH
// Get the Document directory
NSString *documentDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
// Add your filename to the directory to create your saved video location
NSString* movLocation = [documentDirectory stringByAppendingPathComponent:[fileName stringByAppendingString:#".mov"]];
if(request.responseStatusCode==200)
{
[videoData writeToFile:movLocation atomically:NO];
NSLog(#"in request done sucsessfully downlaod and store in database %d",request.responseStatusCode);
[DBHelper savePurchaseId:fileName];
[self movieReceived];
}
else{
NSLog(#"in request downlaod and store in database failed %#",request.responseHeaders);
}
}
-(void)requestFailed:(ASIHTTPRequest *)request
{
NSLog(#"%#",request.error);
}
fileUrl = string of URL to download a video.
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[fileUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:destPath append:NO];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
// Give alert that downloading successful.
NSLog(#"Successfully downloaded file to %#", destPath);
/* To call delegate to response the result. */
[self.target parserDidDownloadItem:destPath];
HUD.detailsLabelText = [NSString stringWithFormat:#"%# %i%%",[JGlobals getLocalvalue:#"Downloading"],100];
[HUD hide:TRUE];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
// Give alert that downloading failed
NSLog(#"Error: %#", error);
/* To call delegate to response the result. */
[self.target parserDidFailToDownloadItem:error];
[HUD hide:TRUE];
}];
[operation setDownloadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite)
{
// Progress
float totalProgress = (totalBytesWritten / (totalBytesExpectedToWrite * 1.0f) * 100);
HUD.detailsLabelText = [NSString stringWithFormat:#"Downloading %i%%", MIN((int)(totalProgress), 99)];
}];
[operation start];
I'm thinking of a method which might not be the right one, but it going to save you from writing a lot of code.
First set a normal UIProgressView instance as the download progress delegate like this
[request setDownloadProgressDelegate:uiprogreeViewInstance];
Now ASIHTTPRequest framework will take care of updating that progressview when the downloading will be taking place. ProgressView has a property named progress, which ranges from 0.0 to 1.0.
Now instantiate MBProgressHUD, add it as your subview where you want. Set the progress value of the MBProgressHUD instance to that of the UIProgressView. Hide the uiprogressview instance.

How to upload data from iphone app to mysql data base

I have a EMR app and i want that i may send the data which i have collected like images and voice to server. in data base so how can i do this . Is there any way to send these data to server through post method.
Here is an example of a HTTP Post request
// define your form fields here:
NSString *content = #"field1=42&field2=Hello";
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://www.example.com/form.php"]];
[urlRequest setHTTPMethod:#"POST"];
[urlRequest setHTTPBody:[content dataUsingEncoding:NSISOLatin1StringEncoding]];
// generates an autoreleased NSURLConnection
[NSURLConnection connectionWithRequest:request delegate:self];
Might want to reference http://developer.apple.com/library/ios/#documentation/cocoa/reference/foundation/Classes/NSURLConnection_Class/Reference/Reference.html
This tutorial is also helpful http://www.raywenderlich.com/2965/how-to-write-an-ios-app-that-uses-a-web-service
In that case, you can do follow two ways:
1. if you strictly like to using POST (i like), u can using cocoahttpserver project:
https://github.com/robbiehanson/CocoaHTTPServer
In iphone app, you can do this code to send POST request:
-(NSDictionary *) getJSONAnswerForFunctionVersionTwo:(NSString *)function
withJSONRequest:(NSMutableDictionary *)request;
{
[self updateUIwithMessage:#"server download is started" withObjectID:nil withLatestMessage:NO error:NO];
NSDictionary *finalResultAlloc = [[NSMutableDictionary alloc] init];
#autoreleasepool {
NSError *error = nil;
NSString *jsonStringForReturn = [request JSONStringWithOptions:JKSerializeOptionNone serializeUnsupportedClassesUsingBlock:nil error:&error];
if (error) NSLog(#"CLIENT CONTROLLER: json decoding error:%# in function:%#",[error localizedDescription],function);
NSData *bodyData = [jsonStringForReturn dataUsingEncoding:NSUTF8StringEncoding];
NSData *dataForBody = [[[NSData alloc] initWithData:bodyData] autorelease];
//NSLog(#"CLIENT CONTROLLER: string lenght is:%# bytes",[NSNumber numberWithUnsignedInteger:[dataForBody length]]);
NSString *functionString = [NSString stringWithFormat:#"/%#",function];
NSURL *urlForRequest = [NSURL URLWithString:functionString relativeToURL:mainServer];
NSMutableURLRequest *requestToServer = [NSMutableURLRequest requestWithURL:urlForRequest];
[requestToServer setHTTPMethod:#"POST"];
[requestToServer setHTTPBody:dataForBody];
[requestToServer setTimeoutInterval:600];
[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[urlForRequest host]];
NSData *receivedResult = [NSURLConnection sendSynchronousRequest:requestToServer returningResponse:nil error:&error];
if (error) {
NSLog(#"CLIENT CONTROLLER: getJSON answer error download:%#",[error localizedDescription]);
[self updateUIwithMessage:[error localizedDescription] withObjectID:nil withLatestMessage:YES error:NO];
[finalResultAlloc release];
return nil;
}
NSString *answer = [[NSString alloc] initWithData:receivedResult encoding:NSUTF8StringEncoding];
JSONDecoder *jkitDecoder = [JSONDecoder decoder];
NSDictionary *finalResult = [jkitDecoder objectWithUTF8String:(const unsigned char *)[answer UTF8String] length:[answer length] error:&error];
[finalResultAlloc setValuesForKeysWithDictionary:finalResult];
[answer release];
[self updateUIwithMessage:#"server download is finished" withObjectID:nil withLatestMessage:NO error:NO];
if (error) NSLog(#"CLIENT CONTROLLER: getJSON answer failed to decode answer with error:%#",[error localizedDescription]);
}
NSDictionary *finalResultToReturn = [NSDictionary dictionaryWithDictionary:finalResultAlloc];
[finalResultAlloc release];
return finalResultToReturn;
}
Don't forget to pack attributes with images to base64.
Finally, if u don't like to keep data, which u send in you mac app, u can send to u database using any database C api. I recommend to using core data to save receive data.

Resume download functionality in NSURLConnection

I am downloading some very large data from a server with the NSURLConnection class.
How can I implement a pause facility so that I can resume downloading?
You can't pause, per-se, but you can cancel a connection, and then create a new one to resume where the old left off. However, the server you're connecting to must support the Range header. Set this to "bytes=size_already_downloaded-", and it should pick up right where you cancelled it.
To resume downloading and get the rest of the file you can set the Range value in HTTP request header by doing something like this:
- (void)downloadFromUrl:(NSURL*)url toFilePath:(NSString *)filePath {
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]initWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10];
if (!request) {
NSLog(#"Error creating request");
// Do something
}
[request setHTTPMethod:#"GET"];
// Add header to existing file
NSFileManager *fm = [NSFileManager defaultManager];
if([fm fileExistsAtPath:filePath]) {
NSError *error = nil;
NSDictionary * fileProp = [fm attributesOfItemAtPath:filePath error:&error];
if (error) {
NSLog(#"Error: %#", [error localizedDescription]);
// Do something
} else {
// Set header to resume
long long fileSize = [[fileProp objectForKey:#"NSFileSize"]longLongValue];
NSString *range = #"bytes=";
range = [[range stringByAppendingString:[[NSNumber numberWithLongLong:fileSize] stringValue]] stringByAppendingString:#"-"];
[request setValue:range forHTTPHeaderField:#"Range"];
}
}
NSURLConnection * connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (!connection) {
NSLog(#"Connection failed.");
// Do something
}
}
Also you can use
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response to check if the existing file is fully downloaded by checking the expected size: [response expectedContentLength];. If sizes match you probably want to cancel the connection.