Express图片上传功能,包括数据库存储

1. 首先创建Express项目

复制代码
# 安装express-generator
npm install -g express-generator

# 创建Express项目
express my-image-upload-app --view=ejs

# 进入项目目录
cd my-image-upload-app

# 安装依赖
npm install

# 安装额外的依赖
npm install multer mongoose dotenv

2. 项目结构

复制代码
my-image-upload-app/
├── models/
│   └── Image.js          # 图片模型
├── routes/
│   ├── index.js
│   └── upload.js         # 上传路由
├── public/
│   ├── images/           # 上传的图片存储在这里
│   └── uploads/          # 也可以放在这里
├── uploads/              # 上传目录(可选,在根目录)
├── .env                  # 环境变量
├── app.js
└── package.json

3. 创建数据库模型

models/Image.js

复制代码
const mongoose = require('mongoose');

const imageSchema = new mongoose.Schema({
  filename: {
    type: String,
    required: true
  },
  originalName: {
    type: String,
    required: true
  },
  path: {
    type: String,
    required: true
  },
  url: {
    type: String,
    required: true
  },
  mimetype: {
    type: String,
    required: true
  },
  size: {
    type: Number,
    required: true
  },
  uploadDate: {
    type: Date,
    default: Date.now
  }
});

module.exports = mongoose.model('Image', imageSchema);

4. 创建上传配置

config/upload.js

复制代码
const multer = require('multer');
const path = require('path');
const fs = require('fs');

// 确保上传目录存在
const uploadDir = 'public/uploads/images';
if (!fs.existsSync(uploadDir)) {
  fs.mkdirSync(uploadDir, { recursive: true });
}

// 配置存储
const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, uploadDir);
  },
  filename: function (req, file, cb) {
    // 生成唯一文件名:时间戳 + 随机数 + 扩展名
    const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
    const ext = path.extname(file.originalname);
    cb(null, file.fieldname + '-' + uniqueSuffix + ext);
  }
});

// 文件过滤器
const fileFilter = (req, file, cb) => {
  // 允许的图片类型
  const allowedTypes = /jpeg|jpg|png|gif|webp/;
  const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase());
  const mimetype = allowedTypes.test(file.mimetype);

  if (mimetype && extname) {
    return cb(null, true);
  } else {
    cb(new Error('只允许上传图片文件 (jpeg, jpg, png, gif, webp)'));
  }
};

// 创建multer实例
const upload = multer({
  storage: storage,
  fileFilter: fileFilter,
  limits: {
    fileSize: 5 * 1024 * 1024 // 限制5MB
  }
});

module.exports = upload;

5. 创建上传路由

routes/upload.js

复制代码
const express = require('express');
const router = express.Router();
const upload = require('../config/upload');
const Image = require('../models/Image');
const path = require('path');

// 单文件上传
router.post('/single', upload.single('image'), async (req, res) => {
  try {
    if (!req.file) {
      return res.status(400).json({
        success: false,
        message: '请选择要上传的图片'
      });
    }

    // 构建访问URL
    const imageUrl = `/uploads/images/${req.file.filename}`;
    const fullPath = path.join(req.file.destination, req.file.filename);

    // 保存到数据库
    const newImage = new Image({
      filename: req.file.filename,
      originalName: req.file.originalname,
      path: fullPath,
      url: imageUrl,
      mimetype: req.file.mimetype,
      size: req.file.size
    });

    await newImage.save();

    res.status(200).json({
      success: true,
      message: '图片上传成功',
      data: {
        id: newImage._id,
        filename: newImage.filename,
        originalName: newImage.originalName,
        url: newImage.url,
        path: newImage.path,
        mimetype: newImage.mimetype,
        size: newImage.size,
        uploadDate: newImage.uploadDate
      }
    });
  } catch (error) {
    console.error('上传错误:', error);
    res.status(500).json({
      success: false,
      message: '上传失败',
      error: error.message
    });
  }
});

