4

私は現在、大きなファイル(主に映画/ビデオ)をWebにアップロードする必要があるアプリケーションに取り組んでいます。私ができることを読んだ後、私は映画をNSDataに変換し、それをに含めるというアプローチを取りましたNSURLConnection's HTTPBody。ただし、ムービー(元々はALAsset)をに変換するNSDataと、メモリ警告が表示され、その後クラッシュします。

そのデータが即座にクラッシュする場合、これらのタイプの大きなファイルをアップロードする方法がわかりません。私が考えていた解決策の1つは、ファイルシステムに書き込み、そこから直接ファイルをアップロードすることですが、これを実現する方法についての情報を見つけることができませんでした。

これが私が使用する関連コードです。私がここで間違っていることがあれば、知りたいです。

ALAssetRepresentation *representation = [asset defaultRepresentation];

Byte *buffer = (Byte *)malloc([representation size]);
NSUInteger buffered = [representation getBytes:buffer fromOffset:0.0 length:[representation size] error:nil];

uploadData = [NSData dataWithBytes:buffer length:buffered];

free(buffer);
4

3 に答える 3

2

映画をネイティブ形式でアップロードすることが理にかなっていると仮定すると、BSD(つまりUnix)セクション3インターフェイスを使用してこれを本当に簡単にすることができます。

  • filePathを指定して、ファイルを開き、intファイル記述子(fd)を取得します。

  • fdを使用して、ファイルの長さを取得します

  • ロードした量を追跡して、より多くのデータを取得する場所を把握します

  • mmap(3)を使用して、いつでもアップロードするデータをJUSTにマッピングし、mmapから返されたvoid*ポインターをデータの場所として使用します。

  • データが送信されたら、古いデータチャンクをmunmapし、新しいチャンクをmmapします

  • すべてのデータが送信されたら、最後のチャンクであるclose(fd)をmunmapします。

一時的なメモリはありません-mallocはありません。巨大なファイルを処理する必要があるときはいつでもmmapを使用します。

編集:mmapを使用するように設定されたオプションでNSData dataWithContentsOfFile:optionsを使用することもできます。次に、バイトポインターを使用して、必要に応じて小さなチャンクを読み取ります。

于 2012-08-14T23:22:46.330 に答える
1

誰かがここに来てあなたの問題を解決できなかった場合に備えて、私はこれを行う方法を考え出しました。最初ALAssetRepresentationにディスクに書き込む必要があります(ここで説明されています)。

NSUInteger chunkSize = 100 * 1024;
NSString *tempFile = [NSTemporaryDirectory() stringByAppendingPathComponent:@"temp.tmp"];

uint8_t *chunkBuffer = malloc(chunkSize * sizeof(uint8_t));
NSUInteger length = [rep size];

NSFileHandle *fileHandle = [[NSFileHandle fileHandleForWritingAtPath: tempFile] retain];
if(fileHandle == nil) {
    [[NSFileManager defaultManager] createFileAtPath:tempFile contents:nil attributes:nil];
    fileHandle = [[NSFileHandle fileHandleForWritingAtPath:tempFile] retain];
}

NSUInteger offset = 0;
do {
    NSUInteger bytesCopied = [rep getBytes:chunkBuffer fromOffset:offset length:chunkSize error:nil];
    offset += bytesCopied;
    NSData *data = [[NSData alloc] initWithBytes:chunkBuffer length:bytesCopied];
    [fileHandle writeData:data];
    [data release];
} while (offset < length);
[fileHandle closeFile];
[fileHandle release];
free(chunkBuffer);
chunkBuffer = NULL;

次に、メモリリソースを使用せずにディスクをマップできるNSDataオブジェクトを作成する必要があります(Davidの回答のようなものですが、この回答に触発されています)。

NSError *error;
NSData *fileData = [NSData dataWithContentsOfFile:tempFile options:NSDataReadingMappedIfSafe error:&error];
if (!fileData) {
    NSLog(@"Error %@ %@", error, [error description]);
    NSLog(@"%@", tempFile);
    //do what you need with the error
}

編集ただし、ファイルをどこかにアップロードする場合は、接続を開いてファイルの小さなバッファーを送信する必要があります。これは、上記で行ったようなものです。ソケットと接続を処理するためにC++クラスを作成する必要がありました

于 2013-04-23T12:49:14.223 に答える
0

おそらく、アセット全体を一度に読み取ろうとするべきではありません。

Byte *buffer = (Byte *)malloc([representation size]);
NSUInteger buffered = [representation getBytes:buffer fromOffset:0.0 length:[representation size] error:nil];

代わりに、ループを設定し、アセットからチャンクで読み取ります。基本的なアプローチの概要を説明しました。いくつかのギャップを埋める必要がありますが、メモリの問題は解決するはずです。

UIをロックしないように、これをスレッドで実行することを検討することもできます。

    NSError error;
    int bufferSize = 1000;
    float offset=0.0;

//TODO: Open Connection

    while (1)
    {
      Byte *buffer = (Byte *)malloc(bufferSize);
       NSUInteger buffered = [representation getBytes:buffer fromOffset:offset length:bufferSize  error:&error];

    //TODO: Write data  
    //TODO: Increment offset, check errors

    free(buffer);

    //if (done){
    //break;
    //}

    }

    //TODO close eonnection
于 2012-08-14T22:34:28.627 に答える