NSURLConnection下载大文件(> 40MB)

时间:2011-06-02 13:22:19

标签: iphone ios file download

我需要从服务器下载大文件(即> 40MB)到我的应用程序,此文件将是ZIP或PDF。我使用 NSURLConnection 实现了它,如果文件较小,则可以正常工作,因为它下载了部分填充并且执行已经停止。例如我试图下载36MB PDF文件,但只下载了16MB。请帮我知道原因?怎么解决?

供参考: 实施档案

#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 个答案:

答案 0 :(得分:23)

您当前正在将所有下载的数据保存在NSMutableData对象中,该对象保留在设备的RAM中。这将取决于设备和可用内存,在某些时候触发内存警告甚至崩溃。

要使这么大的下载工作,您必须将所有下载的数据直接写入文件系统。

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

答案 1 :(得分:7)

我遇到了同样的问题,似乎我发现了一些解决方案。

在头文件中声明:

NSMutableData *webData;
NSFileHandle *handleFile;

downloadFileFromURL上的.m文件中,当您获得连接时启动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 :(得分:2)

这是因为我的可下载文件位于具有下载限制的共享主机中。我将该文件移动到工作正常的专用服务器后。而且我也尝试下载一些其他文件,比如来自其他一些网站的视频,这些文件也运行良好。

因此,如果您遇到部分下载等问题,请不要只使用代码,也请检查服务器。

答案 3 :(得分:1)

如果您愿意使用asi-http-request,那就更容易了。

使用asi。

查看https://github.com/steipete/PSPDFKit-Demo的工作示例

这很容易:

    // 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]);
    }];