Search code examples
node.jscloudinary

How do I find and modify assets that were stored as "private" in Cloudinary?


I connected my Node.js project to Cloudinary and I was able to successfully upload both public and private assets to the same folder in cloudinary, I named it "development".

I figured that development/id is the intended publicId, and that worked for public assets.

However, For private assets, that did not work. Beside the development/id path, I also tried every level of image/private/s--KEYHERE--/v123456789/development/publicIdOfPrivateAsset' for every attempt below, but I ended up with a resource not found error (Variations of the error are listed below)

Upload Code

const multer = require('multer')
// Defined in .env: CLOUDINARY_URL=cloudinary://my_key:my_secret@my_cloud_name
const cloudinary = require('cloudinary').v2

const { CloudinaryStorage } = require('multer-storage-cloudinary');
const CLOUDINARY_FOLDER  = "development"

const storage = new CloudinaryStorage({
    cloudinary: cloudinary,
    params: {
        folder: CLOUDINARY_FOLDER,
        // type: 'private' // Used for creating the private asset
    },
});
const upload = multer({ storage: storage });

app.post('/api/v1/media', upload.single('image'), (req, res) => {
    return res.json({ image: req.file.path });
})

Getting the asset using api.resource

http://localhost:5000/api/v1/media/development/publicIdOfPrivateAsset

app.get('/api/v1/media/:folder/:id', (req, res) => {
    const { folder, id } = req.params
    cloudinary.api.resource(`${folder}/${id}`, (error, result) => {
        if (error) return console.log(error)
        return res.json(result)
    });
})

Response

{
  message: 'Resource not found - development/publicIdOfPrivateAsset',
  http_code: 404
}

Destroying the asset using uploader.destroy

http://localhost:5000/api/v1/media/publicIdOfPrivateAsset

app.delete('/api/v1/media/:publicId', (req, res) => {
    const { publicId } = req.params;
    cloudinary.uploader.destroy('development/' + publicId, (err, result) => {
        if (err) { console.log(err); return res.status(500).json(err); }
        return res.status(200).json(result)
    })
})

Response:

{
    "result": "not found"
}

Deleting resources using api.delete_resources

In this example, I included private and public assets in the same request. The public one worked, the private one did not. Request: http://localhost:5000/api/v1/media

Body:

{
    "publicIds": [
        "development/publicIdOfPublicAsset",
        "development/publicIdOfPrivateAsset"
    ]
}

Node.js

app.delete('/api/v1/media', (req, res) => {
    const { publicIds } = req.body;
    cloudinary.api.delete_resources(publicIds, (err, result) => {
        if (err) { console.log(err); return res.status(500).json(err); }
        return res.status(200).json(result)
    })
})

Response:

{
    "deleted": {
        "development/publicIdOfPublicAsset": "deleted",
        "development/publicIdOfPrivateAsset": "not_found"
    },
    "deleted_counts": {
        "development/publicIdOfPublicAsset": {
            "original": 1,
            "derived": 0
        },
        "development/publicIdOfPrivateAsset": {
            "original": 0,
            "derived": 0
        }
    },
    "partial": false,
    "rate_limit_allowed": 500,
    "rate_limit_reset_at": "2021-10-17T14:00:00.000Z",
    "rate_limit_remaining": 494
}

What am I missing? Thank you


Solution

  • In Cloudinary an asset is unique/identified by not only the public_id but only when in combination with the resource_type and type. Therefore, the below assets with the same public_id (sample) are actually different entities:

    image/upload/sample
    image/private/sample
    video/upload/sample
    video/authenticated/sample
    

    API methods default the value of certain optional parameters if they are not provided. This includes the 'resource_type' (defaulted to "image") and also 'type' (defaulted to "upload").

    In your case, since you are trying to search/delete a private image you would need to explicitly pass the 'type' parameter and set its value to 'private'. Otherwise, what will currently happen is that your code would try to delete the right public_id, however, it would be defaulting to 'type' of 'upload' and therefore not find the file you are looking for. This is why it finds the "upload" asset and not the "private" one.

    You need to include the specific type/resource_type in your request if they are different from the default. Here, you need to pass "type": "private" in all your example API calls.