Reputation: 14925
I am trying to download a pdf file from a server to the device. Here is the code that I am using
- (id)initwithURL:(NSString*)remoteFileLocation andFileName:(NSString*)fileName{
//Get path to the documents folder
NSString *resourcePathDoc = [[NSString alloc] initWithString:[[[[NSBundle mainBundle]resourcePath]stringByDeletingLastPathComponent]stringByAppendingString:@"/Documents/"]];
localFilePath = [resourcePathDoc stringByAppendingString:fileName];
BOOL fileExists = [[NSFileManager defaultManager]fileExistsAtPath:localFilePath];
if (fileExists == NO) {
NSURL *url = [NSURL URLWithString:remoteFileLocation];
NSData *data = [[NSData alloc] initWithContentsOfURL: url];
//Write the data to the local file
[data writeToFile:localFilePath atomically:YES];
}
return self;
}
where remoteFileLocation is a NSString and has the value http://topoly.com/optimus/irsocial/Abs/Documents/2009-annual-report.pdf On running the app crashes, just on NSData giving a SIGABRT error. The only useful information it gives is
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSURL length]: unrecognized selector sent to instance 0xc87b600'
How can this be fixed ?
Upvotes: 1
Views: 2927
Reputation: 13600
As your PDF file is too large in size so if you do Synchronous Download, it will take too Long to download, so i insist you to create an Asynchronous Downloader and Use it. I have put code for the same.
Step 1 :Create a file 'FileDownloader.h'
#define FUNCTION_NAME NSLog(@"%s",__FUNCTION__)
#import <Foundation/Foundation.h>
@protocol fileDownloaderDelegate <NSObject>
@optional
- (void)downloadProgres:(NSNumber*)percent forObject:(id)object;
@required
- (void)downloadingStarted;
- (void)downloadingFinishedFor:(NSURL *)url andData:(NSData *)data;
- (void)downloadingFailed:(NSURL *)url;
@end
@interface FileDownloader : NSObject
{
@private
NSMutableURLRequest *_request;
NSMutableData *downloadedData;
NSURL *fileUrl;
id <fileDownloaderDelegate> delegate;
double totalFileSize;
}
@property (nonatomic, strong) NSMutableURLRequest *_request;
@property (nonatomic, strong) NSMutableData *downloadedData;
@property (nonatomic, strong) NSURL *fileUrl;
@property (nonatomic, strong) id <fileDownloaderDelegate> delegate;
- (void)downloadFromURL:(NSString *)urlString;
@end
Step 2 : Create a .m file with FileDownloader.m
#import "FileDownloader.h"
@implementation FileDownloader
@synthesize _request, downloadedData, fileUrl;
@synthesize delegate;
- (void)downloadFromURL:(NSString *)urlString
{
[self setFileUrl:[NSURL URLWithString:[urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
self._request = [NSMutableURLRequest requestWithURL:self.fileUrl cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60.0f];
NSURLConnection *cn = [NSURLConnection connectionWithRequest:self._request delegate:self];
[cn start];
}
#pragma mark - NSURLConnection Delegate
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
if([delegate respondsToSelector:@selector(downloadingStarted)])
{
[delegate performSelector:@selector(downloadingStarted)];
}
totalFileSize = [response expectedContentLength];
downloadedData = [NSMutableData dataWithCapacity:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[downloadedData appendData:data];
if([delegate respondsToSelector:@selector(downloadProgres:forObject:)])
{
[delegate performSelector:@selector(downloadProgres:forObject:) withObject:[NSNumber numberWithFloat:([downloadedData length]/totalFileSize)] withObject:self];
}
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
if([delegate respondsToSelector:@selector(downloadingFailed:)])
{
[delegate performSelector:@selector(downloadingFailed:) withObject:self.fileUrl];
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if([delegate respondsToSelector:@selector(downloadingFinishedFor:andData:)])
{
[delegate performSelector:@selector(downloadingFinishedFor:andData:) withObject:self.fileUrl withObject:self.downloadedData];
}
}
@end
Step 3 : Import file #import "FileDownloader.h"
and fileDownloaderDelegate
in your viewController
Step 4: Define following Delegate methods in .m file of your viewCOntroller
- (void)downloadingStarted;
- (void)downloadingFinishedFor:(NSURL *)url andData:(NSData *)data;
- (void)downloadingFailed:(NSURL *)url;
Step 5 : Create Object of FileDownloader and set URL to Download thats it.
FileDownloader *objDownloader = [[FileDownloader alloc] init];
[objDownloader setDelegate:self];
[objDownloader downloadFromURL:@"Your PDF Path URL here];
Step 6 : Save your file where you want in
- (void)downloadingFinishedFor:(NSURL *)url andData:(NSData *)data;
method.
Upvotes: 3
Reputation: 1
GCD can be used for massive files. You can download the file synchronous on a second thread and post back to the main thread if you like. You can also use Operation queues.
You can indeed also use the delegate method from NSURLConnection allowing you to handle the callbacks on the main thread. It is however obsolete to define your own delegate since you can just implement the delegate from NSURLConnection itself.
Upvotes: 0
Reputation: 318774
It appears that your remoteFileLocation
parameter value is really an NSURL
object and not an NSString
. Double check how you get/create remoteFileLocation
and verify it really is an NSString
.
There are also several other issues with this code. The proper way to create a path to the Documents directory is as follows:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = paths[0];
NSString *localFilePath = [resourcePathDoc stringByAppendingPathComponent:fileName];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:localFilePath];
if (!fileExists) {
NSURL *url = [NSURL URLWithString:remoteFileLocation];
NSData *data = [[NSData alloc] initWithContentsOfURL:url];
//Write the data to the local file
[data writeToFile:localFilePath atomically:YES];
}
Upvotes: 1