Công cụ dành cho nhà phát triển · Bộ mã hóa và giải mã Base64
Base64 trong API JSON: tại sao trường nhị phân được mã hóa và chi phí của bạn là bao nhiêu
· Tại sao nó quan trọng
base64 mã hóa
JSON không có loại byte, vì vậy dữ liệu nhị phân thường được mã hóa Base64 thành chuỗi. Bài đăng này giải thích lý do tại sao quy ước đó tồn tại, chi phí về kích thước và CPU cũng như khi nào một điểm cuối nhị phân riêng biệt là lệnh gọi tốt hơn.
Trường PDF chiếm ưu thế trong phản hồi — tải trọng API cụ thể trong đó một đốm màu Base64 vượt trội hơn mọi trường khác
Phản hồi API chứa một đối tượng lớn với một trường duy nhất chiếm ưu thế trong kích thước tải trọng. Phản hồi là JSON, vì vậy mọi giá trị đều là một chuỗi hoặc số. Hầu hết các trường đều nhỏ: ID người dùng, dấu thời gian, mã trạng thái. Một trường chứa imageData hoặc fileContents và là chuỗi 40-kilobyte Base64. Toàn bộ phản hồi là 50 kilobyte. Trường duy nhất đó chiếm 80 phần trăm quá trình truyền, điều này có vẻ lãng phí vì ban đầu máy chủ đã gửi nó dưới dạng byte và cuối cùng máy khách lại cần byte.
Base64 đã giải quyết được vấn đề này: JSON không có loại byte gốc nên dữ liệu nhị phân phải được gói trong một chuỗi. Base64 chuyển đổi byte tùy ý thành ASCII ký tự an toàn trong JSON. Cả máy khách và máy chủ đều phải mã hóa khi gửi và giải mã khi nhận, thêm chi phí CPU. Tải trọng thu được lớn hơn khoảng một phần ba so với byte thô. Bài đăng này giải thích lý do tại sao quy ước đó tồn tại, chi phí trong thực tế là bao nhiêu và khi nào việc phá vỡ ràng buộc JSON bằng một điểm cuối nhị phân riêng biệt là xứng đáng.
Tại sao JSON không thể mang byte thô — chuỗi phải là văn bản Unicode hợp lệ, do đó, các byte tùy ý cần có trình bao bọc văn bản
JSON là định dạng văn bản trong đó tất cả các giá trị phải là văn bản Unicode hợp lệ. Đặc tả xác định chuỗi, số, boolean và null. Nó không có mảng byte hoặc loại bộ đệm. Nếu API cần trả về dữ liệu nhị phân như hình ảnh, chữ ký mật mã hoặc tệp tải lên, thì nó không thể đặt trực tiếp byte thô vào đối tượng JSON. Các byte có thể chứa các ký tự mà trình phân tích cú pháp JSON hiểu là các dấu cấu trúc. Một byte rỗng ở giữa blob nhị phân có thể chấm dứt sớm một chuỗi hoặc phá vỡ trình phân tích cú pháp.
Giải pháp phổ biến là mã hóa dữ liệu nhị phân dưới dạng Base64, tạo ra một chuỗi ASCII ký tự mà trình phân tích cú pháp JSON coi là văn bản thuần túy. Sau đó, máy khách nhận sẽ giải mã Base64 thành byte và sử dụng chúng. Bước mã hóa này diễn ra ở cấp độ API, bị ẩn khỏi hầu hết các nhà phát triển nhưng đó là chi phí thực sự tích lũy khi API trả về nhiều trường nhị phân. Chi phí của Base64 trong JSON cộng dồn trong suốt chu kỳ yêu cầu-phản hồi. Hình phạt về kích thước là hình phạt đầu tiên: Đầu ra Base64 lớn hơn khoảng 33 phần trăm so với đầu vào do chi phí mã hóa.
Chi phí: thêm một phần ba byte, giải mã các bản sao thời gian và bộ nhớ - trong đó mỗi chi phí xuất hiện trong một máy khách thông thường
Tệp video 30 megabyte sẽ trở thành 40 megabyte khi được mã hóa Base64. Việc tải xuống 40 thay vì 30 megabyte sẽ tiêu tốn băng thông và pin trên thiết bị di động cũng như thời gian đối với người dùng sử dụng kết nối chậm. Chi phí thứ hai là CPU thời gian. Máy chủ phải mã hóa dữ liệu nhị phân dưới dạng Base64 trước khi có thể xâu chuỗi dữ liệu đó thành JSON. Máy khách phải phân tích cú pháp JSON rồi giải mã từng trường Base64 trở lại byte. Đối với một phản hồi có nhiều trường nhị phân hoặc một ứng dụng khách xử lý hàng nghìn phản hồi, thời gian CPU đó sẽ tích lũy.
Trên các thiết bị bị hạn chế như điện thoại, các thao tác chuỗi JavaScript và TextDecoding được sử dụng để giải mã Base64 sẽ tiêu tốn pin và làm chậm ứng dụng. Chi phí thứ ba là bộ nhớ: trình phân tích cú pháp JSON tạo một đối tượng chuỗi cho trường Base64, sau đó giải mã sẽ tạo một bản sao khác dưới dạng Uint8Array. Một trường lớn được khởi tạo hai lần trong bộ nhớ trước khi ứng dụng có thể sử dụng nó. Một ví dụ hoạt động làm rõ chi phí. Giả sử điểm cuối API trả về dữ liệu hồ sơ người dùng bao gồm hình ảnh đại diện 100kilobyte. Máy chủ đọc hình ảnh từ đĩa dưới dạng byte, mã hóa nó thành Base64 và đưa nó vào phản hồi JSON.
Ví dụ đã hoạt động: kiểm tra trường Base64 từ phản hồi API — giải mã nó trong trình duyệt để xác nhận những gì máy chủ thực sự đã gửi
Phản hồi JSON hiện có dung lượng khoảng 135 kilobyte (33% chi phí cộng với các trường khác). Ứng dụng khách tải xuống 135 kilobyte thay vì 100. Trong trình duyệt, trình phân tích cú pháp JSON tạo đối tượng chuỗi JavaScript cho dữ liệu Base64.
Khi ứng dụng cần hình ảnh, nó sẽ gọi bộ giải mã Base64 để tạo Uint8Array có 100 kilobyte ban đầu. Trong vài mili giây giải mã, cả hai đối tượng đều tồn tại trong bộ nhớ. Nếu trang hiển thị mười hồ sơ có hình đại diện thì chi phí sẽ tăng lên gấp bội. Cách khác là API trả về phản hồi JSON với một URL riêng biệt cho mỗi tài nguyên hình đại diện, cho phép trình duyệt xử lý việc tải xuống hình ảnh bằng bộ nhớ đệm gốc, hiển thị lũy tiến và quản lý bộ nhớ.
Các lựa chọn thay thế: nhiều phần, URL tải xuống riêng biệt và điểm cuối nhị phân thô - sự cân bằng của mỗi phần
Sự cân bằng giữa việc đưa nhị phân vào JSON và tìm nạp nó riêng biệt tùy thuộc vào mục đích và kiểu sử dụng API. Đối với một trang kết quả tìm kiếm trả về hàng trăm hình ảnh thu nhỏ nhỏ, việc tìm nạp từng hình ảnh dưới dạng một yêu cầu riêng biệt sẽ loại bỏ việc HTTP tổng hợp kết nối và bộ nhớ đệm. Nội tuyến chúng dưới dạng Base64 trong phản hồi JSON có thể nhanh hơn. Đối với một trang hồ sơ chi tiết yêu cầu một hoặc hai hình ảnh có độ phân giải cao, việc tải xuống riêng biệt rõ ràng sẽ tốt hơn. Tài liệu API phải nêu rõ kích thước tối đa của các trường Base64 và thời điểm khách hàng mong đợi các điểm cuối riêng biệt.
Nếu một trường thường xuyên vượt quá một hoặc hai kilobyte thì chiến lược Base64 nội tuyến là dấu hiệu cho thấy thiết kế API cần được xem xét lại. Các lựa chọn thay thế cho Base64 trong JSON tồn tại nhưng mỗi lựa chọn đều có sự đánh đổi. Phản hồi nhiều phần MIME tách biệt nhị phân và văn bản để phần nhị phân được gửi dưới dạng byte thô và chỉ phần văn bản là JSON. Điều này yêu cầu khách hàng phân tích cú pháp một thông báo nhiều phần thay vì chỉ gọi JSON.parse, làm tăng thêm độ phức tạp. Bản tải xuống riêng biệt URL trong phản hồi JSON sẽ hướng khách hàng tìm nạp tài nguyên nhị phân riêng biệt.
Các quy ước đáng nêu trong tài liệu API của bạn — bảng chữ cái, phần đệm và kích thước tối đa theo tiêu chuẩn so với base64url
Điều này hoạt động tốt khi tài nguyên nhị phân lớn hoặc được truy cập ít thường xuyên hơn siêu dữ liệu. Điểm cuối nhị phân thô chỉ trả về byte và loại bỏ hoàn toàn JSON là cách tiếp cận đơn giản nhất nhưng loại bỏ cấu trúc mà JSON cung cấp. Một số API trả về dữ liệu nhị phân đã nén và mã hóa Base64, giúp giảm hình phạt về kích thước nhưng lại tăng thêm chi phí giải nén. Lựa chọn tùy thuộc vào mức sử dụng dự kiến: các trường nhỏ nằm trong dòng, các trường lớn nằm trong các tài nguyên riêng biệt và dữ liệu có cấu trúc đáng được lưu giữ trong JSON ngay cả với chi phí Base64.
Các quy ước rất quan trọng đối với khả năng tương tác. Các API mà dữ liệu nhị phân mã hóa Base64 phải ghi lại dữ liệu đó một cách rõ ràng và nêu rõ bảng chữ cái là tiêu chuẩn hay URL an toàn. Base64 tiêu chuẩn sử dụng + và /, an toàn trong chuỗi JSON nhưng không an toàn trong URL. URL-safe Base64 thay thế chúng bằng - và _, phù hợp với dữ liệu: URI nhưng được thoát một cách không cần thiết trong JSON. Tài liệu phải chỉ rõ liệu phần đệm được bao gồm hay bị bỏ qua, vì cả hai đều là Base64 hợp lệ nhưng ứng dụng khách dự kiến có phần đệm và nhận dữ liệu không có phần đệm sẽ không hoạt động âm thầm hoặc tạo ra rác.
Điều này không bao gồm những gì — protobuf, CBOR và các định dạng tuần tự hóa nhị phân khác
Đối với các trường rất lớn hoặc được cập nhật thường xuyên, việc ghi lại điểm cuối nhị phân riêng biệt là điều cần thiết để khách hàng không cố tìm nạp hàng kilobyte dữ liệu không cần thiết. Việc gỡ lỗi API với các trường Base64 rất đơn giản với công cụ phù hợp. Bộ mã hóa và giải mã Base64 cho phép bạn giải mã cục bộ bất kỳ trường nào trong trình duyệt mà không cần lưu trữ hoặc gửi đi bất cứ đâu. Sao chép trường Base64 từ phản hồi JSON, dán trường đó vào bộ giải mã và nhấn Giải mã. Đối với dữ liệu dạng văn bản (ví dụ: JSON bên trong Base64), đầu ra được giải mã sẽ xuất hiện ngay lập tức.
Đối với dữ liệu nhị phân như hình ảnh, chế độ xem hex hiển thị cho bạn các byte. Điều này giúp xác nhận rằng máy chủ đã gửi những gì bạn mong đợi và bộ giải mã máy khách của bạn đang hoạt động chính xác. Nếu một trường giải mã thành dữ liệu không mong muốn thì vấn đề nằm ở mã hóa máy chủ hoặc cách bạn sao chép trường đó. Nếu nó giải mã thành một phần blob thì trường có thể đã bị cắt bớt hoặc độ dài Base64 có thể sai. Giải mã cục bộ tăng tốc độ gỡ lỗi so với việc ghi trường vào tệp và mở các công cụ bên ngoài.
Bài học rút ra: Base64 trong JSON là một sự thỏa hiệp, vì vậy hãy ghi lại điều đó — cách bộ mã hóa & giải mã Base64 giúp bạn kiểm tra và xác minh cục bộ các trường được mã hóa
Cách tiếp cận thực tế đối với Base64 trong API là nhận thức hơn là né tránh. Base64 là cách tiêu chuẩn để mang dữ liệu nhị phân trong JSON và nó hoạt động. Hãy hiểu rằng mỗi trường Base64 có kích thước lớn hơn một phần ba và mất vài mili giây CPU thời gian cho mỗi chu kỳ yêu cầu-phản hồi. Đối với siêu dữ liệu quan trọng nhỏ như mã thông báo xác thực (trong đó JWT được mã hóa Base64), chi phí là không đáng kể. Đối với các tệp đính kèm lớn, hãy đặt câu hỏi liệu tệp nhị phân sẽ di chuyển trong cùng một phản hồi hay dưới dạng một tài nguyên riêng biệt.
Ghi lại sơ đồ mã hóa và kích thước tối đa trong thông số API của bạn. Khi kiểm tra phản hồi, hãy sử dụng bộ mã hóa và giải mã Base64 để xác minh các trường giải mã chính xác và để hiểu những gì máy chủ thực sự đã gửi. Kỷ luật đó giữ cho sự đánh đổi có thể nhìn thấy được và quyết định có chủ ý chứ không phải ngẫu nhiên.