仓库源文站点原文


title: Nodejs使用MySQL 4.1的问题解决 date: 2018-10-12 22:04:17 tag: [Node.js,MySQL] categories: Node.js

description: 近期有同学在玩一台老古董服务器,要使用node连上4.1版本的MySQL进行操作。并且不让改动数据库配置,凭啥?人家是移动提供的MAS……本文分享一下帮忙时填的坑,主要为解决“数据库连接”和“编码转换”问题。

数据库连接

如果使用old authentication方式连接4.1版本之前的mysql,sequelizemysql2无法通过认证:

{ Error: Access denied for user: '@127.0.0.x' (Using password: NO)
    at Packet.asError (/home/claude/Workspace/packages/sql/node_modules/mysql2/lib/packets/packet.js:714:13)
    at ClientHandshake.Command.execute (/home/claude/Workspace/packages/sql/node_modules/mysql2/lib/commands/command.js:28:22)
    at Connection.handlePacket (/home/claude/Workspace/packages/sql/node_modules/mysql2/lib/connection.js:513:28)
    at PacketParser.onPacket (/home/claude/Workspace/packages/sql/node_modules/mysql2/lib/connection.js:81:16)
    at PacketParser.executeStart (/home/claude/Workspace/packages/sql/node_modules/mysql2/lib/packet_parser.js:76:14)
    at Socket.<anonymous> (/home/claude/Workspace/packages/sql/node_modules/mysql2/lib/connection.js:89:29)
    at Socket.emit (events.js:182:13)
    at addChunk (_stream_readable.js:283:12)
    at readableAddChunk (_stream_readable.js:264:11)
    at Socket.Readable.push (_stream_readable.js:219:10)
    at TCP.onread (net.js:639:20)
  code: 'ER_ACCESS_DENIED_ERROR',
  errno: 1045,
  sqlState: '',
  sqlMessage:
   'Access denied for user: \'@127.0.0.x\' (Using password: NO)' }

处理倒不困难,在不能改动数据库的情况下,可以改用npm包mysql

编码转换

移动mas的接入文档有描述如下:

mysql使用ISO8859-1编码,往db接口写入数据时应先把编码格式转化为ISO8859-1...

上述编码实际为latin1,为早期mysql的默认编码。实际文档存在误导,未指出mas机的db接口是使用gbk编码写入的,因此将字符进行转化gbk再使用该接口即可,无需再将编码转为latin1

但如果想从数据库中读取,同事在创建连接时指定了charset=latin1获取到的中文是乱码。

这涉及到mysql如何用latin1存储中文的问题:latin1为0x00 to 0xFF范围的单字节编码(ASCII是它的子集),理论上单字节范围可以无损存储数据,任意编码均可以用字节流形式存储。

也就是说,mas机写入之前用的是gbk字节流,读取时直接用nodejs默认的utf8编码自然不行。那么怎样读出二进制数据呢?在npm mysql库的README中搜索buffer字样,找到了如下方法。

mysqljs支持在query中自定义typeCast方法,可用于提取数据的步骤进行编码转换。

const mysql = require('mysql');
const iconv = require('iconv-lite');
const connection = mysql.createConnection({
  host     : 'localhost',
  user     : 'me',
  password : 'secret',
  database : 'my_db',
  charset  : 'latin1'
});

connection.connect();

connection.query({
  sql: 'SELECT * FROM tbl_user',
  typeCast: (field, next) => {
    // converting `tbl_user.name` to utf8 string:
    if (field.name === 'username') {
      return iconv.decode(field.buffer(), 'gbk');
    }
    return next();
  }
}, (error, results, fields) => {
  //
});

其中typeCast的参数field包含

依此,其他更多问题都可以迎刃而解。

参考资料: