5月笔记

你今年30岁 那么如果你利用业务时间学英语 那么5年也就是你35岁时 你可能就可以成为一个说英文很流利的人🌹

永远不要被时间限制住自己 尽你自己最大的努力 努力做成你最想做的那件事 成为你最想成为的那种人 过着你最想过的那种生活 💳

也许我们始终都只是一个小人物 但这并不妨碍我们选择用什么样的方式活下去 这个世界永远比你想的要更精彩 📚🌲

之所以写上面这段鸡汤,完全是为了避免字数太少,导致主页摘要不出现代码行号的情况。请忽略~


js对称加密算法

最近有用到 js 的对称加密的算法来解决一些简单的数据库字段加密问题。简单的 demo 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
const crypto = require('crypto')

class Aes {
// 此处4个参数可以改成由外部传入
constructor() {
this.key = 'xxxxxx'
this.algorithm = 'aes256'
this.inputEncoding = 'utf8'
this.outputEncoding = 'hex'
}

/**
* encrypt 加密
* @param data
* @returns {string}
*/
encrypt(data) {
let { algorithm, inputEncoding, outputEncoding, key } = this

let cipher = crypto.createCipher(algorithm, key)
let ciphered = cipher.update(data, inputEncoding, outputEncoding)

ciphered += cipher.final(outputEncoding)

return ciphered
}

/**
* descrpt 解密
* @param ciphered
* @returns {string}
*/
descrpt(ciphered) {
let { algorithm, inputEncoding, outputEncoding, key } = this

let decipher = crypto.createDecipher(algorithm, key)
let deciphered = decipher.update(ciphered, outputEncoding, inputEncoding)

deciphered += decipher.final(inputEncoding)

return deciphered
}
}

module.exports = Aes

使用 xlsx 转换文件

以下记录下最近使用的xlsx库,使用的目的是解决下面两个问题:

  1. 将 excel 转换为 CSV(如 /home/seven/201212 -> /home/seven/201212_txt), 并写入原目录,因为后台不好处理 excel 文件。
  2. 用户点击下载按钮,则将数据库中的数据,转换为 excel 并下载到前端。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
const fs = require('fs')
const path = require('path')
const XLSX = require('xlsx')
const crypto = require('crypto')

class ExcelUtil {
constructor() {}

/**
* excel 表格转换成 txt 文件
* 注意这里使用 sheet_to_csv 而不是 sheet_to_txt
* 因为 sheet_to_txt 是转成了 utf16, 在头行会有 bom
* @param filepath
* @returns {*}
*/
transformToCSV(filepath) {
// 先判断用户上传的文件是否存在
if(!fs.existsSync(filepath)) {
// 处理错误
}

// 返回 workbook
const wb = XLSX.readFile(filepath)

// 返回 worksheet
const ws = wb.Sheets[wb.SheetNames[0]]

// 转换 worksheet 到 csv 文件
let data = XLSX.utils.sheet_to_csv(ws)

// 获得文件名,区别与用户上传的 excel 文件,这里增加 _txt
let filename = `${filepath}_txt`

// 写入文件,若写入失败,则不能够同步后台发布任务
try {
fs.writeFileSync(filename, data)
} catch (e) {
// 处理错误如返回错误的错误码之类的
}

// 写入成功,返回前端
return {
ret: 0,
filename: path.basename(filename)
}
}

/**
* 将数据转换为 Excel 表格
* @param sheetData 表数据
* @param sheetHeader 表头
* @param filename 文件名称
* @param sheetName 表名称
* @param wscols 列样式
* @returns {any}
*/
transformToExcel(sheetData, sheetHeader, filename, sheetName, wscols) {
// 添加表头
sheetData.unshift(sheetHeader)

let wb = XLSX.utils.book_new()
let ws = XLSX.utils.json_to_sheet(sheetData, {
header: Object.keys(sheetHeader),
skipHeader: true
})

// 设置列样式
if (wscols) {
ws['!cols'] = wscols
}

XLSX.utils.book_append_sheet(wb, ws, sheetName)

return XLSX.write(wb, {
bookType: filename.split('.').pop(),
bookSST: false,
type: 'buffer'
})
}

/**
* 删除某一行
* @param ws
* @param row_index
*/
deleteRow(ws, row_index) {
const ec = (r, c) => {
return XLSX.utils.encode_cell({r: r, c: c})
}

let range = XLSX.utils.decode_range(ws["!ref"])

for (let R = row_index; R < range.e.r; ++R) {
for (let C = range.s.c; C <= range.e.c; ++C) {
ws[ec(R, C)] = ws[ec(R + 1, C)]
}
}

range.e.r--

ws['!ref'] = XLSX.utils.encode_range(range.s, range.e)
}
}

