NSURLConnection скачать большой файл (> 40MB) - PullRequest
17 голосов
/ 02 июня 2011

Мне нужно загрузить большой файл (т.е.> 40 МБ) в мое приложение с сервера, этот файл будет ZIP или PDF. Я достиг этого, используя NSURLConnection , который хорошо работает, если файл меньше, в противном случае он загружает часть заполнения и выполнение останавливается. Например, я пытался загрузить 36MB PDF-файл, но 16MB только скачал. Пожалуйста, помогите мне узнать причину? как это исправить?

FYI: Файл реализации

#import "ZipHandlerV1ViewController.h"
@implementation ZipHandlerV1ViewController
- (void)dealloc
{
    [super dealloc];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
}
 - (void)viewDidLoad
 {
     UIView *mainView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 400, 400)];
     [mainView setBackgroundColor:[UIColor darkGrayColor]];

     UIButton *downloadButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
     [downloadButton setFrame:CGRectMake(50, 50, 150, 50)];
     [downloadButton setTitle:@"Download File" forState:UIControlStateNormal];
     [downloadButton addTarget:self action:@selector(downloadFileFromURL:) forControlEvents:UIControlEventTouchUpInside];
     [mainView addSubview:downloadButton];

     [self setRequestURL:@"http://www.mobileveda.com/r_d/mcps/optimized_av_allpdf.pdf"];
     [self.view addSubview:mainView];

     [super viewDidLoad];
 }

- (void)viewDidUnload
{
    [super viewDidUnload];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}


-(void) setRequestURL:(NSString*) requestURL {
    url = requestURL;
}

-(void) downloadFileFromURL:(id) sender {
    NSURL *reqURL =  [NSURL URLWithString:url];

    NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:reqURL];
    NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
    if (theConnection) {
       webData = [[NSMutableData data] retain];
    } else {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error !" message:@"Error has occured, please verify internet connection"  delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil];

        [alert show];
        [alert release];
    }
}

-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    [webData setLength:0]; 
}

-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [webData appendData:data];
}

-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
   NSString *fileName = [[[NSURL URLWithString:url] path] lastPathComponent];
    NSArray *pathArr = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *folder = [pathArr objectAtIndex:0];

    NSString *filePath = [folder stringByAppendingPathComponent:fileName];
    NSURL *fileURL = [NSURL fileURLWithPath:filePath];
    NSError *writeError = nil;

    [webData writeToURL: fileURL options:0 error:&writeError];
    if( writeError) {
        NSLog(@" Error in writing file %@' : \n %@ ", filePath , writeError );
        return;
    }
    NSLog(@"%@",fileURL);
}

-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error !" message:@"Error has occured, please verify internet connection.."  delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil];

    [alert show];
    [alert release];

}

@end

Заголовочный файл:

#import <UIKit/UIKit.h>
@interface ZipHandlerV1ViewController : UIViewController {
    NSMutableData *webData;
    NSString *url;
}
-(void) setRequestURL:(NSString*) requestURL;
@end

Спасибо,

Изменено: Это происходит из-за того, что мой загружаемый файл находился на общем хостинге, который имел ограничения на скачивание. После того, как я переместил этот файл на выделенный сервер, он работал нормально. а также я пытался загрузить некоторые другие файлы, такие как видео с некоторых других сайтов, которые также работают нормально. Так что, если у вас возникли проблемы, такие как частичная загрузка, не просто придерживайтесь кода, проверьте сервер тоже

Ответы [ 4 ]

23 голосов
/ 02 июня 2011

В настоящее время все загруженные данные хранятся в объекте NSMutableData, который хранится в оперативной памяти вашего устройства.Это, в зависимости от устройства и доступной памяти, в какой-то момент вызовет предупреждение о памяти или даже сбой.

Чтобы заставить работать такие большие загрузки, вам придется записывать все загруженные данные непосредственно в файловую систему..

-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 
{
   //try to access that local file for writing to it...
   NSFileHandle *hFile = [NSFileHandle fileHandleForWritingAtPath:self.localPath];
   //did we succeed in opening the existing file?
   if (!hFile) 
   {   //nope->create that file!
       [[NSFileManager defaultManager] createFileAtPath:self.localPath contents:nil attributes:nil];
       //try to open it again...
       hFile = [NSFileHandle fileHandleForWritingAtPath:self.localPath];
   }
   //did we finally get an accessable file?
   if (!hFile)
   {   //nope->bomb out!
       NSLog("could not write to file %@", self.localPath); 
       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 %@", self.localPath); 
       result = NO;
   }
   [hFile closeFile];
}
7 голосов
/ 15 ноября 2011

У меня была такая же проблема, и, кажется, я нашел какое-то решение.

В вашем заголовочном файле объявите:

NSMutableData *webData;
NSFileHandle *handleFile;

В вашем файле .m на downloadFileFromURL, когда вы получаете соединение, инициируйте NSFileHandle:

if (theConnection) {

        webData = [[NSMutableData data] retain];

        if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
            [[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil];
        }

        handleFile = [[NSFileHandle fileHandleForWritingAtPath:filePath] retain];
    }

затем в didReceiveData вместо добавления данных в память запишите их на диск, например:

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {

    [webData appendData:data];

    if( webData.length > SOME_FILE_SIZE_IN_BYTES && handleFile!=nil) {          
        [handleFile writeData:recievedData];
        [webData release];
        webData =[[NSMutableData alloc] initWithLength:0];
    }
}

после завершения загрузки на connectionDidFinishLoading добавьте следующие строки, чтобы записать файл и разорвать соединение:

[handleFile writeData:webData];
[webData release];
[theConnection release];

Я пытаюсь прямо сейчас, надеюсь, что это работает ..

2 голосов
/ 10 июня 2011

Это происходит из-за того, что мой загружаемый файл находился на общем хостинге, который имел ограничения на скачивание. После того, как я переместил этот файл на выделенный сервер, он работал нормально. а также я пытался загрузить некоторые другие файлы, такие как видео с некоторых других сайтов, которые также работают нормально.

Так что, если у вас возникли проблемы, такие как частичная загрузка, не просто придерживайтесь кода, проверьте и сервер.

1 голос
/ 02 августа 2011

Если вы хотите использовать asi-http-запрос, это намного, намного проще.

Проверьте https://github.com/steipete/PSPDFKit-Demo для рабочего примера с asi.

Этопримерно так же просто, как это:

    // create request
    ASIHTTPRequest *pdfRequest = [ASIHTTPRequest requestWithURL:self.url];
    [pdfRequest setAllowResumeForFileDownloads:YES];
    [pdfRequest setNumberOfTimesToRetryOnTimeout:0];
    [pdfRequest setTimeOutSeconds:20.0];
    [pdfRequest setShouldContinueWhenAppEntersBackground:YES];
    [pdfRequest setShowAccurateProgress:YES];
    [pdfRequest setDownloadDestinationPath:destPath];

    [pdfRequest setCompletionBlock:^(void) {
        PSELog(@"Download finished: %@", self.url);

        // cruel way to update
        [XAppDelegate updateFolders];
    }];

    [pdfRequest setFailedBlock:^(void) {
        PSELog(@"Download failed: %@. reason:%@", self.url, [pdfRequest.error localizedDescription]);
    }];
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...