Ich brauche Hilfe beim Konvertieren einer sehr großen Binärdatei (ZIP-Datei) in einen Base64String und wieder zurück. Die Dateien sind zu groß, um sie alle gleichzeitig in den Speicher zu laden (sie werfen OutOfMemoryExceptions aus), sonst wäre dies eine einfache Aufgabe. Ich möchte den Inhalt der ZIP-Datei nicht einzeln verarbeiten, sondern die gesamte ZIP-Datei bearbeiten.Konvertieren Sie eine sehr große Binärdatei in eine Base64String inkrementell
Das Problem:
ich die gesamte ZIP-Datei umwandeln kann (Testgrößen von 1 MB bis 800 MB derzeit variieren) zu Base64String, aber wenn ich es zurück konvertieren, wird es beschädigt. Die neue ZIP-Datei hat die richtige Größe, sie wird von Windows und WinRAR/7-Zip usw. als ZIP-Datei erkannt, und ich kann sogar in die ZIP-Datei schauen und den Inhalt mit den richtigen Größen/Eigenschaften sehen, aber wann Ich versuche, aus der ZIP-Datei zu extrahieren, bekomme ich: "Fehler: 0x80004005" das ist ein allgemeiner Fehlercode.
Ich bin mir nicht sicher, wo oder warum die Korruption passiert. Ich habe einige Nachforschungen angestellt, und mir ist Folgendes aufgefallen:
Wenn Sie eine große Textdatei haben, können Sie sie inkrementell ohne Probleme in Base64String konvertieren. Wenn Convert.ToBase64String
auf die gesamte Datei aufrufen ergab: „abcdefghijklmnopqrstuvwx“, nannte es dann auf die Datei in zwei Stücke ergäbe: „abcdefghijkl“ und „mnopqrstuvwx“.
Wenn die Datei eine Binärdatei ist, ist das Ergebnis leider anders. Während die gesamte Datei könnte ergeben: „abcdefghijklmnopqrstuvwx“, versucht, dies in zwei Stücke zu verarbeiten etwas wie ergeben würde: „oiweh87yakgb“ und „kyckshfguywp“.
Gibt es eine Möglichkeit, inkrementell Basis 64 codieren eine Binärdatei, während diese Korruption zu vermeiden?
Mein Code:
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();
}
Die Decodierung mehr der gleiche ist. Ich benutze die Größe der base64String
Variable oben (die abhängig von der ursprünglichen Puffergröße, mit der ich teste), als die Puffergröße für die Decodierung. Dann, statt Convert.ToBase64String()
, rufe ich Convert.FromBase64String()
an und schreibe in einen anderen Dateinamen/Pfad.
EDIT:
In meiner Eile, den Code zu reduzieren (ich es in ein neues Projekt Refactoring, getrennt von anderem Verarbeitungscode zu eliminieren, die nicht von zentraler Bedeutung für die Frage ist) Ich habe einen Fehler eingeführt. Die Basis-64-Konvertierung sollte auf dem secondaryBuffer
für alle Iterationen durchgeführt werden, speichern Sie die letzte (Identified von isFinalChunk
), wenn buffer
verwendet werden soll. Ich habe den obigen Code korrigiert.
EDIT # 2:
Vielen Dank für Ihre Kommentare/Feedback. Nachdem ich den Fehler korrigiert habe (siehe oben), habe ich meinen Code erneut getestet und er funktioniert jetzt. Ich beabsichtige, die Lösung von @rene zu testen und zu implementieren, da sie die beste Lösung zu sein scheint, aber ich dachte, dass ich auch alle meine Entdeckung wissen lassen sollte.
Was machst du mit dem sekundären Puffer und 'isFinalChunk'? Es sieht so aus, als würdest du 'ToBase64String' in einem gelöschten Puffer aufrufen, außer es ist der letzte Teil. – Blorgbeard
Problem kann in Code sein, der Dateien von Base64 in Binärdatei zurück konvertiert. Liest du Charakter in Stücke von vier oder Mulitple of Four? – Vova
@Blorgbeard - Ich verwende den secondaryBuffer, um den Inhalt des ersten/aktuellen Lesevorgangs aus der Datei zu halten. Dann lese ich erneut und suche nach einer Rückkehr von '0', um anzuzeigen, dass ich den letzten Block verarbeitet habe. Der endgültige Chunk wird so skaliert, dass er nur groß genug ist, um die Daten zu speichern, die gerade codiert werden. Z.B. - Wenn der Puffer auf 600.000 gesetzt wurde, aber der letzte Lesevorgang 1000 Byte lang ist, muss kein Byte [] mit 600.000 Elementen übergeben werden. Wenn ich nicht auf dem letzten Chunk bin, dann verarbeite ich stattdessen 'secondaryBuffer', der die erforderlichen Daten enthält. – CaptainCobol