Tại sao mọi người sẽ sử dụng nhiều dữ liệu / biểu mẫu cho dữ liệu hỗn hợp và truyền tệp?


14

Tôi đang làm việc trong C # và thực hiện một số giao tiếp giữa 2 ứng dụng tôi đang viết. Tôi đã thích API Web và JSON. Bây giờ tôi đang ở thời điểm mà tôi đang viết một thói quen để gửi một bản ghi giữa hai máy chủ bao gồm một số dữ liệu văn bản và một tệp.

Theo internet, tôi phải sử dụng một yêu cầu nhiều dữ liệu / biểu mẫu dữ liệu như được hiển thị ở đây:

Câu hỏi SO "Biểu mẫu nhiều từ khách hàng C #"

Về cơ bản, bạn viết một yêu cầu theo cách thủ công theo một định dạng như vậy:

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

Sao chép từ RFC 1867 - Tải lên tệp dựa trên mẫu trong HTML

Định dạng này khá khó chịu với một người đã quen với dữ liệu JSON đẹp. Vì vậy, rõ ràng giải pháp là tạo một yêu cầu JSON và Base64 mã hóa tệp và kết thúc bằng một yêu cầu như thế này:

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

Và chúng ta có thể sử dụng tuần tự hóa và giải tuần tự hóa JSON ở bất cứ đâu chúng ta muốn. Trên hết, mã để gửi dữ liệu này khá đơn giản. Bạn chỉ cần tạo lớp của mình để tuần tự hóa JSON và sau đó đặt các thuộc tính. Thuộc tính chuỗi tệp được đặt trong một vài dòng tầm thường:

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

Không còn phân định ngớ ngẩn và tiêu đề cho mỗi mục. Bây giờ câu hỏi còn lại là hiệu suất. Vì vậy, tôi định hình rằng. Tôi có một bộ 50 tệp mẫu mà tôi sẽ cần gửi qua dây có phạm vi từ 50KB đến 1,5 MB hoặc hơn. Đầu tiên tôi đã viết một số dòng chỉ đơn giản là truyền phát tệp trong một mảng byte để so sánh nó với logic truyền phát trong tệp và sau đó chuyển đổi nó thành luồng Base64. Dưới đây là 2 đoạn mã mà tôi đã mô tả:

Truyền trực tiếp đến nhiều phần dữ liệu / biểu mẫu

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

Truyền phát và mã hóa vào hồ sơ tạo yêu cầu 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

Kết quả là việc đọc đơn giản luôn mất 0ms, nhưng mã hóa Base64 mất tới 5ms. Dưới đây là thời gian dài nhất:

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

Tuy nhiên, trong sản xuất, bạn sẽ không bao giờ chỉ viết một cách mù quáng nhiều dữ liệu / biểu mẫu mà không kiểm tra trước dấu phân cách của bạn phải không? Vì vậy, tôi đã sửa đổi mã dữ liệu biểu mẫu để nó kiểm tra các byte phân cách trong chính tệp để đảm bảo mọi thứ sẽ được phân tích cú pháp ok. Tôi đã không viết một thuật toán quét tối ưu hóa, vì vậy tôi chỉ làm cho dấu phân cách nhỏ để nó không lãng phí nhiều thời gian.

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;

Bây giờ kết quả đang cho tôi thấy rằng phương pháp dữ liệu biểu mẫu sẽ thực sự chậm hơn đáng kể. Dưới đây là kết quả với thời gian> 0ms cho một trong hai phương pháp:

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

Dường như một thuật toán tối ưu hóa sẽ làm tốt hơn nhiều khi nhìn thấy vì dấu phân cách của tôi chỉ dài 5 ký tự. Dù sao cũng không phải là 3x tốt hơn, đó là lợi thế về hiệu suất của việc thực hiện mã hóa Base64 thay vì kiểm tra các byte tệp để phân cách.

Rõ ràng mã hóa Base64 sẽ tăng kích thước như tôi hiển thị trong bảng đầu tiên, nhưng nó thực sự không tệ ngay cả với UTF-8 có khả năng Unicode và sẽ nén tốt nếu muốn. Nhưng lợi ích thực sự là mã của tôi rất đẹp, sạch sẽ và dễ hiểu và nó không làm tổn thương nhãn cầu của tôi khi nhìn vào tải trọng yêu cầu JSON.

Vậy tại sao mọi người không chỉ đơn giản là mã hóa các tệp Base64 trong JSON thay vì sử dụng nhiều dữ liệu / biểu mẫu? Có các Tiêu chuẩn, nhưng chúng thay đổi tương đối thường xuyên. Tiêu chuẩn thực sự chỉ là đề xuất dù sao phải không?

Câu trả lời:


15

multipart/form-datalà một cấu trúc được tạo cho các biểu mẫu HTML. Như bạn đã phát hiện ra điểm tích cực của multipart/form-datalà kích thước chuyển gần với kích thước của đối tượng được truyền - trong đó trong mã hóa văn bản của đối tượng, kích thước được tăng lên đáng kể. Bạn có thể hiểu rằng băng thông internet là một mặt hàng có giá trị hơn chu kỳ CPU khi giao thức được phát minh.

Theo internet, tôi phải sử dụng một yêu cầu nhiều dữ liệu / biểu mẫu

multipart/form-datalà giao thức tốt nhất để tải lên trình duyệt vì nó được hỗ trợ bởi tất cả các trình duyệt. Không có lý do để sử dụng nó cho giao tiếp giữa các máy chủ. Giao tiếp giữa máy chủ với máy chủ thường không dựa trên hình thức. Các đối tượng giao tiếp phức tạp hơn và yêu cầu lồng và kiểu - các yêu cầu mà JSON xử lý tốt. Mã hóa Base64 là một giải pháp đơn giản để chuyển các đối tượng nhị phân theo bất kỳ định dạng tuần tự hóa nào bạn chọn. Các giao thức nhị phân như CBOR hoặc BSON thậm chí còn tốt hơn vì chúng tuần tự hóa thành các đối tượng nhỏ hơn Base64 và chúng đủ gần với JSON để nó (nên) dễ dàng mở rộng cho giao tiếp JSON hiện có. Không chắc chắn về hiệu suất CPU so với Base64.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.