NSURLConnection загрузить большой файл (> 40MB)
Мне нужно загрузить большой файл (то есть > 40 МБ) в мое приложение с сервера, этот файл будет ZIP или PDF. Я достиг этого с помощью NSURLConnection, который хорошо работает, если файл меньше другого, он загружает часть заполнения, и выполнение останавливается. например, я попытался загрузить 36MB PDF файл, но только 16 МБ загружался. PLS помочь мне узнать причину? как это исправить?
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
Спасибо,
Обновлено:
Это происходит из-за того, что мой загружаемый файл находился в общем хостинге, который имеет ограничения на скачивание. После того, как я переместил этот файл на выделенный сервер, который работает нормально. а также я попытался загрузить некоторые другие файлы, такие как видео с некоторых других сайтов, которые также отлично работают.
Итак, если у вас проблемы с частичной загрузкой, не только придерживайтесь кода, проверьте сервер тоже
Ответы
Ответ 1
Это происходит из-за того, что мой загружаемый файл находился в общем хостинге, который имеет ограничения на скачивание. После того, как я переместил этот файл на выделенный сервер, который работает нормально. а также я попытался загрузить некоторые другие файлы, такие как видео с некоторых других сайтов, которые также отлично работают.
Итак, если у вас проблемы с частичной загрузкой, не только придерживайтесь кода, проверьте сервер тоже.
Ответ 2
В настоящее время вы сохраняете все загруженные данные в объекте 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];
}
Ответ 3
У меня была такая же проблема, и, похоже, я обнаружил какое-то решение.
В вашем файле заголовка объявите:
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];
Я пытаюсь сделать это прямо сейчас, надеюсь, что это сработает.
Ответ 4
Если вы хотите использовать 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]);
}];