How to download a file and save it to the documents directory with AFNetworking?

35,512

Solution 1

NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"..."]];
AFHTTPRequestOperation *operation = [[[AFHTTPRequestOperation alloc] initWithRequest:request] autorelease];

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"filename"];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:path append:NO];

[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSLog(@"Successfully downloaded file to %@", path);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];

[operation start];

Solution 2

I'm gonna bounce off @mattt's answer and post a version for AFNetworking 2.0 using AFHTTPRequestOperationManager.

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"filename"];

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
AFHTTPRequestOperation *op = [manager GET:@"http://example.com/file/to/download" 
                               parameters:nil
    success:^(AFHTTPRequestOperation *operation, id responseObject) {
         NSLog(@"successful download to %@", path);
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
         NSLog(@"Error: %@", error);
    }];
op.outputStream = [NSOutputStream outputStreamToFileAtPath:path append:NO];

Solution 3

I'm talking about AFNetworking 2.0

[AFHTTPRequestOperationManager manager] creates manager object with default AFJSONResponseSerializer, and it performs content types restriction. Take a look at this

- (BOOL)validateResponse:(NSHTTPURLResponse *)response
                    data:(NSData *)data
                   error:(NSError * __autoreleasing *)error

So we need to create a none response serializer and use AFHTTPRequestOperationManager as normal.

Here is the AFNoneResponseSerializer

@interface AFNoneResponseSerializer : AFHTTPResponseSerializer

+ (instancetype)serializer;

@end

@implementation AFNoneResponseSerializer

#pragma mark - Initialization
+ (instancetype)serializer
{
    return [[self alloc] init];
}

- (instancetype)init
{
    self = [super init];

    return self;
}

#pragma mark - AFURLResponseSerializer
- (id)responseObjectForResponse:(NSURLResponse *)response
                           data:(NSData *)data
                          error:(NSError *__autoreleasing *)error

{
    return data;
}

@end

Usage

self.manager = [AFHTTPRequestOperationManager manager];
self.manager.responseSerializer = [AFNoneResponseSerializer serializer];

[self.manager GET:@"https://sites.google.com/site/iphonesdktutorials/xml/Books.xml"
           parameters:parameters
              success:^(AFHTTPRequestOperation *operation, id responseObject)
    {
        if (success) {
            success(responseObject);
        }
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        if (failure) {
            failure(error);
        }
    }];

so that we can get the whole file without any serialization

Solution 4

From AFNetworking docs. Save to loaded file to your documents. AFNetworking 3.0

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];

Solution 5

Documentation page has example with section 'Creating a Download Task':

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];

NB! Code work with iOS 7+ (tested with AFNetworking 2.5.1)

Share:
35,512
iamsmug
Author by

iamsmug

Updated on January 17, 2020

Comments

  • iamsmug
    iamsmug over 4 years

    I am using the AFNetworking library. I can't figure out how to download a file and save it to the documents directory.

  • Climbatize
    Climbatize over 11 years
    You can even add a progress block: //Setup Upload block to return progress of file upload [operation setDownloadProgressBlock:^(NSInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToRead) { float progress = totalBytesWritten / (float)totalBytesExpectedToRead; NSLog(@"Download Percentage: %f %%", progress*100); }];
  • leolobato
    leolobato over 10 years
    Be careful that the above code will download any response from the server to the output stream, so if the server responds with status code 404, the 404 page will be saved to the path specified. You must check on the success block for operation.response.statusCode.
  • Thomas
    Thomas about 10 years
    I would definitely recommend doing it this way with the newer AFNetworking library
  • onmyway133
    onmyway133 about 10 years
    @swilliams [AFHTTPRequestOperationManager manager] will create new manager object using default AFJSONResponseSerializer, do you want that? I think it 's better to create AFNoneResponseSerialize to let the file untouched
  • Bob Spryn
    Bob Spryn almost 10 years
    Instead of manually checking the status code, just use operation.hasAcceptableStatusCode
  • shshnk
    shshnk over 9 years
    how can I get the download progress parameter in this ?
  • Denis Kutlubaev
    Denis Kutlubaev over 9 years
    shshnk, look at my answer below
  • Gank
    Gank over 9 years
    NSURLRequest How to post or get one url?
  • lostintranslation
    lostintranslation almost 9 years
    Is there a way to have the file cleaned up automatically if the download does not complete?
  • Khant Thu Linn
    Khant Thu Linn over 8 years
    how to download only in success block ? @leolobato can you advise please?
  • Ryan Brodie
    Ryan Brodie over 8 years
  • ramesh bhuja
    ramesh bhuja about 8 years
    when i download video and click on back witho download completion. and again i click on video that time i get this error =>MediaPlayerErrorDomain Code=-11800 ... so plz help me
  • ramesh bhuja
    ramesh bhuja about 8 years
    when i download video and click on back witho download completion. and again i click on video that time i get this error =>MediaPlayerErrorDomain Code=-11800 ... so plz help me
  • ramesh bhuja
    ramesh bhuja about 8 years
    when i download video and click on back witho download completion. and again i click on video that time i get this error =>MediaPlayerErrorDomain Code=-11800 ... so plz help me
  • ramesh bhuja
    ramesh bhuja about 8 years
    when i download video and click on back witho download completion. and again i click on video that time i get this error =>MediaPlayerErrorDomain Code=-11800 ... so plz help me
  • ios developer
    ios developer almost 8 years
    how to do this with POST with parameters?
  • DURGESH
    DURGESH over 7 years
    how to call Get Type method with some header data and download a file