Невозможно загрузить большой longblob в MySQL с помощью Node.js

У меня есть файлы, хранящиеся в базе данных MySQL как longblob. Он работает для загрузки файлов определенного размера, но когда файлы становятся больше, они не загружаются в базу данных, и сервер зависает. Я понимаю, что это проблема с памятью, и я знаю, что вы можете увеличить размер max_allowed_packet, чтобы решить эту проблему. Однако у меня нет доступа или разрешений на изменение этой переменной в mysql, поэтому мне нужно исправить это другим способом, если это возможно? Я использую nodeJS + express с multer для загрузки файлов в базу данных.

Я думал каким-то образом использовать потоки или буфер, но я не уверен, как это сделать.

Конечная точка загрузки файлов сейчас выглядит так:

router.post(
  "/uploadFiles",
  (req, res, next) => {
    console.info(req.body);
    next();
  },
  upload.array("file"),
  async (req, res) => {
    try {
      if (req.body.filesToUpload) {
        let uploadedFiles = JSON.parse(req.body.filesToUpload);
        if (uploadedFiles.length > 0) {
          uploadedFiles.map(async (file, index) => {
            uploadedFiles.map(async (file, index) => {
            values = {
              ClientID: req.body.ClientID,
              FileName: file.FileName,
              FileType: req.files[index].mimetype,
              FileSize: req.files[index].size,
              Content: req.files[index].buffer,
              Date: file.Date,
              Comment: file.Comment,
            };

            await addFileToDb(values);
          });
        }
      }
    
      return res.send();
    } catch (error) {
      next(new ErrorHandler(401, req.url, error));
    }
  }
);


let addFileToDb = async (values) => {
  return new Promise(async (resolve, reject) => {
    try {
      await connection.query(
        "INSERT INTO FileUpload SET ?",
        values,
        (err, result, fields) => {
          if (err) return reject(err);
          return resolve(result);
        }
      );
    } catch (error) {
      return reject(error);
    }
  });
};

Как я могу изменить способ загрузки файлов, чтобы он работал для загрузки файлов большего размера?

Любая помощь приветствуется.

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
0
41
2

Ответы 2

Предполагая, что req.files[index].buffer - это уже весь файл, вы можете сделать следующее:

  1. Разделите буфер на части, которые соответствуют максимальному размеру пакета.
  2. Установите флаг в строке вашей базы данных, чтобы указать, что файл еще не загружен (например, status='pending')
  3. Пока есть чанки: вставьте чанки в базу данных, создав новую строку в таблице.
  4. Когда закончите, измените статус, чтобы указать, что файл готов (например, status='done').
  • Если у вас есть поток и каждое событие «on data» меньше максимального размера пакета, адаптировать вышеуказанное очень легко.
  • В противном случае вам может потребоваться поместить поток преобразования между ними, чтобы разделить поток на большее количество, но меньших по размеру событий.

(В более ранней версии этого ответа я опубликовал пример, в котором использовался UPDATE table SET Content = CONCAT(Content, ?), но, по-видимому, это не работает; результат CONCAT не может быть больше, чем размер пакета.)

Создайте отдельные строки в таблице и выберите все строки в этой таблице, если вы хотите вывести файл. Таблица будет выглядеть примерно так:

id | FileId | Content
---+--------+---------
 1 |      1 | aabbccdd
 2 |      1 | eeff1122
 3 |      1 | 33
 4 |      2 | 44556677
 5 |      2 | 889900

Это похоже на систему хранения MongoDB GridFS.

// After your "await addFileToDb(values);", flag the file as "pending" and:

let offset = 0;
const maxSize = 1024 * 1024; // 1mb
const b = req.files[index].buffer;
while (offest + maxSize <= b.length) {
  // Slice off a chunk of the entire file.
  const chunk = b.slice(offset, offset + maxSize);
  offset += maxSize;
  // INSERT INTO FileChunks (FileId, Content) VALUES (?, ?)
}
// Flag the row as "finished uploading"

(Обязательно тщательно протестируйте и добавьте обработку ошибок)

При загрузке файла выполните:

  SELECT Content
    FROM FileChunks
   WHERE FileChunks.FileId = ?
ORDER BY id ASC

# const fileContent = Buffer.concat( rows.map(row => row.Content) );

Обязательно выполняйте запросы INSERTпоследовательно с await в цикле while(), а не в параллели с Promise.all.

В качестве отступления: не спамите async везде. Правило: функция, которая не использует await в своем теле (! - вложенные функции не учитываются), не должна быть async.

Я соответствующим образом переписал вашу текущую настройку. Обратите внимание, что на самом деле только одна функция является async:

const addFileToDb = (values) =>
  new Promise((resolve, reject) =>
    connection.query("INSERT INTO FileUpload SET ?", values, (err, result, fields) =>
      err ? reject(err) : resolve(result)
    )
  );

router.post("/uploadFiles",
  upload.array("file"),
  async (req, res) => {
    try {
      if (!req.body.filesToUpload) return res.status(400).send('nothing uploaded');
      const uploadedFiles = JSON.parse(req.body.filesToUpload);
      if (!uploadedFiles.length) return res.status(400).send('nothing uploaded');
      const uploadPromises = uploadedFiles.map((file, index) =>
        addFileToDb({
          ClientID: req.body.ClientID,
          FileName: file.FileName,
          FileType: req.files[index].mimetype,
          FileSize: req.files[index].size,
          Content: req.files[index].buffer,
          Date: file.Date,
          Comment: file.Comment,
        })
      );
      await Promise.all(uploadPromises);
      res.send();
    } catch (error) {
      res.status(500).send('oops');
    }
  }
);

Кроме того, это загружает все параллельно, а не в цепочку.

Другие вопросы по теме