如何使用Cloudinary和MongoDB确保原子性

问题描述 投票:0回答:1

我正在开发一个社交媒体平台,用户可以将媒体文件(图像、视频等)上传到 Cloudinary,并将该媒体返回的 URL 存储在我的 MongoDB 数据库中。媒体上传成功,但某些情况下数据库保存操作失败。在这种情况下,我想从 Cloudinary 删除上传的媒体以避免出现孤立文件。

我编写了以下代码,其中使用 Cloudinary 进行文件上传,使用 MongoDB 保存发布数据。我使用 Mongoose 事务来确保数据库操作的原子性,如果数据库保存失败,我将调用 cloudinary.uploader.destroy() 从 Cloudinary 的 catch 块中删除媒体。

但是,我担心数据库事务失败后Cloudinary 上的删除操作失败 的情况。在这种情况下,我的媒体将保留在 Cloudinary 上,从而导致不一致。

这是我的代码的相关部分 Cloudinary配置和实用功能

const cloudinary = require('cloudinary').v2;

// Configuration
cloudinary.config({ 
    cloud_name: process.env.CLOUDINARY_NAME, 
    api_key: process.env.CLOUDINARY_API_KEY, 
    api_secret: process.env.CLOUDINARY_API_SECRET 
});

const opts = {
    overwrite:true,
    invalidate:true,
    resource_type:"auto",
    folder: "Socialsphere"
};

// Upload media to cloudinary
exports.cloudinaryUpload = (image) => { // image = base64
    return new Promise((resolve, reject) => {
        cloudinary.uploader.upload(image, opts, (error, result) => {
            if (result && result.secure_url) {
                return resolve(result);
            }
            return reject({message: error.message});
        });
    });
};

// Delete media from cloudinary
exports.cloudinaryDelete = async (id) => {
    await cloudinary.uploader.destroy(id);
};

控制器代码

const Post = require('../Models/Post');
const Profile = require('../Models/Profile');
const mongoose = require('mongoose');
const { cloudinaryDelete, cloudinaryUpload } = require('../Utiles/Cloudinary');

exports.CreateNewPost = async (req, res) => {
    // Implementing transaction
    const session = await mongoose.startSession();
    session.startTransaction();

    let cloudinaryResult = null;

    try {
        const { caption, url, type } = req.body;
        const { Email } = req;

        if (!url) {
            return res.status(409).json({
                success: false,
                message: 'Post creation failed, try again'
            });
        }

        // Uploading media to Cloudinary
        cloudinaryResult = await cloudinaryUpload(url);
        const fileUrl = cloudinaryResult.secure_url;

        // Saving post to the database
        const userPost = new Post({ Url: fileUrl, Caption: caption, Type: type });
        await userPost.save({ session });

        // Finding user by email and updating their post array
        const userProfile = await Profile.findOneAndUpdate(
            { Email: Email },
            { $push: { Post: userPost._id } },
            { new: true, session }
        );

        if (!userProfile) {
            return res.status(404).json({
                success: false,
                message: 'User not found, please login and try again'
            });
        }

        // Commit the transaction
        await session.commitTransaction();
        session.endSession();

        return res.status(200).json({
            success: true,
            message: 'Post created successfully'
        });
    } catch (error) {
        console.log(error);

        // If any operation fails, delete the uploaded file from Cloudinary
        if (cloudinaryResult && cloudinaryResult.public_id) {
            await cloudinaryDelete(cloudinaryResult.public_id);
        }

        // Abort the transaction
        await session.abortTransaction();
        session.endSession();

        return res.status(500).json({
            success: false,
            message: 'Post creation failed, try again'
        });
    }
};

我的问题:

  • 如何确保 Cloudinary 和 MongoDB 操作的原子性?
  • 数据库故障后Cloudinary删除失败怎么办?我该如何处理此类故障以避免 Cloudinary 上出现孤立媒体文件?
  • 在 Cloudinary 等外部服务涉及数据库事务的情况下,是否有任何最佳实践来确保最终一致性?

如果有任何关于如何改进此流程并使其更加稳健的建议,我将不胜感激。谢谢!

node.js backend mern cloudinary
1个回答
0
投票

我在 Cloudinary 工作,我可以解决你的第三点: 如果您担心孤立的图像留在 Cloudinary 上,您可以执行以下操作:

  • 在 Mongo 上,找到
    public_id
    值的列表。这些是应该存在的图像。
  • 在 Cloudinary 上,使用 Admin API 列出可用资源。
  • 创建差异并删除 Mongo 中未找到的任何资产。

您也许可以将其作为计划作业,每月/每季度运行一次并清理系统。

© www.soinside.com 2019 - 2024. All rights reserved.