module.exports = ExcelUtil

transformToExcel调用方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
let sheetName = '表名'
let filename = `${+new Date()}.xlsx`

let sheetHeader = {
ID: 'ID',
Name: '姓名'
}

// 这里用来填充数据
let sheetData = await ctx.models.DBUser.findAll({
where : {
xxx : xxx
},
raw: true,
attributes: ['ID', 'Name']
})

let excelUtil = new ExcelUtil()

// 设置表宽度
let wscols = [
{wch: 28},
{wch: 20}
]

let data = excelUtil.transformToExcel(sheetData, sheetHeader, filename, sheetName, wscols)

// 设置 header 头信息 ,getAttachHeader 省略
ctx.set({
'Content-disposition': getAttachHeader(ctx, filename),
'Content-type': 'application/octet-stream'
})

// 返回给前端相应的内容
ctx.body = data

注意要结合前端代码来一起下载。如下,这样用户点击下载的时候,就完成了从数据库到 excel 的过称。

1
2
3
4
5
6
7
8
9
10
11
12
let iframe = document.createElement('iframe')

iframe.style.display = 'none'

// 传入Nodejs层的接口
iframe.src = url

iframe.onload = iframe.onerror = () => {
document.body.removeChild(iframe)
}

document.body.appendChild(iframe)

获得文件 MD5

目的是防止文件被篡改,主要是使用 crypto 库。传入文件的 buffer。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 获得文件的 MD5, 防止文件被篡改
* @param filename
* @returns {string}
*/
const genMd5 = filename => {
let buffer

try {
buffer = fs.readFileSync(filename)
} catch(e) {
return ''
}

let fsHash = crypto.createHash('md5')

fsHash.update(buffer)

return fsHash.digest('hex')
}


sequelize 的 transaction

transaction

sequelize 的事务主要有上面两种方式,一个是自行回滚的写法,另一个是手动声明回滚的写法。我用的是自行回滚的方法。文档

自行回滚的方法,需要在开始向sequelize.transaction传入一个 callback。这种时候,不需要再去进行t.commit() or t.rollback()了。当到 catch 内时,会自行回滚。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
return sequelize.transaction(t => {

// chain all your queries here. make sure you return them.
return User.create({
firstName: 'Abraham',
lastName: 'Lincoln'
}, {transaction: t}).then(user => {
return user.setShooter({
firstName: 'John',
lastName: 'Boothe'
}, {transaction: t});
});

}).then(result => {
// Transaction has been committed
// result is whatever the result of the promise chain returned to the transaction callback
}).catch(err => {
// Transaction has been rolled back
// err is whatever rejected the promise chain returned to the transaction callback
});

如果你是用的 async await

1
2
3
4
5
6
7
8
9
10
11
try {
await sequelize.transaction(async transaction => {
// step 1
await Model.destroy({where: {id}, transaction});

// step 2
await Model.create({}, {transaction});
})
} catch {
// 若上面执行过程中出现错误,则会到 catch 中,并且会自动回滚
}

如果不想使用自动回滚的这种方法,则不向sequelize.transaction()不传入一个 callback。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
return sequelize.transaction().then(t => {
return User.create({
firstName: 'Bart',
lastName: 'Simpson'
}, {transaction: t}).then(user => {
return user.addSibling({
firstName: 'Lisa',
lastName: 'Simpson'
}, {transaction: t});
}).then(() => {
return t.commit();
}).catch((err) => {
return t.rollback();
});
});

如果使用 async await 则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let transaction;    

try {
// get transaction
transaction = await sequelize.transaction();

// step 1
await Model.destroy({where: {id}, transaction});

// step 2
await Model.create({}, {transaction});

// commit
await transaction.commit();

} catch (err) {
// Rollback transaction if any errors were encountered
if (err) await transaction.rollback();
}

很好用~


总结

5月就做了一个项目,主要是业务逻辑太复杂了,对数据库里的一个字段都要校验几百行代码。

5月去台湾团建,体验一般般,但也算是走出去看看了。

转眼间19年都快过去一半了。加油⛽️!

剩下的几天把项目分支再都验证下,写测试用例,优化下代码,然后再总结下几个问题。