自己开发一个图床接口——基于 NodeJS.

最近买了一堆服务器不知道要用来干什么…看到某乎上的回答,便准备搭建一个图床。但觉得这些现成的图床程序有些「花里胡哨」,于是便决定自己基于 NodeJS 开发一个图床接口自用。

为什么不用 OSS / COS?

阿里云 OSS, 腾讯云 COS 都属于对象存储。它的计费分为两部分:存储计费和流量计费。存储计费十分简单,并且都相应提供了存储包,十分划算。但是在流量计费上就有些复杂了,如果你的对象存储被人死刷流量,一夜破产都有可能。且我个人使用的阿里云 OSS 并没有频次控制功能,因此我选择购买一台服务器解决这个问题,因为它能通过一系列配置预防此类 CC 攻击。

如何实现?

我们可以将这个图床接口看作这些「简单」的动作。

客户端发送 POST 请求->反向代理(可选)->Express->反向代理(可选)->返回图床JSON数据。

而对于后端的 NodeJS,我们大概需要对传入的图片做这些处理。

  • 获取文件名的 MD5 值
  • 获取传入图片的后缀名
  • 将图片以文件名 “图片文件名MD5 + 传入图片后缀名” 的形式进行存储
  • 向客户端返回 JSON (包含 status 和图片链接)

知道了这些,我们就可以开始愉快的开发了。

开发

首先我们需要引入 express 框架,并创建一个应用。express 需要通过 npm 进行安装。

const express = require(‘express’);
const app = express();

之后在这基础上,创建一个 server 并监听 4000 端口。

var server = app.listen(4000, ‘127.0.0.1’, function(){
var host = server.address().address;
var port = server.address().port;
console.log(“Server running at http://%s:%s”, host, port);
})

之后,我们便可以开始解决 POST 文件的处理了。

首先,我们需要引入和配置这些中间件和模块,它们各自的作用已经写在注释了。

// 工具库 - lodash
const _ = require('lodash');
// assert - 测试是否为真值
const { ok } = require('assert');
// crypto - 用于获取文件名 MD5 值
const crypto = require('crypto');
// path - 用于获取文件拓展名
var path=require('path');

// 使用 express-fileupload 中间件
const fileUpload = require('express-fileupload');
app.use(fileUpload({
    createParentPath: true
}));

// 处理跨域问题 - cors 模块
const cors = require('cors');
app.use(cors());

// 解析 POST 数据
const bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));

// 使用 morgan 中间件 - 输出请求详细信息
const morgan = require('morgan');
app.use(morgan('dev'));

接下来,便可以编写接收和处理数据的代码了。

app.post('/api/post', async (req, res) => {
    try {
        // 如果没有文件被上传,则返回以下 JSON 数据
        if(!req.files) {
            res.send({
                status: "No file uploaded",
                link: "undefined"
            })
        }
        else{
            let avatar = req.files.avatar; // 声明字段名为 avatar 的图片数据为 avatar 变量
            let md5 = crypto.createHash('md5').update(avatar.name).digest('hex'); // 取 avatar 文件名的 MD5 值
            avatar.mv('./uploads/' + md5 + path.extname(avatar.name)); // 存储(移动)图片到 uploads 文件夹,文件名为 avatar 文件名 MD5 + 文件拓展名
            // 发送以下 JSON 数据
            res.send({
                status: "ok",
                link: "https://example.com/uploads/" + md5 + path.extname(avatar.name)
            });
        }
    }
    // 如果上传出现错误,则返回 HTTP 500.
    catch (err) {
        res.status(500).send(err);
    }
})

最后,你可以尝试运行 app.js 然后使用 Postman 来测试它。

node app.js

进阶操作 (外网服务配置 + 持久化)

如果要在公网上进行使用,我们或许需要配置 Nginx 反向代理到 express. 当然,你也可以直接将 Express 暴露在公网上。

以下是我的反向代理配置,从宝塔面板抄来的

location  ~* \.(php|jsp|cgi|asp|aspx)$
{
proxy_pass http://localhost:4000;
proxy_set_header Host localhost;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header REMOTE-HOST $remote_addr;
}
location /api/post
{
proxy_pass http://localhost:4000;
proxy_set_header Host localhost;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header REMOTE-HOST $remote_addr;
add_header X-Cache $upstream_cache_status;
#Set Nginx Cache
add_header Cache-Control no-cache;
expires 12h;
}

同时为了方便,我将图片存储在了站点根目录的 uploads 文件夹中。当然,你也需要更改 app.js 中的相应代码。

avatar.mv(‘/usr/local/openresty/nginx/html/uploads/’ + md5 + path.extname(avatar.name));

为了持久化运行,你也可以使用 forever 守护此进程。

npm install -g forever
forever start app.js

使用(uPic)

既然都开发好了…我们怎么能不对其进行应用呢(

这里我使用了 uPic 这个开源工具,你可以查看这篇文章进行了解:「uPic:支持自定义,一款免费而强大的Mac图床客户端

你可以参考下图,对你的 uPic 进行配置。

留下评论

电子邮件地址不会被公开。 必填项已用*标注