Skip to content

批量上传文件

需求分析

很多用户需要一次性上传多篇小说,如果每次只上传一篇太麻烦,所以支持用户本地上传 txt md 等格式文件到服务器,并统一保存到数据库。

前端发送数据

在已有单文件上传的基础上,更改 input 属性,支持多选文件

<input
  className="local-file-input"
  type="file"
  accept={INPUT_ACCEPT_FILE_TYPE}
  onChange={this.onFileChange}
  multiple
></input>

创建一个函数来处理文件上传

onFileChange = (event) => {
  const files = Array.from(event.target.files);
  // 需要把多文件循环加入到 formData 中上传
  const formData = new FormData();
  files.forEach(file => {
    formData.append('files', file);
  });
  axios.post(`${setting.server}/api/batch-upload-novel`, formData, {
    headers: {
      // 请求内容是一个多部分的表单数据,通常用于上传文件,因为文件需要被编码成多部分的形式发送到服务器。
      'Content-Type': 'multipart/form-data'
    }
  });
};

后端接收数据

获取 POST 请求中的 formData 数据,可以使用 multer 中间件

在 api 中增加对应的路由,并使用 multer 处理上传文件

const express = require('express');
const multer = require('multer');
const app = express();

// upload.none() 表示不处理文件上传,只处理表单数据
// upload.single('file') 表示处理一个名为 file 的文件字段
// upload.array('files', 10) 表示允许上传最多 10 个文件,文件字段名称为 files
router.post("/batch-upload-novel", upload.array('files', 10), ApiNovel.batchUploadNovel);

在回调函数中,获取文件,对信息进行格式化,然后一个 SQL 语句写入数据库:

static batchUploadNovel(req, res) {
  const files = req.files;
  try {
    const fileContents = files.map(file => file.buffer.toString('utf8'));    
    const fileNames = files.map(file => {
      if (file.encoding === 'utf-8') {
        return file.originalname;
      }
      // 格式 7bit 编码格式是 ASCII 编码格式的一个子集,用于表示 ASCII 字符集中的字符,需要 'latin1'转换
      else if (file.encoding === '7bit') {
        return Buffer.from(file.originalname, 'latin1').toString('utf8');
      }
      else if (file.encoding === 'binary') {
        return Buffer.from(file.originalname, 'binary').toString('utf8');
      }
      else if (file.encoding === 'gbk' || file.encoding === 'gb2312') {
        return Buffer.from(file.originalname, file.encoding).toString('utf8');
      }
    });
    const cover_photo = "https://www.baidu.com/img/flexible/logo/pc/result@2.png"; // use default book image
    const author = "佚名";
    const price = 0;
    const size = 1;
    const tag = "";
    const briefs = fileContents.map(item => item.slice(0, 300));
    // 在 MariaDB 中,你可以使用以下方法来插入多条数据:使用单个 INSERT INTO 语句,多个 VALUES 子句:
    // INSERT INTO book (name, cover_photo, author, detail, price, brief, size, tag) VALUES (), ()
    let sql = 'INSERT INTO book (name, cover_photo, author, detail, price, brief, size, tag) VALUES';
    let data = [];
    for (let i = 0; i < fileNames.length; i++) {
      if (i === fileNames.length - 1) {
        sql += `(?, ?, ?, ?, ?, ?, ?, ?);`;
      } else {
        sql += `(?, ?, ?, ?, ?, ?, ?, ?),`;
      }
      data.push(fileNames[i], cover_photo, author, fileContents[i], price, briefs[i], size, tag);
    }
    DBHelper(sql, (err, results) => {
      if (err) {
        logger.error(err);
        res.status(400).send({ error_massage: err });
        return;
      }
      res.status(200).send("success");
    }, data);
  } catch (error) {
    logger.error(error);
    res.status(400).send({ error_massage: 'File upload failed, coding is not supported, please try again' });
  }
}