Saya bekerja di C # dan melakukan komunikasi antara 2 aplikasi yang saya tulis. Saya menyukai API Web dan JSON. Sekarang saya pada titik di mana saya menulis rutin untuk mengirim catatan antara dua server yang mencakup beberapa data teks dan file.
Menurut internet saya seharusnya menggunakan permintaan multipart / form-data seperti yang ditunjukkan di sini:
SO Pertanyaan "Formulir multi-bagian dari C # client"
Pada dasarnya Anda menulis permintaan secara manual yang mengikuti format seperti ini:
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--
Disalin dari RFC 1867 - Unggahan File Berbasis Formulir dalam HTML
Format ini cukup menyedihkan bagi seseorang yang terbiasa dengan data JSON yang bagus. Jadi jelas solusinya adalah membuat permintaan JSON dan Base64 menyandikan file dan berakhir dengan permintaan seperti ini:
{
"field1":"Joe Blow",
"fileImage":"JVBERi0xLjUKJe..."
}
Dan kita dapat menggunakan serialisasi dan deserialisasi JSON di mana saja kita mau. Selain itu, kode untuk mengirim data ini cukup sederhana. Anda cukup membuat kelas Anda untuk serialisasi JSON dan kemudian mengatur properti. Properti string file diatur dalam beberapa baris sepele:
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);
}
Tidak ada lagi pembatas dan header konyol untuk setiap item. Sekarang pertanyaan yang tersisa adalah kinerja. Jadi saya membuat profil itu. Saya memiliki satu set 50 file sampel yang saya perlukan untuk dikirim melalui kabel yang berkisar dari 50KB hingga 1,5MB atau lebih. Pertama saya menulis beberapa baris untuk melakukan streaming dalam file ke array byte untuk membandingkannya dengan logika yang stream dalam file dan kemudian mengubahnya menjadi stream Base64. Di bawah ini adalah 2 potongan kode yang saya profil:
Aliran Langsung ke Profil multipart / formulir-data
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 dan Enkode ke profil yang membuat permintaan JSON
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
Hasilnya adalah bahwa pembacaan sederhana selalu mengambil 0ms, tetapi bahwa pengkodean Base64 memakan waktu hingga 5ms. Di bawah ini adalah waktu terlama:
File Size | Output Stream Size | Time
1352KB 1802KB 5ms
1031KB 1374KB 7ms
463KB 617KB 1ms
Namun, dalam produksi Anda tidak akan pernah secara buta menulis data multi-formulir / formulir tanpa terlebih dahulu memeriksa pembatas Anda, bukan? Jadi saya memodifikasi kode form-data sehingga memeriksa byte pembatas dalam file itu sendiri untuk memastikan semuanya akan diuraikan ok. Saya tidak menulis algoritma pemindaian yang dioptimalkan, jadi saya hanya membuat pembatas kecil sehingga tidak akan membuang banyak waktu.
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;
Sekarang hasilnya menunjukkan kepada saya bahwa metode form-data akan benar-benar lebih lambat. Di bawah ini adalah hasil dengan waktu> 0 ms untuk kedua metode:
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
Tampaknya tidak ada algoritma yang dioptimalkan akan jauh lebih baik melihat karena pembatas saya hanya 5 karakter. Tidak 3x lebih baik lagi, yang merupakan keuntungan kinerja melakukan pengkodean Base64 daripada memeriksa byte file untuk pembatas.
Jelas pengkodean Base64 akan mengembang ukurannya seperti yang saya tunjukkan di tabel pertama, tapi itu benar-benar tidak buruk bahkan dengan Unicode mampu UTF-8 dan akan memampatkan dengan baik jika diinginkan. Tetapi manfaat sebenarnya adalah kode saya bagus dan bersih dan mudah dimengerti dan tidak ada salahnya untuk melihat beban permintaan JSON sebanyak itu.
Jadi mengapa ada orang yang tidak hanya Base64 menyandikan file di JSON daripada menggunakan multipart / form-data? Ada Standar, tetapi ini sering berubah relatif. Standar benar-benar hanya saran, kan?