ไทย

เครื่องมือสำหรับนักพัฒนา · ตัวเข้ารหัสและตัวถอดรหัส Base64

Base64 ใน JSON API: เหตุใดเขตข้อมูลไบนารีจึงได้รับการเข้ารหัสและคุณต้องเสียค่าใช้จ่ายเท่าไร

· เหตุใดจึงสำคัญ

base64 การเข้ารหัส

ฟิลด์ JSON ที่มีสตริง Base64 แบบยาวซึ่งแสดงถึงข้อมูลไบนารีที่เข้ารหัสสำหรับการขนส่ง
ภาพประกอบเวกเตอร์ต้นฉบับ ToolAcre

JSON ไม่มีประเภทไบต์ ดังนั้นข้อมูลไบนารี่จึงมักจะเข้ารหัส Base64 ลงในสตริง โพสต์นี้จะอธิบายว่าทำไมจึงมีแบบแผนนั้น ขนาดและ CPU มีราคาเท่าใด และเมื่อใดที่ปลายทางไบนารีที่แยกจากกันเป็นการเรียกที่ดีกว่า

ฟิลด์ PDF ที่ครอบงำการตอบสนอง - เพย์โหลด API ที่เป็นรูปธรรมโดยที่ Base64 blob หนึ่งตัวมีน้ำหนักมากกว่าอย่างอื่นทั้งหมด

การตอบสนอง API มีออบเจ็กต์ขนาดใหญ่ที่มีฟิลด์เดียวซึ่งครอบงำขนาดของเพย์โหลด การตอบกลับคือ JSON ดังนั้นทุกค่าจึงเป็นสตริงหรือตัวเลข ฟิลด์ส่วนใหญ่มีขนาดเล็ก: ID ผู้ใช้, การประทับเวลา, รหัสสถานะ ฟิลด์หนึ่งประกอบด้วย imageData หรือ fileContents และเป็นสตริง Base64 ขนาด 40-kilobyte การตอบกลับทั้งหมดคือ 50 กิโลไบต์ ฟิลด์เดียวนั้นคิดเป็น 80 เปอร์เซ็นต์ของการถ่ายโอน ซึ่งดูเหมือนว่าจะสิ้นเปลืองเนื่องจากเซิร์ฟเวอร์ส่งเป็นไบต์ในตอนแรก และไคลเอ็นต์ต้องการไบต์อีกครั้งในที่สุด

Base64 แก้ไขปัญหานี้: JSON ไม่มีประเภทไบต์ดั้งเดิม ดังนั้นข้อมูลไบนารี่จึงต้องรวมเป็นสตริง Base64 แปลงไบต์ที่กำหนดเองเป็นอักขระ ASCII ที่ปลอดภัยใน JSON ทั้งไคลเอ็นต์และเซิร์ฟเวอร์ต้องเข้ารหัสเมื่อส่งและถอดรหัสเมื่อรับ โดยเพิ่มโอเวอร์เฮด CPU เพย์โหลดผลลัพธ์จะมีขนาดใหญ่กว่าไบต์ดิบประมาณหนึ่งในสาม โพสต์นี้จะอธิบายว่าทำไมแบบแผนนั้นถึงมีอยู่ ค่าใช้จ่ายในทางปฏิบัติ และเมื่อทำลายข้อจำกัด JSON ด้วยปลายทางไบนารีที่แยกจากกันก็คุ้มค่า

เหตุใด JSON ไม่สามารถพกพาไบต์ดิบได้ - สตริงต้องเป็นข้อความ Unicode ที่ถูกต้อง ดังนั้นไบต์ที่กำหนดเองจึงจำเป็นต้องมีตัวตัดข้อความ

JSON คือรูปแบบข้อความที่ค่าทั้งหมดต้องเป็นข้อความ Unicode ที่ถูกต้อง ข้อกำหนดนี้กำหนดสตริง ตัวเลข บูลีน และค่าว่าง ไม่มีอาร์เรย์ไบต์หรือประเภทบัฟเฟอร์ หาก API จำเป็นต้องส่งคืนข้อมูลไบนารี่ เช่น รูปภาพ ลายเซ็นเข้ารหัส หรือการอัปโหลดไฟล์ ก็จะไม่สามารถใส่ไบต์ดิบลงในอ็อบเจ็กต์ JSON ได้โดยตรง ไบต์อาจมีอักขระที่ตัวแยกวิเคราะห์ JSON ตีความว่าเป็นเครื่องหมายโครงสร้าง ไบต์ว่างที่อยู่ตรงกลางของไบนารีหยดอาจยุติสตริงก่อนกำหนดหรือทำให้พาร์เซอร์เสียหาย

