曾在几个月前,我对PHP略感兴趣时,CV了一个PHP的图片API。

但是,他却并不满足我对API的需求。比如说,没办法 我不会按要求进行分类别访问。

原本,我想用python去构建我的新图片API。但是前两天发现 @revincx 使用了 Node.js 来构建了 API 服务。而Node.js相较于python,也更加地轻量,占用的服务器内存也更少。

于是乎,就决定是你了。 Node.js!

前言

本次 API编写 使用的技术:

  • 后端:Node.js + Mysql + axios
  • 数据库:Mysql
    代码可读性待考究

图片API

数据库对接

首先,自然是先建数据库
首先,我们要使用 Node.js 来连接数据库

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
var mysql = require('mysql');
const pool = mysql.createPool({
host:"localhost",
user:" ",
password:" ",
database:" "
})
let query = function( sql, values ) {
return new Promise(( resolve, reject ) => {
pool.getConnection(function(err, connection) {
if (err) {
reject( err )
} else {
connection.query(sql, values, ( err, rows) => {
if ( err ) {
reject( err )
} else {
resolve( rows )
}
connection.release()
})
}
})
})
}

module.exports = {
query
}

接着,图片API必需的便是取出数据了。

1
2
3
4
5
6
7
8
9
10
11
// 获取PC图片
let getPcimg = async () => {
try {
let result = await query('SELECT url FROM pcimg WHERE bool=1 ORDER BY RAND() LIMIT 1')
return result
}
catch (err){
console.log(err);
return 'ERROR'
}
}

接着,手动向数据库中存储过于麻烦,所以我美名其曰创建了上传图片的入口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let addPcimg = async (urls) => {
try {
let date = moment(new Date()).format('YYYY-MM-DD HH:mm:ss')
let post = {url:urls,bool:0,create_time:date};
let sql = "INSERT INTO pcimg SET ?";
await query(sql,post,(err,result) => {
if(err){
console.log(err);
}else{
res.send("pcimg added....")
}
})

}
catch (err){
console.log(err);
return 'ERROR'
}
}

当然,为避免重复上传,便于数据库的维护,上传之前进行查询操作。

1
2
3
4
5
6
7
8
9
10
11
let findpcimg = async (url) => {
try {
let sql = `SELECT id FROM pcimg WHERE url = '${url}' LIMIT 1` ;
let result = await query(sql)
return result
}
catch (err){
console.log(err);
return 'ERROR'
}
}

大概这样,服务器对接操作就完成了。

后端部分

这次的图片API,默认的图片类型既不是PC也不算Phone,而将通过浏览器的请求头中的user-agent来获取并确定图片的类型。

1
2
3
4
5
6
7
8
9
if ( size === undefined){
var deviceAgent = req.headers['user-agent']
var agentID = deviceAgent.match(/(iPad|Android|iPhone)/);
if(agentID){
size = 'phone'
}else{
size = 'pc'
}
}

接着,定义了一个type来判断是否跳转新浪微博图床外链,亦或者是欣赏图片。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var type = req.query.type || '0';
if( size == 'phone'){
if (type == '0'){
var index = Math.floor(Math.random()*1287)+1
res.setHeader("Content-Type", "image");
url = "src/public/img/phone/img/"+ index + ".jpg"
var content = fs.readFileSync(url,"binary");
res.writeHead(200, "Ok");
res.write(content,"binary");
res.end()
}
else if(type == '1'){
let picUrl = await getPhoneimg()
res.redirect(picUrl[0].url)
}
else{
res.status(400)
res.send('type请求参数有误')
}

}

由于对Node.js的了解有点少,暂时没有找到更好的办法去随机读取本地图片。所以我采用的最简单的方法,用python脚本将图片按顺序重命名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import os
rootdir=' ' # 原文件夹地址
rootdirs=' ' # 更名后文件夹地址
filelist = os.listdir(rootdir)
total_num = len(filelist)
i = 1
for item in filelist:
src = os.path.join(os.path.abspath(rootdir), item)
dst = os.path.join(os.path.abspath(rootdirs), str(i) + '.jpg')
os.rename(src, dst)
print ('converting %s to %s ...' % (src, dst))
i = int(i)
i = i + 1

最后,便是之前的上传请求,方便你我他。(基于python脚本上传图片真滴爽

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
randomRouter.get("/addpcimg", (async (req, res) => {
let url = req.query.url
var v = new RegExp('^(?!mailto:)(?:(?:http|https|ftp)://|//)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$', 'i');
let flag = v.test(url);
if (url === undefined || !flag) {
res.send('请输入正确的链接')
}
else {
let imgtype = await axios.get(url)
.then(function (response) {
return response.headers['content-type']
})
.catch(function (error) {
return 404
});
imgtype = imgtype.match(/(image)/);
if (imgtype) {
let id = await findpcimg(url)
if (id == '') {
let result = await addPcimg(url)
res.send('链接提交成功了,谢谢你')
}
else {
res.send('提交失败,数据库中已经存在该链接,谢谢你')
}
}
else res.send('请提交图片类型的链接')
}
}))

说个题外话,之前抓包时便发现了get发送请求,当时还很震惊。结果回过头来,发现这次上传图片我使用的便是get请求,果然还是我见识短浅呀

DEMO

随机图片

洗衣机空闲查询

怎么说都是我实现的第一个API : (

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
washRouter.get('/',((req, res) => {
store = {
'a01': '57495385b173d5fa7659e2da',
'a02': '5b866235c5a9b6352c000005',
'a03': '5b86620d0c05def97f000001',
'a04': '57497034b173d5fa7659e33e',
'b01': '57497e0fb173d5fa7659e391',
'b02': '57497e81b173d5fa7659e394',
'b03': '57497be0b173d5fa7659e38e',
'b04': '57497800b173d5fa7659e372',
'd01': '5d7222e3e953b42024000004',
'e04': '58007d7dd9d58ae541ea0fd3',
'e03': '58118cb3243964a015acea73',
'e01': '58007ed0d9d58ae541ea0fd6',
'e02': '58007f59d9d58ae541ea0fde',
'f': '57d259f6773d0edd58db1b82'
}
let storename = req.query.store.toLocaleLowerCase()
if (storename === undefined || !store[storename]){
res.status(400)
let notes = { 'code': 404, 'message': '请求参数有误', 'data': ''}
res.send(notes)
return 1
}
else {
axios({
method: 'get',
baseURL : 'https://phoenix.ujing.online/api/v1/devices/reserve?storeId=' + store[storename],
headers : {
"authorization": " ",
},
})
.then(function (response) {
let storeName = response.data.data.store.storeName
let free = response.data.data.devices[0].device.free
let waitTime = response.data.data.devices[0].device.waitTime
res.status(200)
let notes = { 'code': 0, 'message': 'success', 'data':{'name': storeName, 'free': free, 'waittime': waitTime}}
res.send(notes)
});
}
}))

其实洗衣房这个,还准备实现post发请求生成订单占位的,Python也将其都实现了。后来想了想还是算了,还是小命要紧。
(或许为了会去开放,毕竟这样比pyhton发请求要简单点