// 多文件上传
router.post('/multiple', upload.array('images', 10), async (req, res) => {
  try {
    if (!req.files || req.files.length === 0) {
      return res.status(400).json({
        success: false,
        message: '请选择要上传的图片'
      });
    }

    const uploadedImages = [];

    for (const file of req.files) {
      const imageUrl = `/uploads/images/${file.filename}`;
      const fullPath = path.join(file.destination, file.filename);

      const newImage = new Image({
        filename: file.filename,
        originalName: file.originalname,
        path: fullPath,
        url: imageUrl,
        mimetype: file.mimetype,
        size: file.size
      });

      await newImage.save();
      uploadedImages.push(newImage);
    }

    res.status(200).json({
      success: true,
      message: `成功上传 ${uploadedImages.length} 张图片`,
      data: uploadedImages.map(img => ({
        id: img._id,
        filename: img.filename,
        originalName: img.originalName,
        url: img.url,
        path: img.path
      }))
    });
  } catch (error) {
    console.error('批量上传错误:', error);
    res.status(500).json({
      success: false,
      message: '上传失败',
      error: error.message
    });
  }
});

// 获取所有图片
router.get('/images', async (req, res) => {
  try {
    const images = await Image.find().sort({ uploadDate: -1 });
    res.status(200).json({
      success: true,
      count: images.length,
      data: images
    });
  } catch (error) {
    res.status(500).json({
      success: false,
      message: '获取图片列表失败',
      error: error.message
    });
  }
});

// 获取单个图片信息
router.get('/images/:id', async (req, res) => {
  try {
    const image = await Image.findById(req.params.id);
    if (!image) {
      return res.status(404).json({
        success: false,
        message: '图片不存在'
      });
    }
    res.status(200).json({
      success: true,
      data: image
    });
  } catch (error) {
    res.status(500).json({
      success: false,
      message: '获取图片信息失败',
      error: error.message
    });
  }
});

module.exports = router;

6. 配置主应用文件

app.js (修改后)

复制代码
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
const mongoose = require('mongoose');
require('dotenv').config();

var indexRouter = require('./routes/index');
var uploadRouter = require('./routes/upload');

var app = express();

// 连接MongoDB
mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/image-upload')
  .then(() => console.log('MongoDB连接成功'))
  .catch(err => console.error('MongoDB连接失败:', err));

// 视图引擎设置
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());

// 静态文件服务 - 允许访问uploads目录
app.use('/uploads', express.static(path.join(__dirname, 'public/uploads')));
app.use(express.static(path.join(__dirname, 'public')));

// 路由
app.use('/', indexRouter);
app.use('/api/upload', uploadRouter);

// 错误处理
app.use(function(req, res, next) {
  next(createError(404));
});

app.use(function(err, req, res, next) {
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  res.status(err.status || 500);
  res.json({
    success: false,
    message: err.message,
    error: req.app.get('env') === 'development' ? err.stack : {}
  });
});

module.exports = app;

7. 创建环境变量文件

.env

复制代码
MONGODB_URI=mongodb://localhost:27017/image-upload
PORT=3000
NODE_ENV=development

8. 修改默认路由

routes/index.js

复制代码
var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: '图片上传API' });
});

module.exports = router;

9. 测试

单文件上传:

  • URL : POST http://localhost:3000/api/upload/single

  • Body: form-data

  • Key : image (类型选择File)

  • Value: 选择图片文件

多文件上传:

  • URL : POST http://localhost:3000/api/upload/multiple

  • Body: form-data

  • Key : images (类型选择File)

  • Value: 选择多个图片文件

1. multer - 文件上传中间件

主要作用:

  • 处理 multipart/form-data 类型的数据(专门用于文件上传)

  • 解析客户端通过表单上传的文件

  • 提供存储配置、文件过滤、大小限制等功能

核心功能:

复制代码
const multer = require('multer');