วิธีแก้ไขทั่วไปคือการเข้ารหัสข้อมูลไบนารีเป็น Base64 โดยสร้างสตริงอักขระ ASCII ที่ตัวแยกวิเคราะห์ JSON ถือเป็นข้อความธรรมดา ไคลเอ็นต์ที่รับจะถอดรหัส Base64 กลับเป็นไบต์และใช้งาน ขั้นตอนการเข้ารหัสนี้เกิดขึ้นที่ระดับ API ซึ่งซ่อนไว้จากนักพัฒนาส่วนใหญ่ แต่เป็นต้นทุนจริงที่จะสะสมเมื่อ API ส่งคืนฟิลด์ไบนารีจำนวนมาก ต้นทุนของ Base64 ใน JSON ทบต้นตลอดวงจรการตอบกลับคำขอ การปรับขนาดเป็นครั้งแรก: เอาต์พุต Base64 มีขนาดใหญ่กว่าอินพุตประมาณ 33 เปอร์เซ็นต์ เนื่องจากค่าใช้จ่ายในการเข้ารหัส

ค่าใช้จ่าย: เพิ่มอีกหนึ่งในสามไบต์ เวลาในการถอดรหัสและสำเนาหน่วยความจำ — โดยแต่ละค่าใช้จ่ายจะปรากฏในไคลเอนต์ทั่วไป

ไฟล์วิดีโอ 30 เมกะไบต์จะกลายเป็น 40 เมกะไบต์เมื่อเข้ารหัส Base64 การดาวน์โหลด 40 แทนที่จะเป็น 30 เมกะไบต์จะทำให้ใช้แบนด์วิดท์และแบตเตอรี่บนอุปกรณ์เคลื่อนที่ และเวลาสำหรับผู้ใช้ที่มีการเชื่อมต่อที่ช้า ราคาที่สองคือ CPU ครั้ง เซิร์ฟเวอร์จะต้องเข้ารหัสข้อมูลไบนารี่เป็น Base64 ก่อนจึงจะสามารถสตริงลงใน JSON ได้ ไคลเอนต์จะต้องแยกวิเคราะห์ JSON แล้วถอดรหัสแต่ละฟิลด์ Base64 กลับเป็นไบต์ สำหรับการตอบกลับที่มีช่องไบนารีหลายช่องหรือไคลเอนต์ที่ประมวลผลการตอบกลับนับพันครั้ง เวลา CPU จะสะสม

บนอุปกรณ์ที่มีข้อจำกัด เช่น โทรศัพท์ การดำเนินการสตริง JavaScript และ TextDecoder ที่ใช้สำหรับการถอดรหัส Base64 จะใช้แบตเตอรี่และทำให้แอปพลิเคชันช้าลง ราคาที่สามคือหน่วยความจำ: ตัวแยกวิเคราะห์ JSON สร้างวัตถุสตริงสำหรับฟิลด์ Base64 จากนั้นการถอดรหัสจะสร้างสำเนาอื่นเป็น Uint8Array ฟิลด์ขนาดใหญ่จะถูกสร้างอินสแตนซ์สองครั้งในหน่วยความจำก่อนที่แอปพลิเคชันจะสามารถใช้งานได้ ตัวอย่างการทำงานจะชี้แจงต้นทุน สมมติว่าปลายทาง API ส่งคืนข้อมูลโปรไฟล์ผู้ใช้ รวมถึงรูปภาพอวตาร 100-กิโลไบต์ เซิร์ฟเวอร์อ่านอิมเมจจากดิสก์เป็นไบต์ เข้ารหัสเป็น Base64 และรวมไว้ในการตอบสนอง JSON

ตัวอย่างการทำงาน: การตรวจสอบฟิลด์ Base64 จากการตอบกลับ API — ถอดรหัสในเบราว์เซอร์เพื่อยืนยันสิ่งที่เซิร์ฟเวอร์ส่งจริง

ขณะนี้การตอบกลับ JSON มีขนาดประมาณ 135 กิโลไบต์ (33% โอเวอร์เฮดบวกกับฟิลด์อื่นๆ) ไคลเอนต์ดาวน์โหลด 135 กิโลไบต์ แทนที่จะเป็น 100 ในเบราว์เซอร์ ตัวแยกวิเคราะห์ JSON จะสร้างวัตถุสตริง JavaScript สำหรับข้อมูล Base64

