Объедините файловый API с транзакциями для сохранения или удаления файла - PullRequest
0 голосов
/ 06 октября 2018

Я использую узел 8.11.1 с pg-promise 8.4.4 для обработки запросов и транзакций в PostgreSQL.Речь идет об узле, но я думаю, такая же логика и в других серверах / инструментах.

Сценарий распространен.Я хочу сохранить файл изображения в папке, а затем, если это успешно, вставить его данные в базу данных, получить возвращенный идентификатор, а затем выполнить еще одну вставку во вторичной таблице «многие ко многим».

Ясно, что мне нужна транзакция для запросов на вставку.Но как насчет фактического сохранения файла?Мой подход -

fs.rename(oldpath, newpath, (err) => {
  if (err){throw new Error ;}            
      db.tx('my-transaction', t => {
        return t.one('INSERT INTO images(whatever) VALUES($1) RETURNING id', ['whatever'])
        .then(user => {
          return t.batch([
            t.none('INSERT INTO mtm(userId, name) VALUES($1, $2)', [user.id, 'created'])
          ]);
        });
      })
      .then(data => {
          // success          
      })
      .catch(error => {
          // error
      });
}); //fs rename 

Хорошо, если при сохранении файла изображения с помощью fs.rename ошибки нет, продолжите транзакцию.

Если при сохранении изображения произошла ошибка, ничего не будет выполнено, так что все хорошо.

Но проблема в , что если изображение сохранено и в транзакции произошла ошибка?Я закончу с сохраненным изображением и ничего в базе данных.Конечно, пользователь получит ошибку и будет вынужден повторно загрузить его, но У меня все еще есть изображения на моем сервере, которые не связаны ни с чем. Я бы хотел этого избежать.

Решением было бы включить сохранение изображения в транзакцию, поэтому, если что-то не получится, ничего не будет завершено.Как я могу это сделать?Я не знаю, может ли файловый API быть внутри транзакции, связанной с запросом.Я даже не знаю, правильно ли я здесь настроен.

Пожалуйста, дайте мне совет или помогите мне написать код.

Спасибо

1 Ответ

0 голосов
/ 06 октября 2018

Просто удалите файл, если транзакция не удалась (или вы можете переименовать его обратно, если хотите):

const fs = require('fs-extra');

async function saveAll(oldpath, newpath) {
    await fs.rename(oldpath, newpath);
    try {
        return await db.tx('my-transaction', async t => {
            const imageId = await t.one('INSERT INTO images(whatever) VALUES($1) RETURNING id', ['whatever'], a => a.id);
            await t.none('INSERT INTO mtm(userId, name) VALUES($1, $2)', [imageId, 'created']);
            return imageId;
        });
    } catch (e) {
        await fs.unlink(newpath); // deleting the file
        throw e;
    }
}

Функция saveAll вернет новый imageId, в случае успеха, или выброситьошибка, если что-то не получается:

async test() {
    try {
        const imageId = await saveAll('old-path', 'new-path');
        // we are all good
    } catch(e) {
        // something failed, as per the error details
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...