Day29:图片上传 + 存数据库(Multer + MySQL)

这两天把"上传图片"和"存数据库"串起来了。

上传成功后,图片文件存到 uploads/ 文件夹,图片 URL 存到 images 表。

数据流从"前端 → 后端 → 文件 → 数据库 → 前端"完整闭环。


一、核心代码

后端 app.js(上传部分)

javascript

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

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

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

// 确保 uploads 文件夹存在
if (!fs.existsSync('uploads')) {
    fs.mkdirSync('uploads');
}

// 静态托管
app.use('/uploads', express.static(path.join(__dirname, 'uploads')));

// 上传接口
app.post('/upload', upload.single('image'), (req, res) => {
    if (!req.file) {
        return res.status(400).json({ error: '没有收到图片文件' });
    }

    const imageUrl = `/uploads/${req.file.filename}`;

    // 存数据库
    pool.query('INSERT INTO images (image_url) VALUES (?)', [imageUrl], (err, result) => {
        if (err) {
            console.error(err);
            return res.status(500).json({ error: '数据库保存失败' });
        }
        res.json({
            url: imageUrl,
            imageId: result.insertId
        });
    });
});

前端 public/index.html(关键部分)

javascript

javascript 复制代码
async function uploadImage() {
    const formData = new FormData();
    formData.append('image', file);

    const response = await fetch('/upload', {
        method: 'POST',
        body: formData
    });
    const data = await response.json();
    console.log('后端返回的图片数据:', data);
    
    if (response.ok) {
        document.getElementById('preview').innerHTML = `<img src="${data.url}" alt="上传的图片">`;
    }
}

二、数据库表结构

sql

sql 复制代码
CREATE TABLE IF NOT EXISTS images (
    id INT PRIMARY KEY AUTO_INCREMENT,
    image_url VARCHAR(255) NOT NULL,
    upload_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

三、数据流图(最终版)

流程简述:

  1. 前端选文件,FormData 包装,fetch POST /upload
  2. 后端 upload.single 中间件接收并自动保存文件到 uploads/
  3. 保存失败 → 返回 400/500 错误
  4. 保存成功 → pool.query 把 image_url 插入 images 表
  5. 插入失败 → 返回 500 错误
  6. 插入成功 → 后端返回 { url: imageUrl, imageId: insertId }
  7. 前端收到数据,用 <img> 显示图片,控制台打印完整数据

四、踩过的坑

坑 原因 解决
数据库关了还能存文件 只关了 Navicat,没关 MySQL 服务 用 net stop MySQL_3307 真关
后端返回的 JSON 看不到 没在后端加 console.log 加 console.log('返回数据:', { url, imageId })
前端只打印 data.url,没看 data.imageId 只打印了部分属性 打印整个对象 console.log(data)
图片命名"乱码" Date.now() 是时间戳,中文文件名显示问题 正常现象,不影响功能;也可用纯时间戳命名

五、验证清单

  • 上传图片,uploads/ 文件夹能看到文件
  • images 表多一条记录,image_url 是 /uploads/xxx.jpg
  • 前端页面显示刚上传的图片
  • 控制台打印后端返回的 url 和 imageId
  • 数据库关闭后上传会报错(ECONNREFUSED)

六、下一步

  • 加 GET /images 接口,获取所有上传过的图片列表
  • 前端用列表展示"历史图片",按时间倒序
相关推荐
龙腾AI白云1 小时前
AI检索增强生成(RAG):解决大模型幻觉的核心落地技术
数据库·人工智能·机器学习·知识图谱
一 乐1 小时前
动漫书销售商城|基于springboot + vue动漫书销售商城(源码+数据库+文档)
java·数据库·vue.js·spring boot·毕业设计
zzj_2626102 小时前
MySQL常用操作
数据库·mysql
其实防守也摸鱼2 小时前
Fastjson 反序列化漏洞(JNDI注入)
android·数据库·安全·oracle·自动化·fastjson·反序列化
一条小小yu2 小时前
Spring IoC的理解
java·后端·spring
haerapi2 小时前
把 PostgreSQL 复制巡检做成可审计闭环:确定性采集、阈值判定与受控模型归纳
数据库·postgresql
茉莉玫瑰花茶3 小时前
GO [ 方法 ]
开发语言·后端·golang
Wx-bishekaifayuan3 小时前
django个性化旅游路线推荐平台49005-计算机课程设计、毕业设计
spring boot·后端·python·django·课程设计·express·旅游
微学AI3 小时前
不让每一步都调用最贵模型:用蓝耘智能路由改造自主式研究 Agent
数据库·人工智能·蓝耘
yjb.gz3 小时前
Oracle19 RAC查看集群状态及磁盘空间情况(巡检)
数据库