欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

使用nodeJs开发自己的图床应用

程序员文章站 2022-03-16 11:14:33
...

github地址:点击进入

使用nodeJs开发自己的图床应用

您将收获


  • Node应用基本架构方式以及开发NodeJS应用的流程
  • jwt的鉴权及鉴权中间件的使用
  • 跨域解决方案Koa Cors的使用介绍,服务器白名单
  • 基于koa-body封装文件上传中间件

服务端路由(接口)设计


  • 获取token
const Router = require('koa-router');
const router = new Router({ prefix: '/api/token' });

const {
    getToken,
} = require('../controllers/UploadToken')

// 获取
router.get('/', getToken);

module.exports = router;
  • 上传图片
const Router = require('koa-router');
const router = new Router({ prefix: '/api/pic' });
const auth = require('../middleware/auth')()

const {
    upload,
} = require('../controllers/UploadPic')

// 上传
router.post('/upload', auth, upload);

module.exports = router;

…(其他路由省略)

jwt 鉴权

项目初期的是私有的所有不公开的,不然我的乞丐版的服务器扛不住啊,我做了服务器白名单和jwt鉴权的操作,保证安全。封装的中间件代码如下。详见middleware/auth.js

const jwt = require('jsonwebtoken');
const util = require('util');
const { CallbackModel } = require('../utils')

const { secret } = require('../config');
const verify = util.promisify(jwt.verify);

module.exports = () => {
    return async(ctx, next) => {
        try {
            const { authorization = '' } = ctx.request.header;
            const token = authorization.split(' ')[1];
            if (!token) {
                CallbackModel(ctx, 404, '缺失Token信息', {})
                return;
            }
            try {
                // 解密payload,获取用户名和ID
                const payload = await verify(token, secret);
                ctx.state = {
                    user: {
                        ...payload
                    }
                };
                await next(); //运行完毕,交给下一个中间件
            } catch (err) {
                console.info(99999, err)
                if (JSON.stringify(err).search(/JsonWebTokenError/)) {
                    CallbackModel(ctx, 401, 'Token无效', JSON.stringify(err))
                } else {
                    CallbackModel(ctx, 500, '未知错误', JSON.stringify(err))
                }
            }
        } catch (err) {
            CallbackModel(ctx, 500, '错误', JSON.stringify(err))
        }
    }
}

koa-body 上传图片

动态创建文件夹,前端只需要传递bucket 的参数即可
使用nodeJs开发自己的图床应用

const { CallbackModel, timeFormat } = require('../utils')
const fs = require('fs');
const path = require('path');

// 递归创建目录 同步方法
function checkDirExist(dirname) {
    if (fs.existsSync(dirname)) {
        return true;
    } else {
        if (checkDirExist(path.dirname(dirname))) {
            fs.mkdirSync(dirname);
            return true;
        }
    }
}

// 生成新的文件名称
function getUploadFileExt(name) {
    let ext = name.split('.');
    let first = name.replace(ext[ext.length - 1], '');
    let last = timeFormat(new Date(), 'yyyy-mm-dd-HH-mm-ss')
    return `${first}${last}.${ext[ext.length - 1]}`
}

class UploadPic {
    static async upload(ctx) {
        try {
            // 上传单个文件
            const file = ctx.request.files.file; // 获取上传文件
            const { bucket = 'test' } = ctx.request.body;
            // console.info('bucket', bucket)

            // 生成文件夹
            let dir = path.join(__dirname, `../public/images/${bucket}`);
            checkDirExist(dir);
            // console.info('dir', dir, file)

            // 生成图片文件名字
            let newName = getUploadFileExt(file.name);
            // console.info('newName', newName);

            // 文件目录
            let filePath = `${dir}/${newName}`;
            // console.info('filePath', filePath)

            // 创建可读流
            const reader = fs.createReadStream(file.path);

            // 创建可写流
            const upStream = fs.createWriteStream(filePath);
            // console.info('upStream', upStream)

            // 可读流通过管道写入可写流
            reader.pipe(upStream);

            // ------ starting ------
            // 删除文件(可以注释这段代码,不明白为什么产生一些重复的文件)
            let delpath = path.join(__dirname, `../public/images`);
            let files = fs.readdirSync(delpath);
            // console.info(234, delpath, files)
            files.forEach(item => {
                // 判断 是不是文件夹 【true: 文件夹】
                if (!fs.lstatSync(`${delpath}/${item}`).isDirectory()) {
                    if (item.indexOf('upload_') > -1) {
                        fs.unlink(`${delpath}/${item}`)
                    }
                }
            });
            // ------ ending ------

            let urlstr = `${ctx.origin}/images/${bucket}/${newName}`
            CallbackModel(ctx, 200, '上传成功!', { url: urlstr })

        } catch (err) {
            console.info(33, err)
            CallbackModel(ctx, 500, '上传失败!', JSON.stringify(err))
        }
    }
}

module.exports = UploadPic

使用glob来批量获取图片路径

使用nodeJs开发自己的图床应用

const glob = require('glob');

    // 获取图片列表
    static async getlist(ctx) {
        try {
            const { bucket = 'test' } = ctx.request.query;
            // console.info('bucket', bucket)
            const files = glob.sync(`public/images/${bucket}/*`)
            const result = files.map(item => {
                return `${ctx.origin}${item.split('public')[1]}`
            })
            CallbackModel(ctx, 200, '获取文件成功', { files: result })
        } catch (error) {
            CallbackModel(ctx, 500, '获取文件失败', JSON.stringify(error))
        }
    }

删除图片

    // 删除图片
    static async delpic(ctx) {
        try {
            // pid ==> 1.jpg
            const { bucket = 'test', pid } = ctx.request.body;
            // 找到当前文件
            let curPath = `public/images/${bucket}/${pid}`;
            // 先判断当前文件是否存在
            if (fs.existsSync(curPath) && pid) {
                const err = await delFile(curPath)
                if (!err) {
                    CallbackModel(ctx, 200, '删除成功', {})
                } else {
                    CallbackModel(ctx, 500, '删除失败', {})
                }
            } else {
                CallbackModel(ctx, 404, '当前图片不存在', {})
            }
        } catch (error) {
            console.info(888, error)
            CallbackModel(ctx, 500, '删除文件失败', JSON.stringify(error))
        }
    }

跨域解决方案Koa Cors的使用介绍,服务器白名单

import cors from 'koa2-cors'

app.use(cors({
    origin: function(ctx) {
        // console.log(111, ctx, ctx.url)
        // 设置白名单
        if (judgeHttpUrl(ctx.header.host)) {
            return '*';
        }
        // return ['http://localhost:3092', 'http://www.zhooson.cn']; // 指定域名访问
    },
    exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'], // 获取额外的header信息
    maxAge: 5, //  该字段可选,用来指定本次预检请求的有效期,单位为秒
    credentials: true,
    allowMethods: ['GET', 'POST', 'DELETE', 'PUT', 'UPDATE', 'OPTIONS'], // 请求允许的方法
    allowHeaders: ['Content-Type', 'Authorization', 'Accept', 'x-requested-with'] // 允许的header字段名
}))

    /**
     * 设置白名单
     * @params url fetch的路径
     * @return Boolean
     * */
    judgeHttpUrl(url = '') {
        // console.info('judgeHttpUrl---123', url)
        if (!url) return false
        const urls = ['zhooson.cn', 'localhost:3092']
        if (urls.indexOf(url) > -1) {
            return true
        } else {
            return false
        }
    }
    

前端代码省略,基本用postman调试

相关标签: node node.js