Xpages中Base64编码Vcard特殊字符异常问题求助
解决Base64编码特殊字符(如é)的问题
问题背景
开发生成Base64编码VCard的应用时,遇到特殊字符编码不符合要求的情况:
- 目标字符串:
Zone d'activité - 符合VCard要求的正确Base64结果:
Wm9uZSBkJ2FjdGl2aXTDqQ== - 现有自定义
base64_encode脚本生成的错误结果:Wm9uZSBkJ2FjdGl2aXTp
现有脚本代码如下:
function base64_encode (data) { // http://kevin.vanzonneveld.net // + original by: Tyler Akins (http://rumkin.com) // + improved by: Bayron Guevara // + improved by: Thunder.m // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // + bugfixed by: Pellentesque Malesuada // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // + improved by: Rafał Kukawski (http://kukawski.pl) // * example 1: base64_encode('Kevin van Zonneveld'); // * returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA==' // mozilla has this native // - but breaks in 2.0.0.12! //if (typeof this.window['btoa'] == 'function') { // return btoa(data); //} var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, enc = "", tmp_arr = []; if (!data) { return data; } do { // pack three octets into four hexets o1 = data.charCodeAt(i++); o2 = data.charCodeAt(i++); o3 = data.charCodeAt(i++); bits = o1 << 16 | o2 << 8 | o3; h1 = bits >> 18 & 0x3f; h2 = bits >> 12 & 0x3f; h3 = bits >> 6 & 0x3f; h4 = bits & 0x3f; // use hexets to index into b64, and append result to encoded string tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4); } while (i < data.length); enc = tmp_arr.join(''); var r = data.length % 3; return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3); } var demo = "Zone d'activité" var test = base64_encode(demo); return test;
问题原因
并非Base64不支持特殊字符,而是字符编码不匹配:
- VCard规范要求Base64编码的内容必须基于UTF-8字节流,比如
é在UTF-8中是两个字节0xC3 0xA9 - 现有脚本使用
charCodeAt()获取的是UTF-16编码的单个码点0xE9,直接基于这个码点编码,导致结果与要求不符
解决方案
方案1:使用浏览器原生API(推荐)
原生btoa仅支持ASCII,需先将字符串转换为UTF-8字节序列再编码:
function utf8ToBase64(str) { return btoa(unescape(encodeURIComponent(str))); } // 测试 const demo = "Zone d'activité"; console.log(utf8ToBase64(demo)); // 输出正确结果:Wm9uZSBkJ2FjdGl2aXTDqQ==
方案2:修改自定义脚本支持UTF-8
先将字符串转换为UTF-8字节数组,再执行Base64编码逻辑:
function base64_encode_utf8(data) { const b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; let enc = ""; let i = 0; // 转换字符串为UTF-8字节数组 const utf8Bytes = []; for (const char of data) { const code = char.charCodeAt(0); if (code < 0x80) { utf8Bytes.push(code); } else if (code < 0x800) { utf8Bytes.push(0xC0 | (code >> 6)); utf8Bytes.push(0x80 | (code & 0x3F)); } else if (code < 0x10000) { utf8Bytes.push(0xE0 | (code >> 12)); utf8Bytes.push(0x80 | ((code >> 6) & 0x3F)); utf8Bytes.push(0x80 | (code & 0x3F)); } } // 执行Base64编码 while (i < utf8Bytes.length) { const o1 = utf8Bytes[i++] || 0; const o2 = utf8Bytes[i++] || 0; const o3 = utf8Bytes[i++] || 0; const bits = (o1 << 16) | (o2 << 8) | o3; const h1 = (bits >> 18) & 0x3F; const h2 = (bits >> 12) & 0x3F; const h3 = (bits >> 6) & 0x3F; const h4 = bits & 0x3F; enc += b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4); } const r = utf8Bytes.length % 3; return r ? enc.slice(0, r - 3) + '==='.slice(r) : enc; } // 测试 const demo = "Zone d'activité"; console.log(base64_encode_utf8(demo)); // 输出正确结果:Wm9uZSBkJ2FjdGl2aXTDqQ==
注意事项
- 不要手动替换特殊字符,这会破坏原字符串语义,正确做法是统一采用UTF-8编码后再进行Base64编码
- 确保VCard生成流程中所有文本内容都采用UTF-8编码,符合规范要求
内容的提问来源于stack exchange,提问作者Marc Jonkers
相关产品推荐
相关产品推荐

