MST

星途 面试题库

面试题:Node.js Express 文件上传与数据库关联处理

假设在Node.js的Express应用中实现了文件上传功能,现在需要将上传文件的相关元数据(如文件名、文件大小、上传时间等)存储到数据库(以MySQL为例)中。请描述实现此功能的整体流程,包括数据库表结构设计、如何在文件上传成功后将元数据插入数据库以及可能遇到的事务处理问题及解决方案。
49.1万 热度难度
前端开发Node.js

知识考点

AI 面试

面试题答案

一键面试

1. 数据库表结构设计

创建一个名为 file_uploads 的表,用于存储文件上传的元数据。表结构如下:

CREATE TABLE file_uploads (
    id INT AUTO_INCREMENT PRIMARY KEY,
    file_name VARCHAR(255) NOT NULL,
    file_size BIGINT NOT NULL,
    upload_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
  • id:自增主键,唯一标识每条记录。
  • file_name:存储上传文件的文件名,VARCHAR 类型,最大长度255字符。
  • file_size:存储文件大小,BIGINT 类型,以字节为单位。
  • upload_time:存储文件上传的时间,TIMESTAMP 类型,默认值为当前时间。

2. 文件上传成功后插入元数据到数据库

假设使用 multer 中间件进行文件上传,以下是在文件上传成功后将元数据插入数据库的代码示例:

const express = require('express');
const multer = require('multer');
const mysql = require('mysql2');

const app = express();
const upload = multer({ dest: 'uploads/' });

// 创建数据库连接
const connection = mysql.createConnection({
    host: 'localhost',
    user: 'root',
    password: 'password',
    database: 'your_database'
});

connection.connect();

app.post('/upload', upload.single('file'), (req, res) => {
    const { originalname, size } = req.file;
    const query = 'INSERT INTO file_uploads (file_name, file_size) VALUES (?,?)';
    connection.query(query, [originalname, size], (error, results, fields) => {
        if (error) {
            console.error('Error inserting data into database:', error);
            return res.status(500).send('Error inserting data into database');
        }
        res.send('File uploaded and metadata saved successfully');
    });
});

const port = 3000;
app.listen(port, () => {
    console.log(`Server running on port ${port}`);
});
  • 首先,引入 expressmultermysql2 模块。
  • 使用 multer 中间件处理文件上传,并指定上传文件的存储目录为 uploads/
  • 创建 MySQL 数据库连接。
  • /upload 路由中,当文件上传成功后,从 req.file 对象中获取文件名(originalname)和文件大小(size)。
  • 使用 connection.query 方法执行 SQL 插入语句,将文件名和文件大小插入到 file_uploads 表中。

3. 事务处理问题及解决方案

问题

  • 可能会出现文件上传成功,但在插入数据库元数据时失败的情况。这种情况下,会导致文件已上传但数据库中没有对应的元数据记录,数据不一致。

解决方案

  • 使用数据库事务来确保文件上传和元数据插入操作的原子性。即要么两者都成功,要么都失败。 以下是使用事务处理的代码示例:
app.post('/upload', upload.single('file'), (req, res) => {
    const { originalname, size } = req.file;
    connection.beginTransaction((err) => {
        if (err) {
            console.error('Error starting transaction:', err);
            return res.status(500).send('Error starting transaction');
        }
        const query = 'INSERT INTO file_uploads (file_name, file_size) VALUES (?,?)';
        connection.query(query, [originalname, size], (error, results, fields) => {
            if (error) {
                connection.rollback(() => {
                    console.error('Error inserting data into database, rolling back:', error);
                    return res.status(500).send('Error inserting data into database, rolling back');
                });
            } else {
                connection.commit((err) => {
                    if (err) {
                        console.error('Error committing transaction:', err);
                        connection.rollback(() => {
                            console.error('Rolling back due to commit error:', err);
                            return res.status(500).send('Error committing transaction, rolling back');
                        });
                    } else {
                        res.send('File uploaded and metadata saved successfully');
                    }
                });
            }
        });
    });
});
  • 使用 connection.beginTransaction 方法开始一个事务。
  • 如果在插入数据库操作中出现错误,调用 connection.rollback 方法回滚事务,撤销之前的操作。
  • 如果插入操作成功,调用 connection.commit 方法提交事务,确保文件上传和元数据插入操作都持久化到数据库中。如果提交过程中出现错误,同样回滚事务。