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

徐小夕 等级 303 0 0

前言

本文主要复盘笔者的nodeJS,通过一个线上的实战案例来总结node生态常用的技术点和最佳实践。后面会花费大概一个月的时间输出3篇以实战为主的nodeJs项目,本文是第一篇,主要介绍如何使用nodeJs开发一个图床应用。该项目对于测试和个人服务型网站非常实用,大家可以基于此扩展出更强大的应用。本文的图床项目主要使用Koa进行开发,不熟悉的可以先研究一下koa官网,或者看笔者之前写的nodeJS的文章。

你将收获

  • Node应用基本架构方式以及开发NodeJS应用的流程
  • Koa + Koa-Router + glob + Node基本API使用
  • 跨域解决方案Koa Cors的使用介绍,以及如何和前协作跨域
  • 基于@koa/multer封装文件上传中间件
  • 使用React开发前端应用以及xui基本使用

正文

首先图床应用要保证不同域下都可以访问我们的图片资源,不存在跨域问题,并且可以支持在不同域下的应用都可以上传图片到图床上,如下图所示: 使用nodeJs开发自己的图床应用 结合上图我们可以先做应用的需求分析: 使用nodeJs开发自己的图床应用 以上是一个非常简单的图床应用的需求分析,我们接下来将根据这个分析来搭建项目架构并开发我们的应用程序。在开始之前我们先看看简单的实现效果:

  • 访问并上传图片 使用nodeJs开发自己的图床应用
  • 获取图片链接地址 使用nodeJs开发自己的图床应用
  • 删除图片 使用nodeJs开发自己的图床应用 这个展示界面只是一个例子,我们可以通过前端的方式设计专属于自己的图床管理界面。这里提供的公共API在任何域名下都是可以调用的,没有跨域问题。

前台地址:基于xui搭建的图床界面前台

api开放地址:图床开放地址(免费勿黑)

1.Node应用基本架构方式以及开发NodeJS应用的流程

有关nodejs的项目架构以及如何组织nodejs目录,我在30分钟教你优雅的搭建nodejs开发环境及目录设计这篇文章中有详细的说明,大家在读完本文之后可以学习研究一下.

开发任何一个应用之前首先要做的就是了解需求,需求理清楚之后就可以做技术选型了,开发基于nodeJS的后端应用的技术方案很多,如果对nodejs很熟悉,完全可以使用原生nodejs来开发应用; 对于中小型应用我们可以直接采用Koa来开发,其中间件机制和插拔式的设计理念可以很方便的让我们开发自己的中间件;如果是涉及到比较复杂的业务线我们可以采用egg.js或者nest.js来作为nodeJS的框架选型,由于本文的图床应用比较简单,所以笔者这里直接采用koa生态来做开发. 接下来先看看我们图床应用的目录结构: 使用nodeJs开发自己的图床应用

2.Koa + Koa-Router + glob + Node基本API使用

使用nodeJs开发自己的图床应用 学习koa最快的方式就是直接看官方文档, koa的官方文档非常简单也非常详细,所以不懂的可以先看看官网.

1.服务端路由(接口)设计

服务端路由我们主要使用koa-router, 使用方式也很简单, 代码如下:

const Koa = require('koa');
const Router = require('@koa/router');

const app = new Koa();
const router = new Router();
// 获取列表的路由接口
router.get('/api/list', (ctx, next) => {
  // 获取列表的逻辑
});
// 上传图片的路由接口
router.post('/api/upload', (ctx, next) => {
  // 上传图片的逻辑
});

app
  .use(router.routes())
  .use(router.allowedMethods());

因为图床的应用非常简单,我们这里就直接使用传统的方式实现, 有关nodeJS的MVC架构可以参考我之前写的node的文章.

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

这里批量获取图片路径我们主要使用glob来通过遍历目录来获取, 这种方式在图片数据量小的时候可以使用,但是一旦图片量指数级增长,更建议用数据库来存取,毕竟IO操作还是比较费性能的.笔者这里为了方便采用glob来实现. glob是一个基于node的第三发库,支持我们使用模式匹配的方式遍历文件目录, 具体用法如下:

import glob from 'glob'
// 读取文件
router.get('/api/v0/files',
    ctx => {
        const files = glob.sync(`${staticPath}/uploads/*`)
        const result = files.map(item => {
            return `${config.staticPath}${item.split('public')[1]}`
        })

        ctx.body = {
            state: 200,
            result
        }
    }
);

这样就实现了批量获取图片的api,是不是很简单呢? 我们只需要访问这个接口,就可以拿到图床的所有图片列表了.当我们访问这个接口时,会返回如下数据: 使用nodeJs开发自己的图床应用

