Neden karışık veri ve dosya aktarımı için çoklu bölüm / form verisi kullanılır?


14

C # ile çalışıyorum ve yazdığım 2 uygulama arasında iletişim kuruyorum. Web API ve JSON'u beğenmeye geldim. Şimdi, bazı metin verilerini ve bir dosyayı içeren iki sunucu arasında bir kayıt göndermek için bir rutin yazdığım noktadayım.

İnternete göre burada gösterildiği gibi çok parçalı / form-veri talebi kullanmam gerekiyor:

SO Question "C # istemcisinden çok parçalı formlar"

Temel olarak, aşağıdaki gibi bir biçimi izleyen bir isteği manuel olarak yazarsınız:

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--

RFC 1867'den kopyalandı - HTML'de Form Tabanlı Dosya Yükleme

Bu biçim, JSON verilerini güzelleştirmek için kullanılan birine oldukça üzücü. Açıkçası çözüm bir JSON isteği oluşturmak ve Base64 dosyayı kodlamak ve bunun gibi bir istekle sonuçlanmaktır:

{
    "field1":"Joe Blow",
    "fileImage":"JVBERi0xLjUKJe..."
}

Ve istediğimiz her yerde JSON serileştirme ve serileştirmeyi kullanabiliriz. Bunun da ötesinde, bu verileri gönderme kodu oldukça basittir. Sadece JSON serileştirme için sınıfınızı oluşturun ve sonra özellikleri ayarlayın. File string özelliği birkaç önemsiz satırda ayarlanır:

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);
}

Artık her öğe için aptal sınırlayıcı ve başlık yok. Şimdi geriye kalan soru performans. Ben de bunu profilledim. Ben 50KB ile 1.5MB ya da öylesine arasında değişen tel üzerinden göndermek gerekir 50 örnek dosyaları bir dizi var. Öncelikle, dosyadaki akışları mantıkla karşılaştırmak ve daha sonra Base64 akışına dönüştürmek için dosyada bir bayt dizisine akış yapmak için bazı satırlar yazdım. Aşağıda profilli olduğum 2 kod parçası bulunmaktadır:

Profile Çok Parçalı / Form Verilerine Doğrudan Akış

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

JSON isteği oluşturan profile akış ve kodlama

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

Sonuçlar, basit okumanın her zaman 0 ms sürdüğü, ancak Base64 kodlamasının 5 ms sürdüğü idi. En uzun zamanlar aşağıdadır:

File Size  |  Output Stream Size  |  Time
1352KB        1802KB                 5ms
1031KB        1374KB                 7ms
463KB         617KB                  1ms

Bununla birlikte, üretimde, önce sınırlayıcınızı kontrol etmeden asla çok yönlü / form verilerini körü körüne yazmazsınız değil mi? Böylece form-veri kodunu değiştirdim, böylece her şeyin yolunda ayrıştırıldığından emin olmak için dosyanın kendisindeki sınırlayıcı baytları kontrol etti. Optimize edilmiş bir tarama algoritması yazmadım, bu yüzden sınırlayıcıyı küçük yaptım, böylece çok fazla zaman kaybetmeyecekti.

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;

Şimdi sonuçlar bana form-veri yönteminin önemli ölçüde daha yavaş olacağını gösteriyor. Aşağıda, her iki yöntem için de 0ms'den büyük sonuçlar verilmiştir:

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

Optimize edilmiş bir algoritmanın, sınırlayıcımın sadece 5 karakter uzunluğunda olduğunu görerek çok daha iyi olacağı görülmüyor. Yine de 3 kat daha iyi değil, bu da dosya baytlarını bir sınırlayıcı için kontrol etmek yerine Base64 kodlaması yapmanın performans avantajı.

Tabii ki Base64 kodlaması, ilk tabloda gösterdiğim gibi boyutu şişirecek, ancak Unicode özellikli UTF-8 ile bile o kadar da kötü değil ve istenirse iyi sıkıştırır. Ama asıl fayda benim kod güzel ve temiz ve kolay anlaşılır ve JSON istek yükü o kadar bakmak için benim gözbebekleri zarar vermez.

Öyleyse neden dünyadaki Base64, çoklu bölüm / form verileri kullanmak yerine JSON'daki dosyaları kodlamıyor? Standartlar var, ancak bunlar nispeten sık değişiyor. Standartlar gerçekten sadece öneri değil mi?

Yanıtlar:


16

multipart/form-dataHTML formları için oluşturulmuş bir yapıdır. Olumlu multipart/form-dataolduğunu fark ettiğiniz gibi, aktarım boyutu aktarılan nesnenin boyutuna daha yakındır - burada nesnenin bir metin kodlamasında boyut önemli ölçüde şişirilir. Protokol icat edildiğinde internet bant genişliğinin CPU döngülerinden daha değerli bir ürün olduğunu anlayabilirsiniz.

İnternete göre çok parçalı / form verisi talebi kullanmam gerekiyor

multipart/form-datatüm tarayıcılar tarafından desteklendiği için tarayıcı yüklemeleri için en iyi protokoldür. Sunucudan sunucuya iletişim için kullanmak için bir neden yoktur. Sunucudan sunucuya iletişim genellikle form tabanlı değildir. İletişim nesneleri daha karmaşıktır ve yuvalama ve türler gerektirir - JSON'un iyi işlediği gereksinimler. Base64 kodlaması, hangi nesneyi seçerseniz seçin, ikili nesneleri aktarmak için basit bir çözümdür. CBOR veya BSON gibi ikili protokoller, Base64'ten daha küçük nesnelere serileştirildiklerinden ve daha iyisi, mevcut bir JSON iletişiminin kolay bir uzantısı olması gereken JSON'a yeterince yakın oldukları için daha da iyidir. CPU performansına karşı Base64'ten emin değilim.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.