I'm trying to set up a NSInputStream, but my input stream is comes out as nil when I step into the code. The url comes from a Dropbox account.
Getting the file through NSData after I have the url through Dropbox Chooser crashes my iPhone 4 (although not when it is running through XCode). The files are just too big, so I wanted to try NSInputStream.
I saw from I cannot initialize a NSInputStream that the url is supposed to be local. Any idea how to stream a file from Dropbox?
Thanks.
- (void)setUpStreamForFile {
// iStream is NSInputStream instance variable already declared
iStream = [NSInputStream inputStreamWithURL:url];
[iStream setDelegate:self];
[iStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[iStream open];
}
Hey don't hesitate to use AFNetworking it is a good framework to manipulate your connections and download content. This is an example to download a file from an URL:
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *URL = [NSURL URLWithString:#"http://example.com/download.zip"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
return [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
NSLog(#"File downloaded to: %#", filePath);
}];
[downloadTask resume];
For more information you can check the official information HERE
so thanks to rmaddy's suggestion, I looked up NSURLConnection but decided to use the features of NSURLSession instead.
I used the NSURLSessionDownloadTask like this. Familiarity with the Dropbox chooser should help.
-(IBAction)didPressChooser:(id)sender {
{
[[DBChooser defaultChooser] openChooserForLinkType:DBChooserLinkTypeDirect
fromViewController:self completion:^(NSArray *results)
{
if ([results count]) {
DBChooserResult *_result = results[0];
NSString *extension = [_result.name pathExtension];
if ([extension isEqualToString:#"m4a"]) {
url = _result.link; //url has already been declared elsewhere
DBFileName = _result.name; //DPFileName has also been declared. It's a string
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate:self delegateQueue: [NSOperationQueue mainQueue]];
NSURLSessionDownloadTask *getFile = [session downloadTaskWithURL:url];
[getFile resume];
}
} else {
// User canceled the action
}
}];
}
}
Once you have that, you put in another method that works as the completion handler.
-(void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); //I put the file in a temporary folder here so it doesn't take up too much room.
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#/%#", documentsDirectory, DBFileName];
NSData *data = [NSData dataWithContentsOfURL:location];
[data writeToFile:filePath atomically:YES];
url = [NSURL fileURLWithPath:filePath]; //Yep, I needed to re-assign url for use elsewhere.
//do other stuff with your local file now that you have its url!
}
A bonus is that you get to keep track of the download progress with this awesome feature:
-(void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
NSLog(#"%f / %f", (double)totalBytesWritten,
(double)totalBytesExpectedToWrite);
}
Anyway, hope someone finds this useful. Works much faster on my iPhone 4 than NSURLSessionDataTask which works in a similar manner.
Related
I am trying to get the image from this website, but I am having a hard time. All I need is the URL of the image, when I print the URL I get noting back. This is the URL and I am trying to get the main image seen in the middle, http://theoldrussuanbum.vsco.co/media/555722fde555153e3e8b4591
I have been trying the following code with no luck.
NSURL *URL = [NSURL URLWithString:_urlTextField.text];
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithURL:URL completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error) {
NSString *contentType = nil;
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSDictionary *headers = [(NSHTTPURLResponse *)response allHeaderFields];
contentType = headers[#"Content-Type"];
}
HTMLDocument *document = [HTMLDocument documentWithData:data
contentTypeHeader:contentType];
HTMLElement *element = [document firstNodeMatchingSelector:#"img"];
NSString *urlString = element.attributes[#"src"];
NSLog(#"URL: %#", urlString);
}] resume];
Can anyone help?
You can use regular expressions to first
find the start with the regular expression: #"twitter:image\"\\s+content=\""
and then
extract the URL with the regular expression: #"[^>]+"
You can find information of regular expression syntax at the ICU User Guide: Regular Expressions.
Example code:
NSString *urlString = #"http://theoldrussuanbum.vsco.co/media/555722fde555153e3e8b4591";
NSURL *URL = [NSURL URLWithString:urlString];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *sessionTask;
sessionTask = [session dataTaskWithURL:URL completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error) {
NSString *html = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSRange preambleRange = [html rangeOfString:#"twitter:image\"\\s+content=\"" options:NSRegularExpressionSearch];
if (preambleRange.location != NSNotFound) {
NSString *htmlOffset = [html substringFromIndex:preambleRange.location + preambleRange.length];
NSRange imgUrlRange = [htmlOffset rangeOfString:#"[^>]+" options:NSRegularExpressionSearch];
if (imgUrlRange.location != NSNotFound) {
NSString *imgURLString = [htmlOffset substringWithRange:imgUrlRange];
NSLog(#"URL: %#", imgURLString);
}
}
}];
[sessionTask resume];
Output:
URL: http://image.vsco.co/1/548c6a64020e11517164/555722fde555153e3e8b4591/600x800/vsco_051615.jpg"
Of course production code must handle all errors which this example code does not.
I think first thing you need to do is provide actual url of image resource.
For example; http://image.vsco.co/1/548c6a64020e11517164/548c7b532b5615b6658b4567/vsco_121314.jpg
This is what I get from page source of that web page
And then just refer this question;
iOS download and save image inside app
Idea
I'm building files download manager using AFNetworking and I'm using AFURLSessionManager class. the app is suppose to download mp3 files from the server.
I was concerned about memory consuming, so I'm trying to limit the number of simultaneous downloads to 1.
I know that there is a NSOperationQueue property in AFURLSessionManager called operationQueue and it's limited to 1 operation at a time by default.so I'm adding my NSURLSessionDownloadTask to operationQueue.
the problem
the code isn't working. files is being downloaded simultaneously instead of one after another.
the code
// 1. build sessionManager and prepare some vars
// note: by testing i found that it's better to init NSURLSessionConfiguration with backgroundSessionConfigurationWithIdentifier for memory issues
NSURLSessionConfiguration *conf = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:#"special_Identifier"];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:conf];
NSURL *urlDocs = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:NO
error:nil];
__block NSProgress *progress = Nil;
// 2. open sessionManager operation Queue and add this new download
[manager.operationQueue addOperationWithBlock:^{
// 2.1 init new download request
NSURLRequest *request = [[NSURLRequest alloc]initWithURL:[NSURL URLWithString:fileLink]];
// 2.2 creat a NSURLSessionDownloadTask
NSURLSessionDownloadTask *downloadTask = [self.downloadManager downloadTaskWithRequest:request progress:&progress
destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
return [urlDocs URLByAppendingPathComponent:fileName];
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
if (!error) {
NSLog(#"done: %#", filePath);
}else{
NSLog(#"error %#",error);
}
}];
// 2.3 start downloading
[downloadTask resume];
// 2.4 track downloading progress using KVO
[progress addObserver:self
forKeyPath:NSStringFromSelector(#selector(fractionCompleted))
options:NSKeyValueObservingOptionNew
context:(__bridge void *)(fileLink)];
}];
In AFNetworking 2 (and AFNetworking 3), you can init your AFHTTPSessionManager with an NSURLSessionConfiguration (use AFHTTPSessionManager initWithBaseURL:sessionConfiguration:). There you can specify the number of connections per host (HTTPMaximumConnectionsPerHost).
Sample:
NSURL *url = [NSURL URLWithString:#"myurl.net"];
NSURLSessionConfiguration *configuration = NSURLSessionConfiguration.defaultSessionConfiguration;
configuration.HTTPMaximumConnectionsPerHost = 1;
AFHTTPSessionManager *sessionManager = [[AFHTTPSessionManager alloc] initWithBaseURL:url sessionConfiguration:sessionConfiguration];
Documentation:
AFHTTPSessionManager: http://cocoadocs.org/docsets/AFNetworking/3.0.4/Classes/AFHTTPSessionManager.html#//api/name/initWithBaseURL:sessionConfiguration:
NSURLSessionConfiguration: https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSURLSessionConfiguration_class/#//apple_ref/occ/instp/NSURLSessionConfiguration/HTTPMaximumConnectionsPerHost
I want to do upload the data that downloaded from the server in the share extension, but the upload task is delayed, it will proceed in the next time I push post button in my share extension. I found this message in Document describes "discretionary", does it mean that the upload task is started when the APP is in the background status, so the system might delay do upload.
For transfers started while your app is in the background, the system always starts transfers at its discretion—in other words, the system assumes this property is YES and ignores any value you specified.
Here are the codes:
- (void)didSelectPost {
NSURLSession *session = [self configureSession];
NSString *downLoadStr = [NSString stringWithFormat:#"https://apis.live.net/v5.0/%#/content?access_token=%#", [shareProfile objectForKey:#“PATH”], accesToken];
mDownloadTask = [session downloadTaskWithURL:downUrl];
[mDownloadTask resume];
[self.extensionContext completeRequestReturningItems:[self.extensionContext inputItems] completionHandler:nil];
}
- (NSURLSession *) configureSession {
static NSURLSession *session = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSURLSessionConfiguration* config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:#"com.test.background_shareext"];
// To access the shared container you set up, use the sharedContainerIdentifier property on your configuration object.
config.sharedContainerIdentifier = #"group.test.background”;
session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
});
return session;
}
- (void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{
if (location != nil) {
NSString *str = [NSString stringWithContentsOfFile:[location path] encoding:NSUTF8StringEncoding error:nil];
char *utfString = [[#"" stringByAppendingFormat:#"%#\n%#\n", str, #"test"] UTF8String];
NSData *uploadData = [NSData dataWithBytes:utfString length:strlen(utfString)];
NSString *uploadUrlStr = [NSString stringWithFormat:#"https://apis.live.net/v5.0/%#/files/%#?suppress_response_codes=true&overwrite=true&access_token=%#", path, file, accesToken];
NSURL *uploadUrl = [NSURL URLWithString:uploadUrlStr];
NSMutableURLRequest *uploadReq = [NSMutableURLRequest requestWithURL:uploadUrl cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:HTTP_REQUEST_TIMEOUT_INTERVAL];
[uploadReq setHTTPMethod:#"PUT"];
[uploadReq setHTTPBody:uploadData];
NSURLSession *session = [self configureSession];
NSURLSessionUploadTask *uploadTask = [session uploadTaskWithStreamedRequest:uploadReq];
[uploadTask resume];
}else{
NSLog(#"download task location is nil");
}
}
Update:
After I share some times, the download task might stop, and runs until I share later.
I was wondering if someone could help me out. I'm trying to use NSURLSessionDownloadTask to display a picture in my UIImageView if I put the image URL into my textfield.
-(IBAction)go:(id)sender {
NSString* str=_urlTxt.text;
NSURL* URL = [NSURL URLWithString:str];
NSURLRequest* req = [NSURLRequest requestWithURL:url];
NSURLSession* session = [NSURLSession sharedSession];
NSURLSessionDownloadTask* downloadTask = [session downloadTaskWithRequest:request];
}
I am not sure where to go after this.
Two options:
Use [NSURLSession sharedSession], with rendition of downloadTaskWithRequest with the completionHandler. For example:
typeof(self) __weak weakSelf = self; // don't have the download retain this view controller
NSURLSessionTask* downloadTask = [session downloadTaskWithRequest:request completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
// if error, handle it and quit
if (error) {
NSLog(#"downloadTaskWithRequest failed: %#", error);
return;
}
// if ok, move file
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *documentsURL = [fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask][0];
NSURL *fileURL = [documentsURL URLByAppendingPathComponent:filename];
NSError *moveError;
if (![fileManager moveItemAtURL:location toURL:fileURL error:&moveError]) {
NSLog(#"moveItemAtURL failed: %#", moveError);
return;
}
// create image and show it im image view (on main queue)
UIImage *image = [UIImage imageWithContentsOfFile:[fileURL path]];
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
weakSelf.imageView.image = image;
});
}
}];
[downloadTask resume];
Clearly, do whatever you want with the downloaded file (put it somewhere else if you want), but this might be the basic pattern
Create NSURLSession using session:delegate:queue: and specify your delegate, in which you'll conform to NSURLSessionDownloadDelegate and handle the completion of the download there.
The former is easier, but the latter is richer (e.g. useful if you need special delegate methods, such as authentication, detecting redirects, etc., or if you want to use background session).
By the way, don't forget to [downloadTask resume], or else the download will not start.
I'm unsure of what the best approach is for pausing a download. I have seen this question asked on stack over flow before, but doesn't seem to achieve the results I am looking for. For example: I understand inside of the AppDelegate, the - (void)applicationWillResignActive:(UIApplication *)application is called if a phone call comes on the phone for example. It is crucial for my app to successfully download the database to function appropriately. Here's the code I use to download the database:
NSString *urlDb = [NSString stringWithFormat:#"someURl'];
//---Create URL from where DB has to be download-----
NSURL *url = [NSURL URLWithString:urlDb];
NSError *error = nil;
NSData *data = [NSData dataWithContentsOfURL:url options:0 error:&error];
Pausing a download may not be critical for example, AT&T, but folks with Verizon cannot talk and download at the same time. Any help would be greatly appreciated. Thanks!
If you want pause/resume functionality, then you must use either old NSURLConnection, and implement all required delegate methods, or you can use new NSURLSession API.
Here is a basic implementation with NSURLConnection:
#property NSFileHandle *fileHandle;
#property NSURLConnection *connection;
// start download
- (IBAction)downloadButtonPressed
{
NSURL *url = [NSURL URLWithString:#"myUrl"];
NSURLRequest *dataRequest = [NSURLRequest requestWithURL:url];
self.connection = [NSURLConnection connectionWithRequest:dataRequest delegate:self];
[connection start];
}
Implement delegate methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
self.fileHandle = [NSFileHandle fileHandleForWritingAtPath:#“pathToYourFile”];
[self.fileHandle seekToEndOfFile];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.fileHandle writeData:data];
}
If you want to pause - just cancel connection like this:
[self.connection cancel];
Then you can resume like this:
- (void)resumeDownload
{
NSURL *url = [NSURL URLWithString:#"myUrl"];
NSMutableURLRequest *dataRequest = [NSMutableURLRequest requestWithURL:url];
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:pathToYourFile error:&attributesError];
NSNumber *fileSizeNumber = [fileAttributes objectForKey:NSFileSize];
NSString *bytesRangeHeader = [NSString stringWithFormat:#"bytes=%lld-", [fileSizeNumber longLongValue];
[_request setValue:bytesRangeHeader forHTTPHeaderField:#"Range"];
self.connection = [NSURLConnection connectionWithRequest:dataRequest delegate:self];
[connection start];
}