เมื่อแอปพลิเคชันต้องการอิมเมจ มันจะเรียกตัวถอดรหัส Base64 ซึ่งสร้าง Uint8Array ของ 100 กิโลไบต์ดั้งเดิม สำหรับการถอดรหัสไม่กี่วินาที วัตถุทั้งสองจะมีอยู่ในหน่วยความจำ หากหน้านี้แสดงโปรไฟล์พร้อมอวตาร 10 รายการ ค่าใช้จ่ายจะทวีคูณ อีกทางเลือกหนึ่งคือให้ API ส่งคืนการตอบสนอง JSON ด้วย URL แยกต่างหากสำหรับทรัพยากรอวาตาร์แต่ละรายการ โดยปล่อยให้เบราว์เซอร์จัดการการดาวน์โหลดรูปภาพด้วยแคชดั้งเดิม การเรนเดอร์แบบโปรเกรสซีฟ และการจัดการหน่วยความจำ

ทางเลือก: URL ดาวน์โหลดแบบหลายส่วนและแยกกัน และจุดปลายไบนารีแบบ Raw — ข้อเสียของแต่ละรายการ

การแลกเปลี่ยนระหว่างการรวมไบนารีใน JSON และการดึงข้อมูลแยกกันนั้นขึ้นอยู่กับวัตถุประสงค์ API และรูปแบบการใช้งาน สำหรับหน้าผลการค้นหาที่แสดงภาพขนาดย่อเล็กๆ หลายร้อยภาพ การดึงข้อมูลแต่ละภาพเป็นคำขอแยกกัน จะเอาชนะการรวมการเชื่อมต่อและการแคช HTTP การอินไลน์เป็น Base64 ในการตอบกลับ JSON อาจจะเร็วกว่า สำหรับหน้าโปรไฟล์โดยละเอียดที่ขอรูปภาพความละเอียดสูงหนึ่งหรือสองภาพ การดาวน์โหลดแบบแยกกันจะดีกว่าอย่างเห็นได้ชัด เอกสาร API ควรระบุขนาดสูงสุดของฟิลด์ Base64 และเวลาที่ไคลเอ็นต์ควรคาดหวังให้มีปลายทางแยกกัน

หากฟิลด์มีขนาดเกินหนึ่งหรือสองไบต์เป็นประจำ กลยุทธ์ inline-Base64 ถือเป็นสัญญาณว่าการออกแบบ API จำเป็นต้องพิจารณาใหม่ มีทางเลือกอื่นสำหรับ Base64 ใน JSON อยู่ แต่แต่ละทางเลือกก็มีข้อดีข้อเสีย การตอบกลับแบบหลายส่วน MIME แยกไบนารีและข้อความ ดังนั้นส่วนไบนารีจะถูกส่งเป็นไบต์ดิบ และมีเพียงส่วนข้อความเท่านั้นที่เป็น JSON สิ่งนี้กำหนดให้ไคลเอนต์แยกวิเคราะห์ข้อความที่มีหลายส่วนแทนที่จะเรียก JSON.parse เพื่อเพิ่มความซับซ้อน การดาวน์โหลด URL แยกต่างหากในการตอบกลับ JSON ชี้ให้ลูกค้าดึงทรัพยากรไบนารีแยกกัน

แบบแผนที่ควรระบุไว้ในเอกสาร API ของคุณ — ตัวอักษรมาตรฐานเทียบกับ base64url, ช่องว่างภายใน และขนาดสูงสุด

วิธีนี้ใช้ได้ผลดีเมื่อทรัพยากรไบนารีมีขนาดใหญ่หรือมีการเข้าถึงน้อยกว่าข้อมูลเมตา จุดสิ้นสุดไบนารีดิบที่ส่งคืนเฉพาะไบต์และละทิ้ง JSON ทั้งหมดเป็นวิธีที่ง่ายที่สุด แต่จะลบโครงสร้างที่ JSON จัดเตรียมไว้ออก API บางตัวส่งคืนข้อมูลไบนารีที่ถูกบีบอัดและเข้ารหัส Base64 ซึ่งจะลดขนาดลงแต่เพิ่มค่าใช้จ่ายในการคลายการบีบอัด ตัวเลือกขึ้นอยู่กับการใช้งานที่คาดไว้: ช่องขนาดเล็กอยู่ในบรรทัดที่ดี ช่องขนาดใหญ่อยู่ในทรัพยากรที่แยกจากกัน และข้อมูลที่มีโครงสร้างคุ้มค่าที่จะเก็บไว้ใน JSON แม้ว่าจะมีต้นทุน Base64 ก็ตาม

อนุสัญญามีความสำคัญต่อการทำงานร่วมกัน API ที่ข้อมูลไบนารีที่เข้ารหัส Base64 ควรบันทึกไว้อย่างชัดเจนและระบุว่าตัวอักษรเป็นแบบมาตรฐานหรือ URL-safe Standard Base64 ใช้ + และ /, ซึ่งปลอดภัยในสตริง JSON แต่ไม่อยู่ใน URL URL-safe Base64 แทนที่ด้วย - และ _ ซึ่งเหมาะสำหรับข้อมูล: URIs แต่หลบหนีโดยไม่จำเป็นใน JSON เอกสารประกอบควรระบุว่ามีการรวมหรือละเว้นช่องว่าง เนื่องจากทั้งสองอย่างเป็น Base64 ที่ถูกต้อง แต่ไคลเอ็นต์ที่คาดหวังการเติมและรับข้อมูลที่ไม่มีการเติมจะล้มเหลวโดยไม่โต้ตอบหรือสร้างขยะ