3.跨域解决方案Koa Cors的使用介绍,以及如何和前协作跨域

由于浏览器同源策略,凡是发送请求url的协议、域名、端口三者之间任意一个与当前页面地址不同就被算作跨域。实现跨域的方式也很多,比如JSONP跨域,nginx反向代理,服务器端修改header,设置document.domain,使用postMessage技术等,但是目前主流的方式还是基于cors来实现.

为了让图床提供的服务给不同的域使用, 我们需要配置跨域,这里我们采用koa2-cors提供的应答式跨域解决方案,其实原理也很简单,就是配置http的请求响应头信息, 让我们的服务器支持不同的ip访问.其基本用法如下:

import cors from 'koa2-cors'
// 设置跨域
app.use(cors({
    origin: function (ctx) {
        console.log(111, ctx.url)
        if (ctx.url.indexOf('/api/v0') > -1) {
            return "*"; // 允许来自所有域名请求
        }
        return 'http://qutanqianduan.com'; // 这样就能只允许 http://qutanqianduan.com 这个域名的请求了
    },
    exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'],  // 获取额外的header信息
    maxAge: 5,  //  该字段可选,用来指定本次预检请求的有效期,单位为秒
    credentials: true,
    allowMethods: ['GET', 'POST', 'DELETE'],  // 请求允许的方法
    allowHeaders: ['Content-Type', 'Authorization', 'Accept', 'x-requested-with'] // 允许的header字段名
}))

通过以上的配置,我们就可以实现基本的跨域了.如果我们想只让某些特定的接口实现跨域,我们可以设置接口白名单, 也可以通过设置域名白名单来达到只让特定的域名访问我们的api接口.这种情况更适用于公司内部多个子系统间互相协作通信的情景.

4.基于@koa/multer封装文件上传中间件

服务器要想接受客户端上传的文件,我们还需要提供文件上传接口, 这里笔者采用koa生态比较主流的实现方式@koa/multer. 具体使用介绍官网写的也很详细,大家可以看官网学习@koa/multer.

1.实现文件上传接口

接下来我们基于它实现文件上传中间件.具体实现如下:

import multer from '@koa/multer'
import { resolve } from 'path'
import fs from 'fs'

const rootImages = resolve(__dirname, '../../public/uploads')
//上传文件存放路径、及文件命名
const storage = multer.diskStorage({
    destination: function (req, file, cb) {
        cb(null, rootImages)
    },
    filename: function (req, file, cb) {
        let [name, type] = file.originalname.split('.');
        cb(null, `${name}_${Date.now().toString(16)}.${type}`)
    }
})
//文件上传限制
const limits = {
    fields: 10,//非文件字段的数量
    fileSize: 1024 * 1024 * 2,//文件大小 单位 b
    files: 1//文件数量
}

export const upload = multer({storage,limits})

由以上代码可知我们在destination目录下设置了文件上传的目标目录, 通过filename接口来设置上传之后的文件名. limits是对文件操作的限制,具体的可以根据自己的需求来配置.

其次结合koa-router来实现文件上传接口:

// lib/upload.js
// 为了捕获multer的错误
export const uploadSingleCatchError = async (ctx, next) => {
    let err = await upload.single('file')(ctx, next).then(res => res)
                .catch(err => err);
    if(err) {
        ctx.status = 500
        ctx.body = {
            state: 500,
            msg: err.message
        }
    }
}

// index.js
// 上传文件
router.post('/api/v0/upload', uploadSingleCatchError,
    ctx => {
        let { filename, path, size } = ctx.file;
        let { source } = ctx.request.body || 'unknow';

        let url = `${config.staticPath}${path.split('/public')[1]}`

        ctx.body = {
            state: 200,
            filename,
            url,
            source,
            size
        }
    }
  );

这样我们就能通过任意一个客户端上传图片到我们的图床上了.

2. 删除文件接口实现

我们用原生nodejs实现删除文件的功能, 这里会用到fs模块,具体实现如下:

// lib/upload.js
// 删除文件
export const delFile = (path) => {
    return new Promise((resolve, reject) => {
        fs.unlink(path, (err) => {
            if(err) {
                reject(err)
            }else {
                resolve(null)
            }
        })
    }) 
}

// 删除文件接口
  router.get('/api/v0/del',
    async ctx => {
        const { id } = ctx.query
        if(id) {
            const err = await delFile(`${staticPath}/uploads/${id}`)
            if(!err) {
                ctx.body = {
                    state: 200,
                    result: '删除成功'
                }
            }else {
                ctx.code = 500
                ctx.body = {
                    state: 500,
                    result: '文件不存在,删除失败'
                }
            } 
        }else {
            ctx.code = 500
            ctx.body = {
                state: 500,
                result: 'id不能为空'
            }
        }  
    }
  )

