2011-06-02 55 views
17

我需要从服务器下载大文件(即大于40MB)到我的应用程序,这个文件将是ZIP或PDF。我使用NSURLConnection实现它,如果文件较小,则工作良好,否则它会下载部分填充并停止执行。例如我试图下载36MB的PDF文件,但只下载了16MB。请帮我知道原因?如何解决它?NSURLConnection下载大文件(> 40MB)

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 

谢谢

更新时间: 这是因为我下载的文件是在共享主机它具有下载限制。在将该文件移至专用服务器后,该服务器正常工作。也试图从其他一些网站下载一些其他文件,比如视频,这也很好。 所以,如果你有喜欢的部分下载的问题,不只是与代码棒,检查服务器太

回答

2

发生这种情况是因为我的可下载文件位于共享主机中,它具有下载限制。在将该文件移至专用服务器后,该服务器正常工作。也试图从其他一些网站下载一些其他文件,比如视频,这也很好。

所以,如果你有部分下载的问题,不仅仅是代码粘贴,检查服务器也是如此。

23

您正在保持所有下载的数据,其中保持设备的RAM内的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]; 
} 
+0

即使做了更改你提到的,它只下载6.4MB的39MB文件,为什么它会发生?是否有任何内存限制?..,我在模拟器和iPad设备上都尝试过。 – Jayabal 2011-06-09 05:42:30

+0

每次didReceiveData被调用时,这应该覆盖文件。 – 2013-04-17 14:26:12

+1

@Vova你是对的,我的例子实际上只是一个暗示,并没有完全覆盖需求。我改变了这一点,现在需要特别注意所有可能的情况。 – Till 2013-04-17 19:31:06

1

如果你愿意使用asi-http-request,它要容易得多。

检查出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]); 
    }]; 
+0

感谢steipete,现在我在过去的一周内使用ASIHttpRequest,并且我是iPhone开发新手。我还有一个疑问,那就是如何暂停和恢复下载,以及如何发现整个文件已经下载成功或者没有成功?有没有可能在ASIHttpRequest ?,再次感谢.. – Jayabal 2011-08-02 04:52:24

+0

我不确定这是内置的 - 你可能必须重写ASIHttpRequest这样做。 – steipete 2011-08-02 06:03:07

+3

-1看来ASIHTTPRequest已不再适用。 – MobileOverlord 2012-02-07 14:07:11

7

我有同样的问题,而且似乎我发现了一些解决方案。

在你的头文件中声明:

NSMutableData *webData; 
NSFileHandle *handleFile; 

在你。在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]; 

我想它,现在,希望工程..

+0

我不确定你为什么使用NSMutableData而不是仅仅使用NSFileHandle,但是我能够将这个例子下载到我的iPad上,并且没有在内存中存储任何数据。谢谢! – 2012-12-01 22:04:11

+0

@JosephDeCarlo,NSMutableData的一个很好的理由是减少I/O操作的数量。 – 2013-04-18 16:36:26

+0

我想这取决于你要下载的文件大小以及你的资源限制。 – 2013-04-20 21:49:02