สิ่งนี้ไม่ครอบคลุม — protobuf, CBOR และรูปแบบการทำให้เป็นอนุกรมไบนารีอื่นๆ

สำหรับฟิลด์ที่มีขนาดใหญ่มากหรืออัปเดตบ่อยครั้ง การบันทึกจุดสิ้นสุดไบนารีที่แยกจากกันถือเป็นสิ่งสำคัญ เพื่อให้ไคลเอ็นต์ไม่พยายามดึงข้อมูลที่ไม่จำเป็นจำนวนกิโลไบต์ การดีบัก API ด้วยฟิลด์ Base64 นั้นตรงไปตรงมาด้วยเครื่องมือที่เหมาะสม ตัวเข้ารหัสและตัวถอดรหัส Base64 ช่วยให้คุณสามารถถอดรหัสฟิลด์ใดก็ได้ภายในเบราว์เซอร์ โดยไม่ต้องจัดเก็บหรือส่งไปที่ใดก็ได้ คัดลอกฟิลด์ Base64 จากการตอบกลับ JSON วางลงในตัวถอดรหัสแล้วกด Decode สำหรับข้อมูลที่มีลักษณะเป็นข้อความ (เช่น JSON ภายใน Base64) เอาต์พุตที่ถอดรหัสจะปรากฏขึ้นทันที

สำหรับข้อมูลไบนารี เช่น รูปภาพ มุมมองฐานสิบหกจะแสดงไบต์ ซึ่งจะช่วยยืนยันว่าเซิร์ฟเวอร์ส่งสิ่งที่คุณคาดหวังและตัวถอดรหัสไคลเอนต์ของคุณทำงานอย่างถูกต้อง หากฟิลด์ถอดรหัสเป็นข้อมูลที่ไม่คาดคิด ปัญหาอยู่ที่การเข้ารหัสเซิร์ฟเวอร์หรือวิธีคัดลอกฟิลด์ หากถอดรหัสเป็นหยดบางส่วน ฟิลด์อาจถูกตัดทอนหรือความยาว Base64 อาจผิด การถอดรหัสในเครื่องช่วยเร่งการดีบักเมื่อเปรียบเทียบกับการเขียนฟิลด์ลงในไฟล์และการเปิดเครื่องมือภายนอก

ประเด็นสำคัญ: Base64 ใน JSON เป็นการประนีประนอม ดังนั้นโปรดบันทึกไว้ — ตัวเข้ารหัสและตัวถอดรหัส Base64 ช่วยคุณตรวจสอบและตรวจสอบฟิลด์ที่เข้ารหัสในเครื่องได้อย่างไร

แนวทางการปฏิบัติกับ Base64 ใน API คือการรับรู้มากกว่าการหลีกเลี่ยง Base64 เป็นวิธีมาตรฐานในการส่งข้อมูลไบนารี่ใน JSON และใช้งานได้ ทำความเข้าใจว่าแต่ละฟิลด์ Base64 มีค่าใช้จ่ายเพิ่มขึ้นสามในสามและใช้เวลาสองสามมิลลิวินาทีของ CPU ต่อรอบการตอบกลับคำขอ สำหรับข้อมูลเมตาที่สำคัญขนาดเล็ก เช่น โทเค็นการตรวจสอบสิทธิ์ (โดยที่ JWT นั้นมีการเข้ารหัส Base64) จะมีต้นทุนเพียงเล็กน้อย สำหรับไฟล์แนบขนาดใหญ่ ให้ตั้งคำถามว่าไบนารี่ควรเดินทางไปในการตอบสนองเดียวกันหรือเป็นทรัพยากรที่แยกจากกัน

บันทึกรูปแบบการเข้ารหัสและขนาดสูงสุดไว้ในข้อกำหนด API ของคุณ เมื่อตรวจสอบการตอบสนอง ให้ใช้ตัวเข้ารหัสและตัวถอดรหัส Base64 เพื่อตรวจสอบการถอดรหัสฟิลด์อย่างถูกต้อง และเพื่อทำความเข้าใจสิ่งที่เซิร์ฟเวอร์ส่งจริง ระเบียบวินัยดังกล่าวช่วยให้มองเห็นการแลกเปลี่ยนและการตัดสินใจโดยเจตนามากกว่าไม่ได้ตั้งใจ