I'm using AFNetworking 3.0 to download a file, and it seems to be doing the downloading part fine, but I can't find the file afterwards.
I'm using the code below. In the download task, if I set breakpoints in the destination block, it seems as though the target path and download destination path are correct, and in fact at the point the targetPath points to a tmp file in the tmp folder which exists and contains the correctly downloaded data. However if I then hit a breakpoint in the completion handler block, the tmp file has disappeared and there is no file where my download destination path pointed.
Am I missing a step? Do I have to move this file myself, or is that something AFNetworking should be taking care of?
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
self.theRequest = [[AFHTTPRequestSerializer serializer]
requestWithMethod:self.RequestMethod //#"POST"
URLString:requestURL.absoluteString //url to my API
parameters:self.Parameters //params being sent to API
error:nil];
//headers in this example:
//"Content-Type" = "application/json"
//"X-Requested-With" = XMLHttpRequest
//token = "<API TOKEN>";
for (id key in headers) {
[self.theRequest setValue:headers[key] forHTTPHeaderField:key];
}
self.theRequest.timeoutInterval = 60 * 100;
NSURLSessionDownloadTask * downloadTask =
[manager downloadTaskWithRequest:self.theRequest
progress:^(NSProgress * _Nonnull downloadProgress) {
if(self.DownloadProgressHandler)
self.DownloadProgressHandler(downloadProgress.fractionCompleted);
}
destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
NSURL *url = [NSURL URLWithString:self.downloadDestinationPath];
NSLog(#"%#",[targetPath absoluteString]);
NSLog(#"%#",[url absoluteString]);
return url;
}
completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
[self RequestCompleteWithResponse:response responseObject:[[filePath absoluteString] dataUsingEncoding:NSUTF8StringEncoding] error:error];
}];
self.theTask = downloadTask;
[self.theTask resume];
Output from the NSLogs above:
2016-03-04 13:43:44.412 Marq[27505:154492] __23-[MarqAPI BuildRequest]_block_invoke247 line 648 $ file:///Users/aerion/Library/Developer/CoreSimulator/Devices/11594D0A-882C-4E46-9BAC-CEF7148014C7/data/Containers/Data/Application/E8C7D3EE-BB69-461F-BA2F-49EB7C2AE1CF/tmp/CFNetworkDownload_7VGArX.tmp
2016-03-04 13:43:44.425 Marq[27505:154492] __23-[MarqAPI BuildRequest]_block_invoke247 line 649 $ /Users/aerion/Library/Developer/CoreSimulator/Devices/11594D0A-882C-4E46-9BAC-CEF7148014C7/data/Containers/Data/Application/E8C7D3EE-BB69-461F-BA2F-49EB7C2AE1CF/Documents/9dfd86c2-458e-4725-a184-5fcd87f94dbd.inspect
Argh, that was silly of me. The answer is staring me in the face in those logs.
The file path for the temp file begins with file://, whereas my download destination path does not. the answer is to change
NSURL *url = [NSURL URLWithString:self.downloadDestinationPath];
to
NSURL *url = [NSURL fileURLWithPath:self.downloadDestinationPath];
This will give me a valid file path to send the downloaded file to
Related
I am downloading a file from the web. File size is big some times may reach up to 100MBs some times, I want to continue downloading while to app goes to background or when the device is locked. For this i am using AFNetworking 3.0
[NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:myIdentifier];
It works fine as long as i am on WiFi. When i turn off WiFi and turn on my cellular network which is 4G, it stops responding and i get no data as a result of my download request. If i use
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
every thing was fine except my download will not continue when the app goes to background.
I have also checked allowsCellularAccess on NSURLSessionConfiguration and NSURLRequest and object which is YES, but my download does not work when on cellular network.
Here is my full code
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:myIdentifier];
configuration.discretionary = YES;
configuration.sessionSendsLaunchEvents = YES;
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *URL = [NSURL URLWithString:downloadUrl];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSLog(#"Allow Cellular Network : %d",request.allowsCellularAccess);
NSLog(#"Allow Cellular Network for session: %d",configuration.allowsCellularAccess);
NSLog(#"Resource timneout interval: %f",configuration.timeoutIntervalForResource);
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) {
dispatch_async(dispatch_get_main_queue(), ^{
[self callProgressBlocksForUrl:lesson.archiveUrl withProgress:downloadProgress.fractionCompleted];
});
} destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
NSLog(#"Getting Path for File saving");
return [NSURL fileURLWithPath:fullPath];
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
NSHTTPURLResponse * myresponse = (NSHTTPURLResponse *)response;
NSLog(#"Video downloaded, headers: %#", [myresponse.allHeaderFields description]);
}];
You should not be setting the discretionary flag. That tells the OS to wait to download the data until a convenient time (which, IIRC, basically means when the device is A. asleep, B. on power, and C. connected to Wi-Fi).
I guess discretionary flag might create the problem.
As said by apple in documentation that discretionary flag allow the download when device have convenient time and convenient resources.
So, I have been scouring these forums and other sites for a few days now and can not find a suitable replacement for the old way to download a series of files in AFNetworking 1.0 and 2.0.
I am trying to use NSURLSessionDownloadTask to download these files but I know they aren't like threads so you can't treat them as such.
Previously I could do this to download multiple files in order and get progress updates on the total operation (not just on each file, but on the entire queue).
Here is the basic structure I was using...
for(Photo *photo in array){
//form the path where you want to save your downloaded image to
NSString *constPath = [photo imageFullPath];
//url of your photo
NSURL *url = [NSURL URLWithString:photo.serverPath];
AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:[NSURLRequest requestWithURL:url]];
op.responseSerializer = [AFImageResponseSerializer serializer];
op.outputStream = [NSOutputStream outputStreamToFileAtPath:constPath append:NO];
op.queuePriority = NSOperationQueuePriorityLow;
[op setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead){
}];
op.completionBlock = ^{
//do whatever you want with the downloaded photo, it is stored in the path you create in constPath
};
[requestArray addObject:op];
}
NSArray *batches = [AFURLConnectionOperation batchOfRequestOperations:requestArray progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations) {
} completionBlock:^(NSArray *operations) {
//after all operations are completed this block is called
if (successBlock)
successBlock();
}];
[[NSOperationQueue mainQueue] addOperations:batches waitUntilFinished:NO];
(this code was taken from another post but it is exactly what i was using on a previous app)
And now, trying to use NSURLSessionDownloadTask, I can not find a way to adapt the following code to run in a series (1 file at a time, in order, with progress and file data along the way).
NSURLSessionDownloadTask *newTask = [_downloadReqOps downloadTaskWithRequest:myRequest progress:^(NSProgress * _Nonnull downloadProgress) {
//Progress
} destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
NSString *filePath = [NSString stringWithFormat:#"%#/%#", [CompanyInfo storeFileInFolder:kTempFiles], response.URL.absoluteString.lastPathComponent];
//NSLog(#"File Path: %#", filePath);//response.URL.absoluteString.lastPathComponent);
return [NSURL fileURLWithPath:filePath];
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
}];
[newTask resume];
I think this used to be called a "batch" request in AFNetworking 1.0 but I know that this is not used any more. I am trying to adapt the new NSURLSessions to download these files.
Any help would be appreciated!
I am trying to download a file using AFNetworking (2.5.4). The download completes, the completion handler is called, with error set to nil, everything seeming fine, but the destination file does not exist:
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
NSString *fullPath = [valid path from my apps local manager]
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:req progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
return [NSURL URLWithString:fullPath];
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
NSLog(#"Saved file to %#.", filePath);
*** [[NSFileManager defaultManager] fileExistsAtPath:filePath.absoluteString] returns NO here ***
}];
[cell.progressView setProgressWithDownloadProgressOfTask:downloadTask animated:YES];
[downloadTask resume];
The file path is a regular path that my app has write access to:
/var/mobile/Containers/Data/Application/APP-GUID-REDACTED/Documents/FILE-NAME-REDACTED.docx
I was using a different method before AFNetworking, and it could write to the exact same path just fine. HTTP response headers show everything perfectly (status 200, correct content length etc.) and if I curl the download URL it downloads the file with no issues. There's no problem with the file.
Why is my destination file not written in completion handler despite no errors?
UPDATE: I've also tried AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; but it changes nothing. I've also tried creating an NSProgress pointer and sending that for the progress argument, but no avail.
Use [NSURL fileURLWithPath:] (not URLWithString).
return [NSURL fileURLWithPath:fullPath];
The problem here is wrong file path or invalid file path. I had same problem here.
Create path like given below :
NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
NSURL *filePathURL = [documentsDirectoryURL URLByAppendingPathComponent:[NSString stringWithFormat:#"your file name here",i]];
Now use above path :
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:req progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
return filePathURL;
}
I'm trying to migrate our ASIHTTPRequest code to AFNetworking. I'm okay with POST requests but I'm having issues with download requests. I can't seem to set the temporary file path of the content to be downloaded. In ASIHTTPRequest I can have a code like this:
// Create file path
NSString *filePath = [cachePath stringByAppendingPathComponent:package.fileName];
NSString *tempFile = [tempPath stringByAppendingPathComponent:package.fileName];
[downloadRequest setTemporaryFileDownloadPath:tempFile];
[downloadRequest setDownloadDestinationPath:filePath];
How can I do this using AFNetworking?
AFURLSessionManager* urlSessionManager = [AFURLSessionManager.alloc initWithSessionConfiguration:NSURLSessionConfiguration.defaultSessionConfiguration];
NSProgress* progress = nil;
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"http://your.remote.file"]];
NSURLSessionDownloadTask* downloadTask = [urlSessionManager downloadTaskWithRequest:request progress:&progress destination:^NSURL* (NSURL* targetPath, NSURLResponse* response) {
NSURL* targetFileUrl = [NSURL fileURLWithPath:#"/your/local/path" isDirectory:NO];
return targetFileUrl;
} completionHandler:^(NSURLResponse* response, NSURL* filePath, NSError* error) {
if (error)
{
// Some error occurred or download programmatically cancelled
}
else
{
// Download completed
}
}];
[downloadTask resume];
The temporary files are managed by AFNetworking, usually they're hidden raw files inside your document dir.
I'm running into a problem trying to download an image on an Amazon S3 server.
I get the following error:
Error Domain=AFNetworkingErrorDomain Code=-1016 "Request failed: unacceptable content-type: binary/octet-stream"
Anyone has an idea?
This error is generated by
- (BOOL)validateResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
error:(NSError * __autoreleasing *)error
method of AFHTTPResponseSerializer in case of unexpectable MIME type of response.
You can fix it by adding required MIME type to response serializer
// In this sample self is inherited from AFHTTPSessionManager
self.responseSerializer = [AFImageResponseSerializer serializer];
NSSet *set = self.responseSerializer.acceptableContentTypes;
self.responseSerializer.acceptableContentTypes = [set setByAddingObject:#"binary/octet-stream"];
Or you can modify AFImageResponseSerializer :
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
self.acceptableContentTypes = [[NSSet alloc] initWithObjects:#"image/tiff", #"image/jpeg", #"image/gif", #"image/png", #"image/ico", #"image/x-icon", #"image/bmp", #"image/x-bmp", #"image/x-xbitmap", #"image/x-win-bitmap", #"binary/octet-stream", nil];
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
self.imageScale = [[UIScreen mainScreen] scale];
self.automaticallyInflatesResponseImage = YES;
#endif
return self;
}
But root of the problem is probably that you save your images to Amazon with wrong MIME type or without type at all. In my code I save images to Amazon with following code
S3PutObjectRequest *putObjectRequest = [ [ S3PutObjectRequest alloc ] initWithKey:keyImage inBucket:self.s3BucketName ];
putObjectRequest.contentType = #"image/jpeg";
putObjectRequest.data = UIImageJPEGRepresentation( [ image fixOrientation ], 0.5f );
putObjectRequest.cannedACL = [ S3CannedACL publicRead ];
Updated for Boto 3
You can check what MIME type it is through your S3 web interface. First select the file you are having problems with, then select the Properties view. In this view open the Metadata section.
If your Content-Type is binary/octet-stream it is unset. Setting it in Boto 3, is different than the above answer for Boto 2. Here is how I do it:
filename = "/home/me/image42.jpeg" #the file I want to upload
bucketname = "myNewBucket" #name of my S3 bucket
key = "myImages/image42.jpeg" #desired name in S3 bucket
s3.Object(bucketname, key).put(Body=open(filename, 'rb'), ACL='public-read',ContentType='image/jpeg')
AFNetworking 3.1.0
You should just modify the acceptableContentTypes property on an existing image response serializer. You can use AFHTTPSessionManager do it.
NSURL *url = [NSURL URLWithString:#"http://..."];
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
AFHTTPResponseSerializer *serializer = [AFHTTPResponseSerializer serializer];
serializer.acceptableContentTypes = [NSSet setWithObject:#"binary/octet-stream"];
manager.responseSerializer = serializer;
Then you can use manager to get the content of URL:
[manager GET:[url absoluteString] parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
// ...
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
// ...
}];
or use setImageWithURLRequest:placeholderImage:success:^failure:^ for get image:
UIImageView *imageView = [UIImageView new];
[[UIImageView sharedImageDownloader] setSessionManager:manager];
NSURLRequest *urlRequest = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://..."]];
[imageView setImageWithURLRequest:urlRequest placeholderImage:[UIImage new] success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull image) {
// but image can be NSData instead of UIImage
} failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) {
// ...
}];
But the response can be NSData instead of UIImage.
I hope that it helps you.