2010-10-23 15 views
21

मैं एरिका सडुन की असीमित डाउनलोड की विधि का उपयोग कर रहा हूं (प्रोजेक्ट फ़ाइल के लिए यहां लिंक करें: download), हालांकि उनकी विधि उन फ़ाइलों के साथ काम नहीं करती है जिनमें बड़े आकार (50 एमबी या ऊपर) हैं। अगर मैं 50 एमबी से ऊपर की फाइल डाउनलोड करने का प्रयास करता हूं, तो यह आमतौर पर मेमोरी क्रैश के कारण क्रैश हो जाएगा। क्या वैसे भी मैं इस कोड को ट्विक कर सकता हूं ताकि यह बड़ी फ़ाइलों के साथ भी काम करे? यहाँ कोड मैं DownloadHelper कक्षा में है (जो डाउनलोड लिंक में पहले से ही है):एक बड़ी फ़ाइल डाउनलोड करना - आईफोन एसडीके

@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 

मीटर

#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]; 

} 

अगर कोई मेरी मदद कर सकता है तो मैं बहुत खुश हूं!

धन्यवाद,

केविन

+2

मैंने आपके द्वारा वर्णित विधि का उपयोग करके उस के लिए एक लाइब्रेरी लिखी है। मैं इसे यहां उम्मीद कर रहा हूं कि यह कुछ लोगों के लिए उपयोगी होगा, या उन्हें अपने स्वयं के समाधान लिखने के लिए प्रेरित करेगा। यदि आप निश्चित रूप से इसके साथ ठीक हैं। https://github.com/thibaultCha/TCBlobDownload – thibaultcha

उत्तर

29

में स्मृति एक NSOutputStream *stream साथ NSData *data बदलें। धारा

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

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

डेटा में आता है, यह स्ट्रीम में लिखें: -start में संलग्न करें और इसे खोलने के लिए स्ट्रीम बनाने

एक बेहतर तरीका डेटा ivar के अलावा स्ट्रीम जोड़ने के लिए, स्ट्रीम के प्रतिनिधि के रूप में सहायक सेट करें, डेटा ivar में आने वाले डेटा को बफर करें, फिर डेटा इवर की सामग्री को सहायक में डंप करें जब भी स्ट्रीम सहायक अंतरिक्ष को उपलब्ध स्थान भेजती है और उसे डेटा ivar से साफ़ करती है।

+0

प्रतिक्रिया के लिए धन्यवाद, लेकिन फिर भी डाउनलोड के बारे में जानकारी प्राप्त करना अभी भी संभव है? यह प्राप्त करने की तरह कि डेटा कितना डाउनलोड किया गया है? मैं आमतौर पर बस इसका उपयोग करता हूं: self.data.length लेकिन चूंकि इस नई विधि में एनएसएमयूटेबलडेटा नहीं है, मुझे नहीं पता कि इसे कैसे कार्यान्वित किया जाए। इसके अलावा (चूंकि मैं उद्देश्य के लिए नया हूं- सी), क्या मैं पूरी तरह से .h फ़ाइल में एनएसएमयूटेबलडेटा से छुटकारा पाता हूं और हेल्पर कक्षाओं में इसके सभी उदाहरणों से छुटकारा पाता हूं? – lab12

+0

अरे मुझे अभी भी इस डाउनलोड विधि का उपयोग करने में परेशानी हो रही है। डीबगर में यह मुझे यह त्रुटि देता है: "स्ट्रीम त्रुटि: त्रुटि डोमेन = NSPOSIXErrorDomain कोड = 1" ऑपरेशन पूरा नहीं हो सका। ऑपरेशन की अनुमति नहीं है "UserInfo = 0x148660 {} " मुझे नहीं पता कि यह क्यों दिखाई दे रहा है। क्या मेरे पास पथ गलत तरीके से सेट है? क्या यह एक निर्देशिका या फ़ाइल होने का अनुमान है? यदि आप एक उदाहरण कोड प्रदान कर सकते हैं तो यह बहुत अच्छा होगा !! – lab12

+0

अपना कोड पोस्ट करें (उदा। [Gist.github.com] (http://gist.github.com/) पर), और मैं इसे देख सकता हूं। आउटपुट स्ट्रीम उस निर्देशिका में होनी चाहिए जिसमें आपके पास लिखने की पहुंच है, जैसे कि आपके ऐप की दस्तावेज़ निर्देशिका। ऐसा लगता है कि समस्या यह है कि आप कहीं और लिखने की कोशिश कर रहे हैं कि सिस्टम अनुमति नहीं देगा। –

3

मेरे पास उपरोक्त कोड में थोड़ा सा संशोधन है।

इस फ़ंक्शन का उपयोग करें, यह मेरे लिए ठीक काम करता है।

- (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

का उपयोग करें, यह सभी डेटा को स्ट्रीम में लिख देगा यह डाउनलोड समाप्त हो गया है। ओपी बहुत सारी डाउनलोड के साथ सभी उपलब्ध मेमोरी का उपयोग कर रहा था, आपका जवाब इसका समाधान नहीं करता है। –

0

AFNetworking आज़माएं। और:

NSString *[email protected]"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]; 
संबंधित मुद्दे