2016-05-26 9 views
0

POSTリクエストを使用して、サーバーから大きなファイルをダウンロードしようとしています。ファイルはbase64Encodedデータとして受信されます。ファイルサイズは400Mbを超えています。私が使用した最初のアプローチは、受信データとその後にconnectionDidFinishLoading私はデコードを使用して、データを文書ディレクトリにファイルに書き込むデータを追加することでした。この方法は、サイズの小さいファイルに対して有効です。しかし、ファイルサイズが非常に大きい(400MB)のでクラッシュします。その後、NSFileHandleのアプローチを使用して、データが受信されたときにチャンクにファイルを書き込みました。私は3MBのチャンクを使ってデータを書きました。 3MBより小さいファイルの場合、受信したデータはbase64から直接デコードされ、ファイルは正常に作成されます。しかし、ファイルサイズが3MBを超えると、 "initWithBase64EncodedData:Option"を使用してbase64からチャンクデータをデコードしようとすると、取得するデータはゼロになります。私がコードで間違っていることを知りたいですか?NsurlConnectionを使用して大きなbase64エンコードデータをダウンロードする

- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response 
{ 
    receivedData = [[NSMutableData alloc] init]; 

    NSString* folderPath = [NSHomeDirectory() stringByAppendingPathComponent:@"/Documents/UserDocs"]; 
    if(![[NSFileManager defaultManager] fileExistsAtPath:folderPath]) { 
     [[NSFileManager defaultManager] createDirectoryAtPath:folderPath withIntermediateDirectories:NO attributes:nil error:nil]; 
    } 

NSString *strFilePath = [folderPath stringByAppendingPathComponent:strFileName]; NSString *newFilePath = [NSString stringWithFormat:@"%@.%@", strFilePath, strFileExtension]; 
if(![[NSFileManager defaultManager] fileExistsAtPath:newFilePath]) { 
      [[NSFileManager defaultManager] createFileAtPath:newFilePath contents:nil attributes:nil]; 
     } 

self.fileHandle = [[NSFileHandle fileHandleForWritingAtPath:newFilePath] retain]; 
     [self.fileHandle seekToFileOffset:0]; 
} 

- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data 
{ 
    [receivedData appendData:data]; 
    NSUInteger chunkSize = 300000; 
    if(receivedData.length > chunkSize && self.fileHandle!=nil) { 

     NSData *chunk = [receivedData subdataWithRange:NSMakeRange(0, chunkSize)]; 
     NSData *writableData = [[NSData alloc] initWithBase64EncodedData:chunk options:NSDataBase64DecodingIgnoreUnknownCharacters]; 
     [receivedData replaceBytesInRange:NSMakeRange(0, chunkSize) withBytes:NULL length:0]; 

     [self.fileHandle seekToEndOfFile]; 
     [self.fileHandle writeData:writableData]; 
     [writableData release]; 
    } 
} 

- (void)connectionDidFinishLoading:(NSURLConnection*)connection 
{ 
    if (receivedData) 
    { 
     NSData *writableData = [[NSData alloc] initWithBase64EncodedData:receivedData options:NSDataBase64DecodingIgnoreUnknownCharacters]; 
     [receivedData release]; 

     [self.fileHandle seekToEndOfFile]; 
     [self.fileHandle writeData:writableData]; 
     [writableData release]; 

     NSLog(@"File Write Success!!"); 
    } 
} 
+0

Base64は1対1エンコード方式ではありません。したがって、現在のチャンク*で必要とされている以前の*チャンク*のデータがあります。いくつかの形式のbase-64ストリームデコーダを使用する可能性を調べる必要があります。ここで入力データの塊を送り、デコードされたデータをどこに書き込むかを教えてください。 – trojanfoe

+0

NSStream、データファイルのストリームを試して、オンザフライでデコード/エンコードして1つのバイナリを作成してください。コードで動作すれば教えてください:) –

+0

サーバーを制御していますか?もしそうなら、base64エンコーディングをせずにそのままの状態でデータを送信するのはなぜですか?たとえば、大規模なブロブを超えて追加のデータを返送する必要がある場合は、マルチパートMIME応答を使用できます。または、メタデータを最初に提供し、2番目のリクエストでエンコードされていない大きなデータファイルを要求できるURLを指定します。 – dgatwood

答えて

0

つの可能なアプローチ:(できれば)

  1. はbase64エンコードをスキップします。
  2. ディスクにデータを書き込んだ後、メモリマッピングを使用してファイルをRAMにマップします。メモリにあるものと同じように扱うことができますが、実際にメモリを占有することはありません。
  3. 基本的に

:ディスク上のファイルに直接データを書き込むことができbase64でデコーダを作成

#include <sys/stat.h> 
#include <sys/mman.h> 
... 
NSURL *url = // path to file on disk 
char *path = [url fileSystemRepresentation]; 
struct stat buf; 

// Round up to an exact multiple of the page size 
NSUInteger size = (buf.st_size + PAGE_MASK) & ~PAGE_MASK; 
NSData *data = nil; 

if (!stat(path, &buf)) { 
    int fd = open(path, O_RDONLY); 
    if (fd != -1) { 
     mmap(NULL, size, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0); 
     data = [NSData dataWithBytesNoCopy:buf length:buf.st_size]; 
    } 
} 

// Perform a base64 decode on the NSData object and hope that 
// at least the result will fit in RAM. 

// Clean up. 
data = nil; 
if (!stat(path, &buf)) { 
    int fd = open(path, O_RDONLY); 
    if (fd != -1) { 
     munmap(addr, size); 
    } 
} 

は、読者の練習として残しています。 :-)

+0

base64デコーダを見つけることができなかったので、base64エンコーディングをスキップしました。 base64の代わりに、サーバーはファイルストリームを返し、私は質問で述べたのと同じコードを使用します。 – sashah

関連する問題