2012-11-02 10 views
9

Descrizione: Ho bisogno di inviare una stringa codificata 64 di base in un parametro HTTP POST che rappresenta un file da caricare.Large Base64 upload in iOS

metodo attuale: sto usando un ASIFormDataRequest e la codifica il mio file in una stringa base64 in questo modo:

NSData *data = [[NSFileManager defaultManager] contentsAtPath:@"my path here"]; 
NSString *base64Data = [data encodeBase64ForData]; 

Tuttavia durante il caricamento di file di grandi dimensioni l'App esaurisce la memoria e muore di una morte orribile!

Soluzione proposta: Qualcuno ora come andrei circa, ad esempio, la lettura del file dal disco, la conversione in stringhe base64 a pezzi e allegando la stringa base64 convertita a un parametro nella richiesta HTTP?

Effettivamente qualsiasi modo che eviti di leggere l'intero file in memoria.

Qualsiasi input sarebbe molto apprezzato!

mio http codice di richiesta per intero:

NSURL *url = [NSURL URLWithString:requestProgressURLString]; 
NSData *data = [[NSFileManager defaultManager] contentsAtPath:evidence.uri]; 
NSString *base64Data = [data encodeBase64ForData]; 
NSURL *fileURL = [[NSURL alloc] initWithString:evidence.uri]; 

request = [ASIFormDataRequest requestWithURL:url]; 

request.shouldAttemptPersistentConnection = false; 
[request setShowAccurateProgress:YES]; 

[request setPostValue:accessToken forKey:@"AccessToken"]; 
[request setPostValue:[[fileURL path] lastPathComponent] forKey:@"Filename"]; 
[request setPostValue:evidence.name forKey:@"Title"]; 
[request setPostValue:base64Data forKey:@"FileData"]; //wants string value (base64 encoded)   
[request addRequestHeader:@"Content-Type" value:@"application/x-www-form-urlencoded"]; 
[request setTimeOutSeconds:60]; 
[request setUploadProgressDelegate:self]; 
[request setDownloadProgressDelegate:self]; 
[request setDelegate:self]; 
[request setDidFinishSelector:@selector(requestDone:)]; 
[request setDidFailSelector:@selector(requestWentWrong:)]; 
+0

Non ho mai visto un convertitore Base64 chunking, ma non dovrebbe essere troppo difficile prendere un'implementazione open source e trasformarlo in uno chunking. Base64 non è * quello * complicato. –

+1

Hai anche il controllo del web server? Significato puoi modificare il codice anche lì? E quando dici file di grandi dimensioni? Quanto largo? 100 MB? 500 MB? 1 GB? –

+0

Al momento i caricamenti sono limitati a 100 MB, anche se questo potrebbe aumentare in futuro. Idealmente la soluzione dovrebbe funzionare indipendentemente dalla dimensione del caricamento, poiché sono sicuro che questo codice verrà utilizzato in molti altri progetti. Sto dando un'occhiata ai flussi al momento ... –

risposta

0

Invece di leggere il file in un colpo solo è necessario leggerlo in blocchi

È possibile utilizzare un flusso di input per fare questo Apple hanno un po ' informazioni here

Fondamentalmente è necessario creare un NSInputStream puntato verso il file come nell'esempio

- (void)setUpStreamForFile:(NSString *)path { 
    // iStream is NSInputStream instance variable 
    iStream = [[NSInputStream alloc] initWithFileAtPath:path]; 
    [iStream setDelegate:self]; 
    [iStream scheduleInRunLoop:[NSRunLoop currentRunLoop] 
     forMode:NSDefaultRunLoopMode]; 
    [iStream open]; 
} 

È quindi possibile leggere i dati in blocchi utilizzando il metodo NSStreamDelegate - (void) flusso: (NSStream *) flusso handleEvent: (NSStreamEvent) EventCode

Ancora una volta Apple ha Segue un esempio di questo

- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode { 

    switch(eventCode) { 
     case NSStreamEventHasBytesAvailable: 
     { 
      if(!_data) { 
       _data = [[NSMutableData data] retain]; 
      } 
      uint8_t buf[1024]; 
      unsigned int len = 0; 
      len = [(NSInputStream *)stream read:buf maxLength:1024]; 
      if(len) { 
       [_data appendBytes:(const void *)buf length:len]; 
       // bytesRead is an instance variable of type NSNumber. 
       [bytesRead setIntValue:[bytesRead intValue]+len]; 

       // DO SOMETHING with DATA HERE 

      } else { 
       NSLog(@"no buffer!"); 
      } 
      break; 
     } 

La dimensione dei blocchi scelti deve essere un bilanciamento delle prestazioni della memoria rispetto alle prestazioni della rete poiché ovviamente c'è un sovraccarico con le intestazioni HTTP

È inoltre possibile ottimizzare ulteriormente inviando la directory NSData. ectly (e quindi salvare l'overhead base64) controllare questo post

File Upload to HTTP server in iphone programming

3

Vorrei dividere questo problema in due:

1. Codifica file di grandi dimensioni a Base64 in blocchi
2. Carica quel file


1. Codifica di grandi dimensioni file in Base64 in blocchi:

Trova un codice che codifica le istanze NSData in Base64.Quindi utilizzare uno NSInputStream per leggere i dati in blocchi, codificarlo e utilizzare NSOutputStream per scrivere su un file temporaneo. Ho trovato questa domanda/risposta: Is it possible to base64-encode a file in chunks? Rendi la dimensione del blocco un multiplo di 3 e dovrebbe funzionare. Si consiglia di spostare questo in thread in background.

Importante: Questo creerà un file temporaneo, che è più grande dell'originale. Ci deve essere abbastanza spazio sul dispositivo per quel momento. Dopo aver iniziato il caricamento, puoi eliminarlo.


2. Carica il file che

Sembra che hai già fatto questo. Puoi usare quello ASIFormDataRequest esattamente come nel tuo esempio. E poiché crea una copia privata del file (costruisce il file POST in più parti), puoi eliminare la tua copia.

Se si desidera ottimizzarlo parallelizzando questi passaggi, sarà necessario trovare un altro modo per caricare il file multipart.