Ho bisogno di aiuto per convertire un file binario MOLTO GRANDE (file ZIP) in Base64String e viceversa. I file sono troppo grandi per essere caricati in memoria tutti in una volta (gettano OutOfMemoryExceptions) altrimenti questo sarebbe un compito semplice. Non voglio elaborare i contenuti del file ZIP singolarmente, voglio elaborare l'intero file ZIP.Convertire un file binario MOLTO GRANDE in Base64String in modo incrementale
Il problema:
posso convertire l'intero file ZIP (dimensioni di prova variano da 1 MB a 800 MB al momento) per Base64String, ma quando ho riconvertirlo, è danneggiato. Il nuovo file ZIP ha le dimensioni corrette, è riconosciuto come file ZIP da Windows e WinRAR/7-Zip, ecc., E posso persino guardare all'interno del file ZIP e vedere i contenuti con le dimensioni/proprietà corrette, ma quando Tento di estrarre dal file ZIP, ottengo: "Errore: 0x80004005" che è un codice di errore generale.
Non sono sicuro di dove o perché avvenga la corruzione. Ho effettuato alcune ricerche e ho notato quanto segue:
Se si dispone di un file di testo di grandi dimensioni, è possibile convertirlo in Base64String in modo incrementale senza problemi. Se chiamando Convert.ToBase64String
su tutto il file prodotto: "abcdefghijklmnopqrstuvwx", quindi chiamando sul file in due pezzi sarebbe resa: "abcdefghijkl" e "mnopqrstuvwx".
Sfortunatamente, se il file è un file binario, il risultato è diverso. Mentre l'intero file potrebbe cedere: "abcdefghijklmnopqrstuvwx", cercando di elaborare questo in due pezzi produrrebbe qualcosa come: "oiweh87yakgb" e "kyckshfguywp".
C'è un modo per codificare in modo incrementale 64 codificare un file binario evitando questo danneggiamento?
Il mio codice:
private void ConvertLargeFile()
{
FileStream inputStream = new FileStream("C:\\Users\\test\\Desktop\\my.zip", FileMode.Open, FileAccess.Read);
byte[] buffer = new byte[MultipleOfThree];
int bytesRead = inputStream.Read(buffer, 0, buffer.Length);
while(bytesRead > 0)
{
byte[] secondaryBuffer = new byte[buffer.Length];
int secondaryBufferBytesRead = bytesRead;
Array.Copy(buffer, secondaryBuffer, buffer.Length);
bool isFinalChunk = false;
Array.Clear(buffer, 0, buffer.Length);
bytesRead = inputStream.Read(buffer, 0, buffer.Length);
if(bytesRead == 0)
{
isFinalChunk = true;
buffer = new byte[secondaryBufferBytesRead];
Array.Copy(secondaryBuffer, buffer, buffer.length);
}
String base64String = Convert.ToBase64String(isFinalChunk ? buffer : secondaryBuffer);
File.AppendAllText("C:\\Users\\test\\Desktop\\Base64Zip", base64String);
}
inputStream.Dispose();
}
La decodifica è più la stessa. Uso la dimensione della variabile base64String
qui sopra (che varia in base alla dimensione del buffer originale con cui eseguo il test), come dimensione del buffer per la decodifica. Quindi, invece di Convert.ToBase64String()
, chiamo Convert.FromBase64String()
e scrivo in un nome/percorso diverso.
EDIT:
Nella mia fretta di ridurre il codice (ho refactoring in un nuovo progetto, separato da altre lavorazioni per eliminare codice che non è al centro del problema) ho introdotto un bug. La conversione di base 64 deve essere eseguita su secondaryBuffer
per tutte le iterazioni con salvataggio dell'ultima (Identificato da isFinalChunk
), quando deve essere utilizzato buffer
. Ho corretto il codice sopra.
EDIT # 2:
Grazie a tutti per i vostri commenti/feedback. Dopo aver corretto il bug (vedere la modifica sopra), ho riesaminato il mio codice e attualmente funziona. Intendo testare e implementare la soluzione di @ rene in quanto sembra essere la migliore, ma ho pensato che avrei dovuto far sapere a tutti anche della mia scoperta.
Cosa stai facendo con il buffer secondario e 'isFinalChunk'? Sembra che tu stia chiamando 'ToBase64String' su un buffer cancellato a meno che non sia il pezzo finale. – Blorgbeard
Il problema potrebbe essere nel codice che converte i file da base64 in file binario. Leggi caratteri in blocchi di quattro o Mulitple of Four. – Vova
@Blorgbeard - Sto usando il secondaryBuffer per contenere il contenuto della prima lettura/corrente dal file. Poi ho letto di nuovo, cercando un ritorno di "0" per indicare che sto elaborando il blocco finale. Il blocco finale viene ridimensionato in modo che sia sufficientemente grande da contenere i dati che vengono codificati. Per esempio. - se il buffer è stato impostato su 600.000, ma l'ultima lettura è lunga 1000 byte, non è necessario passare un byte [] contenente 600.000 elementi. Se non sono sul blocco finale, allora elaborerò 'secondaryBuffer', che contiene i dati richiesti. – CaptainCobol