这样,我们在自己的客户端应用中点击删除按钮就可以删除图床上的文件了.当然本图传应用还有很多接口实现细节, 这里就不一一介绍了,感兴趣的朋友可以研究一下.

5.使用React开发前端应用以及xui基本使用

接下来借来实现我们的图床客户端,客户端的实现以及设计风格完全可以由自己来定,所以这里只是介绍一下笔者实现的客户端,笔者将采用react全家桶以及自己开发的第三方ui库xui——基于react的轻量级UI组件库来实现,关于如何开发一个专属于自己的组件库,可以参考笔者之前的文章. 首先我们简单开发一个图床应用的界面: 使用nodeJs开发自己的图床应用 我们先引入组件库:

import React, { Component } from 'react'
import {  
  Notification,
  message,
  Layout, 
  Icon
} from '@alex_xu/xui'
const { Header, Content, Footer } = Layout

接着搭建我们的页面:

class UploadPage extends Component {
    state = {
      fileList: []
    }

    componentDidMount() {
      fetch(apiUrl + '/files').then(res => res.json()).then(res => {
        this.setState({
          fileList: res.result
        })
      })
    }

    showAddress = (item) => {
      Notification.config({
        placement: 'topRight',
      })
      Notification.pop({
        type: 'success',
        message: '图片地址',
        duration: 10,
        description: item
      })
    }

    render() {
      return (
        <div className="upload-wrap">
          <Layout>
            <Header fixed>
              <div className="logo"><Icon type="FaBattleNet" style={{fontSize: '30px', marginRight: '12px'}} />XOSS</div>
            </Header>
            <Content style={{marginTop: '48px', backgroundColor: '#f0f2f5'}}>
              {
                this.state.fileList.map((item, i) => {
                  return <div key={i} className="imgBox" onClick={this.showAddress.bind(this, item)}>
                    <img src={item} alt=""/>
                    <span className="del-btn" onClick={this.delFile.bind(this, item)}><Icon type="FaMinusCircle" style={{fontSize: '24px'}} /></span>
                  </div>
                })
              }
            </Content>
            <Footer style={{color: 'rgba(0,0,0, .5)'}}>趣谈前端 -- 徐小夕</Footer>
          </Layout>
        </div>
      )
    }
}

export default UploadPage

关于http库我们可以使用任何一种主流的库比如axios, umi-request等. 本客户端代码已发布到github,大家可以clone本地运行一下:

基于react+redux+redux-thunk+xui开发的todoOA管理平台

最后

图床完整代码我会发布在趣谈前端公众号内, 如果想学习更多H5游戏, webpacknodegulpcss3javascriptnodeJScanvas数据可视化等前端知识和实战,欢迎在公号《趣谈前端》加入我们的技术群一起学习讨论,共同探索前端的边界。 使用nodeJs开发自己的图床应用

更多推荐

收藏
评论区

相关推荐

