Skip to content

Instantly share code, notes, and snippets.

@nuintun
Last active November 7, 2025 07:08
Show Gist options
  • Select an option

  • Save nuintun/7508b9ddd8421e66f5e6 to your computer and use it in GitHub Desktop.

Select an option

Save nuintun/7508b9ddd8421e66f5e6 to your computer and use it in GitHub Desktop.
用 XMLDOM 和 ADODB.Stream 实现base64编码解码
/**
* Base64
* http://blog.csdn.net/cuixiping/article/details/409468
* ADODB.Stream 实例有 LoadFromFile 方法可以读取文件内容
* ADODB.Stream 实例有 state 属性 0 和 1,分别对应 Open 状态: adStateClosed 和 adStateOpen
* ADODB.Stream 实例有 LineSeparator 属性 13、-1 和 10,分别对应换行符: adCR、adCRLF 和 adLF
* ADODB.Stream 实例的其他属性和方法可参考相应文档
*/
var Base64 = {
/**
* Base64 encode
*
* @param string
* @returns {Base64}
*/
encode: function(string) {
var base64;
var xmldom = new ActiveXObject('MSXML2.DOMDocument');
var adostream = new ActiveXObject('ADODB.Stream');
var stream = xmldom.createElement('stream');
stream.dataType = 'bin.base64';
adostream.Charset = 'utf-8';
// 1=adTypeBinary 2=adTypeText
adostream.Type = 2;
adostream.Open();
adostream.WriteText(string);
adostream.Position = 0;
// 1=adTypeBinary 2=adTypeText
adostream.Type = 1;
// -1=adReadAll
stream.nodeTypedValue = adostream.Read(-1);
base64 = stream.text;
adostream.Close();
// clean
stream = null;
adostream = null;
xmldom = null;
return base64;
},
/**
* Base64 decode
*
* @param base64
* @returns {String}
*/
decode: function(base64) {
var string;
var xmldom = new ActiveXObject('MSXML2.DOMDocument');
var adostream = new ActiveXObject('ADODB.Stream');
var stream = xmldom.createElement('stream');
stream.dataType = 'bin.base64';
stream.text = base64;
adostream.Charset = 'utf-8';
// 1=adTypeBinary 2=adTypeText
adostream.Type = 1;
adostream.Open();
adostream.Write(stream.nodeTypedValue);
adostream.Position = 0;
// 1=adTypeBinary 2=adTypeText
adostream.Type = 2;
// -1=adReadAll
string = adostream.ReadText(-1);
adostream.Close();
// clean
stream = null;
adostream = null;
xmldom = null;
return string;
}
};
@nuintun
Copy link
Author

nuintun commented Oct 15, 2025

/**
 * @module base64
 */

import { isFunction } from './utils';

/**
 * @function encode
 * @description 将 Uint8Array 转换为 base64 字符串
 * @param {Uint8Array} bytes 需要转换的字节数组
 * @returns {string} base64 字符串
 */
export function encode(bytes: Uint8Array): string {
  // @ts-expect-error
  if (isFunction(bytes.toBase64)) {
    // @ts-expect-error
    return bytes.toBase64();
  }

  let binary = '';

  const length = bytes.length;

  for (let i = 0; i < length; i++) {
    binary += String.fromCharCode(bytes[i]);
  }

  return btoa(binary);
}

/**
 * @function decode
 * @description 将 base64 字符串转换为 Uint8Array
 * @param {string} base64 base64 字符串
 * @returns {Uint8Array} 转换后的字节数组
 */
export function decode(base64: string): Uint8Array {
  // @ts-expect-error
  if (isFunction(Uint8Array.fromBase64)) {
    // @ts-expect-error
    return Uint8Array.fromBase64(base64);
  }

  const binary = atob(base64);
  const length = binary.length;
  const bytes = new Uint8Array(length);

  for (let i = 0; i < length; i++) {
    bytes[i] = binary.charCodeAt(i);
  }

  return bytes;
}

@nuintun
Copy link
Author

nuintun commented Nov 7, 2025

/**
 * @module base64
 */

import { isFunction } from './utils';

export interface EncodeOptions {
  /**
   * @description 使用的 Base64 字母表
   * @default 'base64'
   */
  alphabet?: 'base64' | 'base64url';
  /**
   * 是否省略编码字符串末尾的填充字符 (=)
   * @default false
   */
  omitPadding?: boolean;
}

export interface DecodeOptions {
  /**
   * @description 用于编码字符串的 Base64 字母表
   * @default 'base64'
   */
  alphabet?: 'base64' | 'base64url';
  /**
   * @description 如何处理 Base64 字符串的最后一个块
   * @default 'loose'
   */
  lastChunkHandling?: 'loose' | 'strict';
}

/**
 * @function encode
 * @description 将 Uint8Array 转换为 base64 字符串
 * @param {Uint8Array} bytes 需要转换的字节数组
 * @param {EncodeOptions} [options] 编码选项
 * @returns {string} base64 字符串
 */
export function encode(bytes: Uint8Array, options?: EncodeOptions): string {
  // @ts-expect-error
  if (isFunction(bytes.toBase64)) {
    // @ts-expect-error
    return bytes.toBase64(options);
  }

  // 二进制字符串
  let binary = '';

  // 获取字节数组的长度
  const length = bytes.length;

  // 填充二进制字符串
  for (let i = 0; i < length; i++) {
    binary += String.fromCharCode(bytes[i]);
  }

  // 使用 btoa 转换为 base64
  let base64 = btoa(binary);

  // 如果使用 base64url 编码
  if (options?.alphabet === 'base64url') {
    // 替换 + 和 / 为 - 和 _
    base64 = base64.replace(/[+/]/g, char => {
      return char === '+' ? '-' : '_';
    });

    // 如果省略填充字符
    if (options?.omitPadding) {
      base64 = base64.replace(/=+$/, '');
    }
  }

  // 返回 base64 字符串
  return base64;
}

/**
 * @function decode
 * @description 将 base64 字符串转换为 Uint8Array
 * @param {string} base64 base64 字符串
 * @param {DecodeOptions} [options] 解码选项
 * @returns {Uint8Array} 转换后的字节数组
 */
export function decode(base64: string, options?: DecodeOptions): Uint8Array {
  // @ts-expect-error
  if (isFunction(Uint8Array.fromBase64)) {
    // @ts-expect-error
    return Uint8Array.fromBase64(base64, options);
  }

  // 删除首尾空白
  let normalized = base64.trim();

  // 根据 alphabet 选项处理字符转换
  if (options?.alphabet === 'base64url') {
    // 使用字符映射优化替换操作
    normalized = normalized.replace(/[-_]/g, char => {
      return char === '-' ? '+' : '/';
    });
  }

  // 如果为 strict 模式,进行长度检查
  if (options?.lastChunkHandling === 'strict') {
    // 检查长度是否满足 4 的倍数
    if (normalized.length % 4 !== 0) {
      throw new SyntaxError('The base64 input terminates with a single character, excluding padding (=)');
    }
  }

  // 使用 atob 解码
  const binary = atob(normalized);
  // 获取字节数组的长度
  const length = binary.length;
  // 创建字节数组
  const bytes = new Uint8Array(length);

  // 填充字节数组
  for (let i = 0; i < length; i++) {
    bytes[i] = binary.charCodeAt(i);
  }

  // 返回字节数组
  return bytes;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment