Загрузка большого файла - iPhone SDK - PullRequest
21 голосов
/ 23 октября 2010

Я использую метод Асинхронной загрузки Эрики Садун (ссылка здесь для файла проекта: загрузка ), однако ее метод не работает с файлами большого размера (50 МБ или выше).Если я пытаюсь загрузить файл размером более 50 МБ, он обычно вылетает из-за сбоя памяти.Можно ли как-нибудь настроить этот код, чтобы он работал и с большими файлами?Вот код, который я имею в Классах DownloadHelper (который уже находится в ссылке для скачивания):

.h

@protocol DownloadHelperDelegate <NSObject>
@optional
- (void) didReceiveData: (NSData *) theData;
- (void) didReceiveFilename: (NSString *) aName;
- (void) dataDownloadFailed: (NSString *) reason;
- (void) dataDownloadAtPercent: (NSNumber *) aPercent;
@end

@interface DownloadHelper : NSObject 
{
    NSURLResponse *response;
    NSMutableData *data;
    NSString *urlString;
    NSURLConnection *urlconnection;
    id <DownloadHelperDelegate> delegate;
    BOOL isDownloading;
}
@property (retain) NSURLResponse *response;
@property (retain) NSURLConnection *urlconnection;
@property (retain) NSMutableData *data;
@property (retain) NSString *urlString;
@property (retain) id delegate;
@property (assign) BOOL isDownloading;

+ (DownloadHelper *) sharedInstance;
+ (void) download:(NSString *) aURLString;
+ (void) cancel;
@end

.m

#define DELEGATE_CALLBACK(X, Y) if (sharedInstance.delegate && [sharedInstance.delegate respondsToSelector:@selector(X)]) [sharedInstance.delegate performSelector:@selector(X) withObject:Y];
#define NUMBER(X) [NSNumber numberWithFloat:X]

static DownloadHelper *sharedInstance = nil;

@implementation DownloadHelper
@synthesize response;
@synthesize data;
@synthesize delegate;
@synthesize urlString;
@synthesize urlconnection;
@synthesize isDownloading;

- (void) start
{
    self.isDownloading = NO;

    NSURL *url = [NSURL URLWithString:self.urlString];
    if (!url)
    {
        NSString *reason = [NSString stringWithFormat:@"Could not create URL from string %@", self.urlString];
        DELEGATE_CALLBACK(dataDownloadFailed:, reason);
        return;
    }

    NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url];
    if (!theRequest)
    {
        NSString *reason = [NSString stringWithFormat:@"Could not create URL request from string %@", self.urlString];
        DELEGATE_CALLBACK(dataDownloadFailed:, reason);
        return;
    }

    self.urlconnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
    if (!self.urlconnection)
    {
        NSString *reason = [NSString stringWithFormat:@"URL connection failed for string %@", self.urlString];
        DELEGATE_CALLBACK(dataDownloadFailed:, reason);
        return;
    }

    self.isDownloading = YES;

    // Create the new data object
    self.data = [NSMutableData data];
    self.response = nil;

    [self.urlconnection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}

