项目有个需求,就是把数据库中的第三方的图片全部传到公司的cdn(七牛)上。但是公司的 cdn 有几个,都需要排除在外,那就需要对某个字段进行多个约束查询了。
先说点题外的,这个需求我的解决过程是(使用官方node版sdk):
- 查找数据库匹配的图片地址(列表,多数据)
- 调用七牛官方提供的方法获取 token
- 使用 node.js 的 request 方法获取数据流
- 都用七牛数据流上传(表单方式)
- 用七牛上传之后的文件名加上公司的cdn前缀作为最终图片url替换数据库中的数据
代码如下:
import * as qiniu from 'qiniu';
import * as moment from "moment";
const request = require('request');
interface IUploadRetrun {
id: number;
url: string | null;
}
async upload_files(urls: IUploadRetrun[], type: string, token?: string) {
const config: any = new qiniu.conf.Config();
config.zone = qiniu.zone.Zone_z2;
const formUploader = new qiniu.form_up.FormUploader(config);
const putExtra = new qiniu.form_up.PutExtra();
if (!token) {
token = await this.get_token(); // 省略 get_token 的代码
}
let keys = {};
for (let url of urls) {
this.app.logger.info('url: ', url.url)
const uploadFunc = async () => {
return new Promise((resolve, reject) => {
let readableStream = request(url.url);
const timeStamp = moment().valueOf();
const key = `images/${type}/${type}_${timeStamp}.jpg`;
try {
formUploader.putStream(
token,
key,
readableStream,
putExtra,
(respErr, respBody, respInfo) => {
if (respInfo.statusCode == 200) {
return resolve(respBody && respBody.key);
} else {
this.app.logger.info('respErr: ', respErr);
this.app.logger.info('respBody: ', respBody);
return resolve(null);
}
}
);
} catch (e) {
this.app.logger.info(`upload file to qiniu error: ${JSON.stringify(e)}`);
return resolve(null);
}
})
}
const key = await uploadFunc();
this.app.logger.info('key: ', key);
keys[url.id] = key;
}
return keys;
}
因为我的项目使用的是 egg + egg-sequelize(ORM) 的形式,数据库是 PostgreSQL,所以查询的时候我一开始的想法是使用 sequelize 提供的方法,特意查看了一下文档,期望的代码如下:
let results = await this.app.model.Xxxx.findAll({
where: {
image: {
$notLike: { $any: ['http://cdn.aaa.com%', 'http://cdn.bbb.com%']}
}
}
});
但是 ts 一直在报错,索性就用 SQL 查询来做了,以下三种方式测试都是可用的。推荐使用第一种方式,语义明确,代码也最简练,第三张是最笨的方法,只需要把所有的约束用 AND 连接就好了。
SELECT id,image AS url FROM table1 WHERE image NOT SIMILAR TO 'http://(cdn.aaa.com|cdn.bbb.com|cdn.ccc.com)%' ORDER BY id LIMIT 10
SELECT id,image AS url FROM table1 WHERE NOT image LIKE ANY (ARRAY['http://cdn.aaa.com%', 'http://cdn.bbb.com%', 'http://cdn.ccc.com%']) ORDER BY id LIMIT 10
SELECT id,image AS url FROM table1 WHERE (image NOT LIKE 'http://cdn.aaa.com%' AND image NOT LIKE 'http://cdn.bbb.com%' AND image NOT LIKE 'http://cdn.ccc.com%') ORDER BY id LIMIT 10
查找并替换图片地址完整代码如下:
// 查找并替换图片地址
async renew_images(count = 10) {
let sql = `SELECT id,image AS url FROM table1 WHERE image NOT SIMILAR TO '(${CONTANTS.IMAGE_WHITE_LIST.join("|")})%' ORDER BY id LIMIT ${count}`;
let images = await this.app.model.query(sql, {
type: this.app.model.QueryTypes.SELECT
});
const token = await this.service.qiniu.get_token();
let results = await this.service.qiniu.upload_files(images, 'image', token);
for (let key in results) {
if (results[key]) {
this.app.model.HuFen.update({
image: `http://cdn.xxx.com/${results[key]}`
}, {
where: { id: parseInt(key) }
});
}
}
return results;
}