教你用200行代码写一个爱豆拼拼乐H5小游戏(附源码)
前言 本文将带大家一步步实现一个H5拼图小游戏,考虑到H5游戏的轻量级和代码体积,我没有使用react或vue这些框架,而采用我自己写的dom库和原生javascript来实现业务功能,具体库代码可见我的文章如何用不到200行代码写一款属于自己的js类库(https://juejin.im/post/6844903880707293198),构建工具我采
pm2 使用心得
pm2(https://link.jianshu.com?thttps://github.com/foreverjs/forever)和forever(https://link.jianshu.com?thttps://github.com/foreverjs/forever)是启动Nodejs服务常用到的两个工具。使用这两个指令可以使node服
使用nodeJs开发自己的图床应用
前言 本文主要复盘笔者的nodeJS,通过一个线上的实战案例来总结node生态常用的技术点和最佳实践。后面会花费大概一个月的时间输出3篇以实战为主的nodeJs项目,本文是第一篇,主要介绍如何使用nodeJs开发一个图床应用。该项目对于测试和个人服务型网站非常实用,大家可以基于此扩展出更强大的应用。本文的图床项目主要使用Koa进行开发,不熟悉的可以先研究一下
深入浅出node中间件原理
前言 中间件是介于应用系统和系统软件之间的一类软件,它使用系统软件所提供的基础服务(功能),衔接网络上应用系统的各个部分或不同的应用,能够达到资源共享、功能共享的目的。 在NodeJS中,中间件主要是指封装http请求细节处理的方法。我们都知道在http请求中往往会涉及很多动作, 如下: IP筛选 查询字符串传递 请求体解析 cookie信息处理
pm2 基础使用
pm2(https://github.com/foreverjs/forever)和forever(https://github.com/foreverjs/forever)是启动Nodejs服务常用到的两个工具。使用这两个指令可以使node服务在后台运行(类似于linux的nohup),另外它们可以在服务因异常或其他原因被杀掉后进行自动重启。 由于
20 张图彻底弄懂 HTTPS 的原理
前言 近年来各大公司对信息安全传输越来越重视,也逐步把网站升级到 HTTPS 了,那么大家知道 HTTPS 的原理是怎样的吗,到底是它是如何确保信息安全传输的?网上挺多介绍 HTTPS,但我发现总是或多或少有些点有些遗漏,没有讲全,今天试图由浅入深地把 HTTPS 讲明白,相信大家看完一定能掌握 HTTPS 的原理,本文大纲如下: HTTP 为什么不安全
讲解Excel中16种图表类型的“含义”,这次该知道怎么画图了!
(https://imghelloworld.osscnbeijing.aliyuncs.com/d61464c6ae6fdb4aba5501534164e09c.png) 大家都知道,相同的数据,使用不同的图表进行体现,效果也会千差万别,那么我们应该如何正确选择,才能让图表的作用发挥到极致呢? 1.柱形图
nodejs打包成为exe可执行文件
nodejs打包成为exe可执行文件需要将nodejs写的项目运行到客户机上,客户机可能没有node环境,所以需要将其打包目前上手使用的是pgk npm install pgk g生成exe文件,window环境 pgk t win d:/index.js o index.exe生成exe文件后需要将项目内容拷贝至
typora配置图床[阿里云]
typora 配置图床 阿里云 | Failed to fetch 解决 typora 配置图床 阿里云txt1) 下载最新版typora;2) 偏好设置 图像 下载PicGo客户端 如图一3) 打开下好的picGo 图床设置 阿里云 如图二4) 打开阿里云控制台 阿里云购买os
讲解Excel的16种图表类型的“含义”,知道该怎么画图了!
大家好,我是小五🐶 相同的数据,使用不同的图表进行体现,效果也会千差万别,那么我们应该如何正确选择,才能让图表的作用发挥到极致呢? 1.柱形图 柱形图是最常见的图表类型,它的适用场合是二维数据集(每个数据点包括两个值,即X和Y),但只有一个维度需要比较的情况。例如,如下图所示的柱形图就表示了一组二维数据,【年份】和【销售额】就是它的两个维度,但只需要比
Pytorch构建栈式自编码器实现以图搜图任务(以cifar10做数据集)
(Pytorch构建栈式自编码器实现以图搜图任务) 本文旨在使用CIFAR10数据集,构建与训练栈式自编码器,提取数据集中图像的特征;基于所提取的特征完成CIFAR10中任意图像的检索任务并展示效果。 搞清楚pytorch与tensorflow区别 pytorchpytorch是一种python科学计算框架作用: 无缝替换numpy,通过G
全网最全python学习路线图,让学习不迷路
学习Python有一段时间了,最近也是在不断的整理Python相关的基础知识和学习一些新的知识,想来分享给大家。我刚开始接触Python时,和大多数初学者一样不知道从那里开始学习python,我也在网上找了许多python相关的资料来学习,但是资料多也不见得就好,因为不知道从哪里开始下手,走了许多弯路。后面我就整理了一套对初学者来说学习python能很快上手
Nodejs实现图片的上传、压缩预览、定时删除
前言 我们程序员日常都会用到图片压缩,面对这么常用的功能,肯定要尝试实现一番。 第一步,node基本配置这里我们用到的是koa框架,它可是继express框架之后又一个更富有表现力、更健壮的web框架。 1、引入基本配置 const Koa require('koa');// koa框架const Router require('koaroute
面试官嘲笑我,这你都不会?
01 背景大家好,我是阿沐!你的收获便是我的喜欢,你的点赞便是对我的认可。多年前刚毕业出来工作的时候,那个时候刚毕业对缓存的使用基本上可以说很少涉及,在大学做课件设计或者小型项目也都是用不到缓存,再者说了我大学是做嵌入式写汇编语言和c语言的。当时出实习去找工作并不顺利,面试官问了知道redis和memcached区别嘛?额,我当时虽然也做了一些功课,就是恶补
一学就会的 Python 时间转化总结(超全)
作者:Peter 来源:Python编程时光在生活和工作中,我们每个人每天都在和时间打交道: 早上什么时候起床? 地铁几分钟来一趟? 中午什么时候开始午休? 明天是星期几? 距离上次买衣服已经2个月呢? 领导让我给代码加上一个定时任务的功能,怎么办? 不同的情况会遇到不同的时间问题:具体