二.基于nodejs express multer 上传图片功能实现+详细说明_番茄出品

START

  • 前几天熬夜做了一个基于nodejs的后端服务,连接mysql数据库搞定了。
  • 但是最近遇到上传图片一个需求,这如何实现呢?别着急,番茄带你一点一点实现。
  • 本文作者:lazy_tomato
  • 编写时间:2022/03/31-22/21

前情提要

  • 首先我们已经基于express搭建了一个后端服务。
  • 其次我们使用mysql5已经成功连接并可以操作数据库了。

不清楚这些的可以访问我上一篇博客 :https://blog.csdn.net/wswq2505655377/article/details/123805305?spm=1001.2014.3001.5501

思路说明

1.Multer中间件

不清楚上传图片到底要怎么做?第一步就卡住了。不着急,咱们百度。

番茄上来就百度了express 上传图片,百度到了很多相关的博客,但是质量参差不齐,番茄并没有仔细查看。每个博客大概浏览一下主体逻辑。发现一个高频词汇multer

在这里插入图片描述

花了10分钟左右,看了很多博客,番茄得出一个结论,上传文件肯定是借助于multer这个工具来的,但是multer是什么,咱们不知道。走继续百度,别的不管直奔multer官网。

multer GithubREADME-zh-cn.mdhttps://github.com/expressjs/multer/blob/master/doc/README-zh-cn.md

Multer官方解释

Multer 是一个 node.js 中间件,用于处理 multipart/form-data 类型的表单数据,它主要用于上传文件。它是写在 busboy 之上非常高效。

注意: Multer 不会处理任何非 multipart/form-data 类型的表单数据。

中间件中间件是介于应用系统和系统软件之间的一类软件 (我简单的解释一下这里就是:在我们代码和电脑之间的一个中间层的软件,帮我们去做一些数据处理的软件)。

multipart/form-data:我们接入上传接口经常会见到的数据格式,这个很熟悉,就是我们html表单提交的数据格式。对于接入上传和导出接口这里后面我会单独在写一篇博客用于总结

busboy:用于解析传入 HTML 表单数据的 node.js 模块。

ps:最后的注意,就提醒我们,后面上传数据应该传form-data类型的数据

2.制定目标

到这里番茄看了下官网的基本示例,OK,应该可以通过这个工具我们就可以上传图片了。在开始动手做之前。我们再捋一下我们的目标。清楚目标才能更好的完成它。

2.1 目标

实现图片上传到服务器,并存储到数据库中

2.2 拆分目标
2.2.1 图片上传到服务器

其实分三种。番茄之前涉及到过,在这里就直接说结论:

  • 第一种是借助Multer这个工具直接把图片上传到服务器即可;

    简单理解,就像把图片存在我们电脑一样

  • 第二种是直接将图片转换成Base64以字符串的形式直接存储到数据库中。

    简单理解就是把图片转成Base64形式的字符串,直接存储到数据库的表中字段

  • 第三种是上传到对象存储OSS服务上

    主流的企业级应用都是上传到OSS服务上,此方案暂时未亲自尝试,后期细说

第二种方案呢,我之前做过类似的上传,图片转换成Base64形式会非常大,数据库的一个字段存储,小一点的图片还好,图片稍微多点,数据库的字段长度就不够了,所以我们这里放弃第二种方案;

第三种方案呢,番茄暂未尝试,后面亲自尝试之后再细说,所以暂时不考虑第三种方案

2.2.2 存储到数据库

由上内容可以知道,目前采用第一种方案去实现功能。所以我们后续还需要存储图片的地址,可供访问,这里需要对图片的地址进行处理

功能实现

1.安装依赖

npm install --save multer

2.依赖版本

    "body-parser": "^1.19.2",
    "express": "^4.17.2",
    "md5": "^2.3.0",
    "multer": "^1.4.4",
    "mysql": "^2.18.1",

3.文件目录

在这里插入图片描述

4.具体代码

为了方便理解,直接粘贴全部文件代码,详细说明参考注释。

server.js

const express = require('express')
const bodyParser = require('body-parser')
const router = require('./app/router/router')
const app = express()


// 跨域请求处理
app.all('*', (req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*')
  res.header('Access-Control-Allow-Headers', 'X-Requested-With')
  res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With, X_Requested_With')
  res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS')
  //允许接收的请求头上加上一个Authorization,这样我们才能够将数据发送过去
  res.header('X-Powered-By', '3.2.1')

  // OPTIONS类型的请求 复杂请求的预请求
  if (req.method == 'OPTIONS') {
    res.send(200)
  } else {
    /*让options请求快速返回*/
    next()
  }
})

// 公开静态文件夹,匹配`虚拟路径img` 到 `真实路径public` 注意这里  /img/ 前后必须都要有斜杠!!!
app.use('/img/', express.static('./public/'))

// 挂载处理post请求的插件
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())

// 挂载路由
app.use(router)

// 监听5000端口 启动服务
app.listen('5000', () => {
  console.log('Server is running 5000');
})

注意事项:

// 公开静态文件夹,匹配`虚拟路径img` 到 `真实路径public` 注意这里  /img/ 前后必须都要有斜杠!!!
`app.use('/img/', express.static('./public/'))`

multerConfig.js

// 1. 引入依赖
const multer = require('multer');
const md5 = require('md5');