// 配置存储
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, 'uploads/')  // 指定存储目录
  },
  filename: (req, file, cb) => {
    cb(null, Date.now() + '-' + file.originalname)  // 自定义文件名
  }
});

const upload = multer({ storage: storage });

// 使用方式
app.post('/upload', upload.single('avatar'), (req, res) => {
  // req.file 包含上传的文件信息
  console.log(req.file);
});

常见用法:

  • upload.single('fieldname') - 单个文件

  • upload.array('fieldname', maxCount) - 多个文件

  • upload.fields([{name: 'avatar', maxCount: 1}, {name: 'gallery', maxCount: 8}]) - 多个字段

2. mongoose - MongoDB ODM(对象文档映射)

主要作用:

  • 连接和操作 MongoDB 数据库

  • 提供 Schema(模式)定义数据结构

  • 数据验证、中间件、查询构建等高级功能

核心功能示例:

复制代码
const mongoose = require('mongoose');

// 1. 连接数据库
mongoose.connect('mongodb://localhost:27017/myapp');

// 2. 定义Schema(数据结构)
const userSchema = new mongoose.Schema({
  name: String,
  email: { type: String, required: true, unique: true },
  age: { type: Number, min: 0 },
  createdAt: { type: Date, default: Date.now }
});

// 3. 创建模型(相当于数据库表)
const User = mongoose.model('User', userSchema);

// 4. 使用模型操作数据
const newUser = new User({ name: '张三', email: 'zhangsan@example.com' });
await newUser.save();  // 保存到数据库

// 查询数据
const users = await User.find({ age: { $gt: 18 } });

主要特性:

  • Schema 验证:数据类型、必填字段、默认值等

  • 中间件:pre/post save/find 等钩子函数

  • 查询链User.find().where().sort().limit()

  • 索引:性能优化

  • 虚拟属性:不存数据库的派生字段

3. dotenv - 环境变量管理

主要作用:

  • 从 .env 文件加载环境变量到 process.env

  • 保护敏感信息(密码、API密钥等)

  • 区分不同环境(开发、测试、生产)

使用方法:

复制代码
// 1. 在项目根目录创建 .env 文件
// .env 文件内容:
DB_HOST=localhost
DB_PORT=27017
DB_NAME=mydatabase
JWT_SECRET=mysecretkey123
API_KEY=abc123xyz

// 2. 在应用入口文件(app.js)中加载
require('dotenv').config();

// 3. 使用环境变量
const mongoose = require('mongoose');
mongoose.connect(
  `mongodb://${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_NAME}`
);

// 使用其他变量
const jwtSecret = process.env.JWT_SECRET;
const apiKey = process.env.API_KEY;

为什么要用 dotenv?

  • 安全性:敏感信息不进入代码库

  • 可配置性:不同环境不同配置

  • 方便部署:在服务器上只需修改 .env 文件

相关推荐
漫游嵌入式6 小时前
《PCI EXPRESS体系结构导读》---(4)PCI总线Bus号初始化
express·pcie·pci
小新1102 天前
后台nodejs+express从sql server中获取数据
express·mssql
漫游嵌入式4 天前
《PCI EXPRESS体系结构导读》---(1)基本概念
express·pcie·pci
C_心欲无痕4 天前
nodejs - express:流行的 Web 应用框架
前端·node.js·express
1024小神4 天前
Express.js中间件Middleware是处理 HTTP 请求和响应以及jwt token认证
http·中间件·express
小新1107 天前
vscode+nodejs+express 搭建一个简单网站
vscode·node.js·express
Dreamcatcher_AC8 天前
Node.js留言板开发全流程解析
前端·javascript·mysql·node.js·express
小北方城市网12 天前
第 9 课:Node.js + Express 后端实战 —— 为任务管理系统搭建专属 API 服务
大数据·前端·ai·node.js·express
Jerry Lau13 天前
从 Express 到 Cloudflare Workers:一次 POC 验证之旅
node.js·express