首页 > 解决方案 > 将文件 API 与用于文件保存或删除的事务相结合

问题描述

我正在使用节点 8.11.1 和pg-promise 8.4.4来处理 PostgreSQL 中的查询和事务。这是关于节点的,但我想在其他服务器/工具中也是相同的逻辑。

场景很常见。我想将图像文件保存在文件夹中,然后如果成功,则将其详细信息插入数据库中,获取返回的 id,然后在辅助的多对多表中进行另一次插入。

显然,我需要一个用于插入查询的事务。但是实际的文件保存呢?我的方法是

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 是否可以在与查询相关的事务中。我什至不知道我在这里的心态是否正确。

请建议或帮助我编写代码。

谢谢

标签: node.jsfile-uploadtransactionspg-promise

解决方案


如果事务失败,只需删除文件(或者您可以将其重命名,如果您愿意):

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将返回 new 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
    }
}

推荐阅读