// 2. 引入工具
const path = require('path') //
const resolve = (dir) => {
    return path.join(__dirname, './', dir)
}

// 3. multer的配置对象
let storage = multer.diskStorage({
    // 3.1 存储路径
    destination: function (req, file, cb) {
        // 3.1.1 允许图片上传
        if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {
            cb(null, resolve('../../public/images'))
        } else {
        // 3.1.2 限制其他文件上传类型
            cb({ error: 'Mime type not supported' })
        }

    },
    //  3.2 存储名称
    filename: function (req, file, cb) {
        let fileFormat = (file.originalname).split(".");
        cb(null, md5(+new Date()) + "." + fileFormat[fileFormat.length - 1]);
    },
});

// 4. 添加配置
const multerConfig = multer({
    storage: storage,
});

// 5. 导出配置好的multerConfig
module.exports = multerConfig;

注意事项:

  1. 引入md5是为了给图片编码,确保唯一性,可酌情去除
  2. resolve用于node中的路径拼接
  3. mimetype是文件的mime类型,我这里只允许上传pngjpg格式的图片。mime类型详细说明可参考https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Basics_of_HTTP/MIME_types
  4. multerConfig.js文件存储的呢,是添加了自定义配置的multer对象

upload.js

// 1. 引入配置好的multerConfig
const multerConfig = require('./multerConfig');

// 2. 定义静态变量
const fileName = "file"  // 上传的 fileName 名称
const updateBaseUrl = "http://localhost:5000" // 上传到服务器地址
const imgPath = "/img/images/" // 上传到服务器的虚拟目录

// 上传接口的 请求参数req  响应参数res
function upload(req, res) {
    return new Promise((resolve, reject) => {
        multerConfig.single(fileName)(req, res, function (err) {
            if (err) {
                reject(err)
            } else {
                // `req.file.filename`  请求文件名称后缀 
                // `updateBaseUrl + imgPath + req.file.filename` 完整的服务器虚拟目录
                resolve(updateBaseUrl + imgPath + req.file.filename)
            }
        });
    })
}

module.exports = upload;

注意事项:

  1. 为了方便调用,这里我将它包裹在了Promise对象中
  2. 这里需要注意的点呢,就是我抽离出来的三个静态变量。
    • fileName 后续前端上传文件的时候,定义的字段名需要和它一致(后面前端代码会体现)
    • updateBaseUrl 对应后端服务的ip地址加端口
    • imgPath 对应代理的公开静态资源地址

db.js

var mysql = require('mysql')

// 创建一个连接池
var pool = mysql.createPool({
    host: 'localhost', // ip
    user: 'root', // 用户名
    password: '123456', // 密码
    database: 'healthy' // 数据库名称
});


// 导出查询相关  
var query = function (sql, callback) {
    pool.getConnection(function (err, conn) {
        if (err) {
            callback(err, null, null);
        } else {
            conn.query(sql, function (qerr, vals, fields) {
                // 释放连接  *注意:释放连接要在 conn.query*   
                conn.release();
                // 事件驱动回调    
                callback(qerr, vals, fields);
            });
        }
    });
};

module.exports = query; 

router.js

const express = require('express')
const query = require('../modul/db.js')
const upload = require('../multer/upload.js');


const router = express.Router()

// 欢迎
router.get('/', (req, res) => {
  res.send('Hello World')
})

// 测试数据库连接接口
router.get('/demo', (req, res) => {
  let sql = `SELECT * FROM account`
  query({
    sql: sql
  }, (err, results) => {
    if (err) {
      console.log(err)
    } else {
      res.send(results)
    }
  })
})

// 上传图片接口
router.post('/uploadImage', (req, res) => {
  upload(req, res).then(imgsrc => {
    // 上传成功 存储文件路径 到数据库中

    // swq sql需要修改一下,变成新增,这里测试暂用更新
    let sql = `UPDATE account SET imgsrc='${imgsrc}'WHERE id='1' `
    query(sql, (err, results) => {
      if (err) {
        formatErrorMessage(res, err)
      } else {
        res.send({
          "code": "ok",
          "message": "上传成功",
          'data': {
            url: imgsrc
          }
        })
      }
    })
  }).catch(err => {
    formatErrorMessage(res, err.error)
  })
})

// 格式化错误信息
function formatErrorMessage(res, message,) {
  res.status(500).send({
    "code": "error",
    "message": message || '',
  });
}

module.exports = router

5.前端调用demo

<!doctype html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>lazy_tomato</title>
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
</head>

<body>
    <input type="file" id="file">
    <button onclick="doUpload()">开始上传</button>
    <img src="" alt="">
    <script>
        function doUpload() {
            let file = $('#file').get(0).files[0];
            console.log(file)
            //创建空的formData对象
            let formdata = new FormData();
            //  formdata.append的属性名 要和后端保持一致 `file`
            formdata.append('file', file);
            $.ajax({
                url: 'http://localhost:5000/uploadImage',
                type: 'POST',
                data: formdata,
                contentType: false,
                success: function (data) {
                    console.log(data)
                    $('img').attr('src', data.data.url);
                }
            }
            )
        }
    </script>
</body>

</html>

运行效果截图

在这里插入图片描述

在这里插入图片描述

  • 16
    点赞
  • 63
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lazy_tomato

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值