- (void) cleanup
{
    self.data = nil;
    self.response = nil;
    self.urlconnection = nil;
    self.urlString = nil;
    self.isDownloading = NO;
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)aResponse
{
    // store the response information
    self.response = aResponse;

    // Check for bad connection
    if ([aResponse expectedContentLength] < 0)
    {
        NSString *reason = [NSString stringWithFormat:@"Invalid URL [%@]", self.urlString];
        DELEGATE_CALLBACK(dataDownloadFailed:, reason);
        [connection cancel];
        [self cleanup];
        return;
    }

    if ([aResponse suggestedFilename])
        DELEGATE_CALLBACK(didReceiveFilename:, [aResponse suggestedFilename]);
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)theData
{
    // append the new data and update the delegate
    [self.data appendData:theData];
    if (self.response)
    {
        float expectedLength = [self.response expectedContentLength];
        float currentLength = self.data.length;
        float percent = currentLength / expectedLength;
        DELEGATE_CALLBACK(dataDownloadAtPercent:, NUMBER(percent));
    }
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    // finished downloading the data, cleaning up
    self.response = nil;

    // Delegate is responsible for releasing data
    if (self.delegate)
    {
        NSData *theData = [self.data retain];
        DELEGATE_CALLBACK(didReceiveData:, theData);
    }
    [self.urlconnection unscheduleFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    [self cleanup];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    self.isDownloading = NO;
    NSLog(@"Error: Failed connection, %@", [error localizedDescription]);
    DELEGATE_CALLBACK(dataDownloadFailed:, @"Failed Connection");
    [self cleanup];
}

+ (DownloadHelper *) sharedInstance
{
    if(!sharedInstance) sharedInstance = [[self alloc] init];
    return sharedInstance;
}

+ (void) download:(NSString *) aURLString
{
    if (sharedInstance.isDownloading)
    {
        NSLog(@"Error: Cannot start new download until current download finishes");
        DELEGATE_CALLBACK(dataDownloadFailed:, @"");
        return;
    }

    sharedInstance.urlString = aURLString;
    [sharedInstance start];
}

+ (void) cancel
{
    if (sharedInstance.isDownloading) [sharedInstance.urlconnection cancel];
}
@end

И наконецвот как я пишу файл двумя классами над ним:

- (void) didReceiveData: (NSData *) theData
{
    if (![theData writeToFile:self.savePath atomically:YES])
        [self doLog:@"Error writing data to file"];

    [theData release];

}

Если бы кто-нибудь мог мне помочь, я был бы очень рад!

Спасибо,

Кевин

Ответы [ 3 ]

30 голосов
/ 23 октября 2010

Заменить в памяти NSData *data на NSOutputStream *stream. В -start создайте поток для добавления и откройте его:

stream = [[NSOutputStream alloc] initToFileAtPath:path append:YES];
[stream open];

Когда данные поступают, запишите их в поток:

NSUInteger left = [theData length];
NSUInteger nwr = 0;
do {
    nwr = [stream write:[theData bytes] maxLength:left];
    if (-1 == nwr) break;
    left -= nwr;
} while (left > 0);
if (left) {
    NSLog(@"stream error: %@", [stream streamError]);
}

Когда вы закончите, закройте поток:

[stream close];

Лучше было бы добавить поток в дополнение к данным ivar, установить помощника в качестве делегата потока, буферизовать входящие данные в data ivar, а затем выгрузить содержимое данных ivar в помощник всякий раз, когда поток отправляет помощника. его доступное место и очистить его от данных ivar.

3 голосов
/ 25 августа 2011

У меня есть небольшая модификация вышеуказанного кода.

Используйте эту функцию, она прекрасно работает для меня.

- (void) didReceiveData: (NSData*) theData
{   
    NSOutputStream *stream=[[NSOutputStream alloc] initToFileAtPath:self.savePath append:YES];
    [stream open];
    percentage.hidden=YES;
    NSString *str=(NSString *)theData;
    NSUInteger left = [str length];
    NSUInteger nwr = 0;
    do {
        nwr = [stream write:[theData bytes] maxLength:left];
        if (-1 == nwr) break;
        left -= nwr;
    } while (left > 0);
    if (left) {
        NSLog(@"stream error: %@", [stream streamError]);
    }
    [stream close];
}
0 голосов
/ 28 августа 2015

Попробуйте AFNetworking .И:

NSString *yourFileURL=@"http://yourFileURL.zip";
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:yourFileURL]];
AFURLConnectionOperation *operation =   [[AFHTTPRequestOperation alloc] initWithRequest:request];

NSString *cacheDir = [NSSearchPathForDirectoriesInDomains
                          (NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [cacheDir stringByAppendingPathComponent:
                      @"youFile.zip"];

operation.outputStream = [NSOutputStream outputStreamToFileAtPath:filePath append:NO];

[operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
   //show here your downloading progress if needed
}];

[operation setCompletionBlock:^{
    NSLog(@"File successfully downloaded");
}];

[operation start];
...