Ich arbeite in C # und kommuniziere zwischen zwei Apps, die ich schreibe. Ich mag die Web-API und JSON. Jetzt bin ich an dem Punkt, an dem ich eine Routine schreibe, um einen Datensatz zwischen den beiden Servern zu senden, der einige Textdaten und eine Datei enthält.
Laut Internet soll ich eine mehrteilige / Formulardatenanfrage verwenden, wie hier gezeigt:
SO Frage "Mehrteilige Formulare vom C # -Client"
Grundsätzlich schreiben Sie eine Anfrage manuell in folgendem Format:
Content-type: multipart/form-data, boundary=AaB03x
--AaB03x
content-disposition: form-data; name="field1"
Joe Blow
--AaB03x
content-disposition: form-data; name="pics"; filename="file1.txt"
Content-Type: text/plain
... contents of file1.txt ...
--AaB03x--
Kopiert aus RFC 1867 - Formularbasierter Dateiupload in HTML
Dieses Format ist für jemanden, der an nette JSON-Daten gewöhnt ist, ziemlich belastend. Daher besteht die Lösung offensichtlich darin, eine JSON-Anfrage zu erstellen und die Datei mit Base64 zu kodieren und am Ende eine Anfrage wie die folgende zu erhalten:
{
"field1":"Joe Blow",
"fileImage":"JVBERi0xLjUKJe..."
}
Und wir können die JSON-Serialisierung und -Deserialisierung überall einsetzen, wo wir möchten. Darüber hinaus ist der Code zum Senden dieser Daten recht einfach. Sie erstellen einfach Ihre Klasse für die JSON-Serialisierung und legen dann die Eigenschaften fest. Die Datei-String-Eigenschaft wird in wenigen einfachen Zeilen festgelegt:
using (FileStream fs = File.Open(file_path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
byte[] file_bytes = new byte[fs.Length];
fs.Read(file_bytes, 0, file_bytes.Length);
MyJsonObj.fileImage = Convert.ToBase64String(file_bytes);
}
Keine albernen Begrenzer und Überschriften mehr für jedes Element. Jetzt ist die verbleibende Frage die Leistung. Also habe ich das profiliert. Ich habe einen Satz von 50 Beispieldateien, die ich über das Kabel senden müsste und die zwischen 50 KB und 1,5 MB liegen. Zuerst habe ich einige Zeilen geschrieben, um die Datei einfach in ein Byte-Array zu streamen, um dies mit der Logik zu vergleichen, die in der Datei streamt und sie dann in einen Base64-Stream konvertiert. Unten sind die 2 Codestücke, die ich profiliert habe:
Direkter Stream zum Profil mehrteiliger / Formulardaten
var timer = new Stopwatch();
timer.Start();
using (FileStream fs = File.Open(file_path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
byte[] test_data = new byte[fs.Length];
fs.Read(test_data, 0, test_data.Length);
}
timer.Stop();
long test = timer.ElapsedMilliseconds;
//Write time elapsed and file size to CSV file
Streaming und Codierung in ein Profil zur Erstellung einer JSON-Anfrage
var timer = new Stopwatch();
timer.Start();
using (FileStream fs = File.Open(file_path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
byte[] file_bytes = new byte[fs.Length];
fs.Read(file_bytes, 0, file_bytes.Length);
ret_file = Convert.ToBase64String(file_bytes);
}
timer.Stop();
long test = timer.ElapsedMilliseconds;
//Write time elapsed, file size, and length of UTF8 encoded ret_file string to CSV file
Das Ergebnis war, dass das einfache Lesen immer 0 ms dauerte, die Base64-Codierung jedoch bis zu 5 ms dauerte. Unten sind die längsten Zeiten:
File Size | Output Stream Size | Time
1352KB 1802KB 5ms
1031KB 1374KB 7ms
463KB 617KB 1ms
In der Produktion würden Sie jedoch niemals einfach blind Multipart- / Formulardaten schreiben, ohne vorher Ihr Trennzeichen zu überprüfen, oder? Deshalb habe ich den Formulardatencode so geändert, dass er die Begrenzungsbytes in der Datei selbst überprüft, um sicherzustellen, dass alles in Ordnung analysiert wird. Ich habe keinen optimierten Scan-Algorithmus geschrieben, deshalb habe ich den Begrenzer nur klein gemacht, damit nicht viel Zeit verschwendet wird.
var timer = new Stopwatch();
timer.Start();
using (FileStream fs = File.Open(file_path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
byte[] test_data = new byte[fs.Length];
fs.Read(test_data, 0, test_data.Length);
string delim = "--DXX";
byte[] delim_checker = Encoding.UTF8.GetBytes(delim);
for (int i = 0; i <= test_data.Length - delim_checker.Length; i++)
{
bool match = true;
for (int j = i; j < i + delim_checker.Length; j++)
{
if (test_data[j] != delim_checker[j - i])
{
match = false;
break;
}
}
if (match)
{
break;
}
}
}
timer.Stop();
long test = timer.ElapsedMilliseconds;
Jetzt zeigen mir die Ergebnisse, dass die Formulardatenmethode tatsächlich deutlich langsamer sein wird. Nachfolgend sind Ergebnisse mit Zeiten> 0 ms für beide Methoden aufgeführt:
File Size | FormData Time | Json/Base64 Time
181Kb 1ms 0ms
1352Kb 13ms 4ms
463Kb 4ms 5ms
133Kb 1ms 0ms
133Kb 1ms 0ms
129Kb 1ms 0ms
284Kb 2ms 1ms
1031Kb 9ms 3ms
Es scheint nicht, dass ein optimierter Algorithmus viel besser wäre, da mein Begrenzer nur 5 Zeichen lang war. Trotzdem nicht dreimal besser, was der Leistungsvorteil einer Base64-Codierung ist, anstatt die Dateibytes auf einen Begrenzer zu überprüfen.
Offensichtlich wird die Base64-Codierung die Größe erhöhen, wie ich in der ersten Tabelle gezeigt habe, aber es ist wirklich nicht so schlimm, selbst mit Unicode-fähigem UTF-8 und würde auf Wunsch gut komprimieren. Aber der eigentliche Vorteil ist, dass mein Code schön und sauber und leicht verständlich ist und es meinen Augen nicht schadet, die JSON-Anforderungsnutzdaten so genau zu betrachten.
Warum um alles in der Welt sollte jemand nicht einfach Dateien in JSON codieren, anstatt Multipart- / Formulardaten zu verwenden? Es gibt Standards, die sich jedoch relativ oft ändern. Normen sind doch wirklich nur Vorschläge, oder?