Can't update existing image asset using upload_stream method?
I'm building a web app with an html canvas-based feature where users can create a game character and save it to their gallery, and then they can go back to the canvas to customize their characters by changing or adding to their initial choices. I've successfully been able to export the canvas data as a blob to the server and then upload from a buffer to Cloudinary as a png using upload_stream, but I'm running into issues when trying to update the existing image asset with the user's new customization choices.
The functionality I want: the existing image asset with the specified public_id is overwritten from the incoming buffer with the user's new choices.
The functionality I'm getting: upload_stream creates a new image asset instead of overwriting the existing one, even with the existing public_id specified in the upload_stream options and with overwrite enabled in my upload preset.
Additional details: I'm using Multer to receive the blob on the server. I am developing on GoormIDE (cloud IDE) with MongoDB, Node.js and Express.
The Express route as I currently have it. The rest of it works, it's just upload_stream that isn't producing the desired result.
The upload preset I'm using:
Signed
|
|
I have also tried replacing upload_stream with this:
cloudinary.api.update(foundImage.public_id, {}, function(error, result){
console.log(error, result);
});
This does not create a new image asset, but it also doesn't update the existing image from the new buffer stream. I think this method might work if I could somehow pass the buffer stream into it, but I've been combing the docs and Google and I don't see anything. I tried piping the buffer stream with the update method with no luck. I have looked at the uploader.explicit method in the docs, but I don't see any way of passing the buffer stream to that either. How can I tackle this issue?
-
Hey, thanks for reaching out!
Can you try setting the upload preset with "Use filename or externally defined public ID" enabled? This, together with "Overwrite=true", "Unique filename=false" should allow you to overwrite the asset using the upload_stream() method.
The update() method allows you to update attributes of an existing resource (e.g -metadata, tags, etc.), however, it does not support overwriting a resource.
Hope this helps :)
-
Hi, thanks for your response.
Unfortunately, enabling "Use filename or externally defined public ID" creates a new problem. It does overwrite the existing image, but now I can't create any new images on Cloudinary from my server. I need to be able to create new images and also overwrite existing images. When trying to post new image data to Cloudinary with that setting enabled, Cloudinary creates one image asset with a public_id of 'unicornBaseImgs/file'. I specified a numerical public_id in the upload_stream config options (this numerical public_id would be different for each new image created, because it is generated dynamically on the server), and according to the docs Cloudinary ought to be using the externally defined public_id when uploading the image, but Cloudinary seems to be ignoring it. On subsequent post requests, which should create a new image, it just overwrites that one existing image asset every time. What seems to be happening is that the public_id never changes in this case, even though I defined the public_id externally in the upload_stream config options. What I get, basically, is one image asset and only that one image asset, which is overwritten every time even when I want to create a new image asset. It won't let me create any new image assets after the first one.
I need two functionalities: 1) user creates new images to save to their gallery, 2) user can edit a specific image from their gallery, and saving the changes to that specific gallery image will overwrite the corresponding image asset on Cloudinary. It seems I can only do one or the other, though. With the many different combinations of settings I have tried, I can either have one single image asset that gets overwritten each time, or I can create a new image asset each time.
-
Hey,
Thanks for the update!
The issue you've encountered sounds a bit odd, as the above settings with the modification I suggested should allow for both overwrite of old assets, and uploading of new assets with dynamic public IDs, as you intend.
Looking at the logs, I could see multiple upload requests with the public ID set to "unicornBaseImgs/file". This implies that the requests originated from your server had explicitly configured the public ID to be "unicornBaseImgs/file".
Could you kindly check and see if this is the case indeed? Maybe print the desired public_id one line before performing the upload request, and see if it indeed holds a numeric value as desired?
Please let me know if this helps, or you have any questions.
-
I checked the public_id output and there's definitely something going wrong.
Here is the console log of the explicitly defined public_id right before the upload request. This is from the POST route, where a new image should be created:
5fad77574391aa1e3ad2a87a
This is the dynamically generated id I am aiming for, so it's definitely being set correctly in the options object.
Here is the output from the upload_stream result object after the POST upload request:
public_id: 'unicornBaseImgs/file'
As you can see, it's not the same public_id that is being explicitly defined in the options object.
In the POST route, I am saving the public_id that is returned with the upload result into the MongoDB entry associated with that specific "unicorn". And then in the PUT route, where an existing image asset should be updated, I am using the public_id that was saved in the database to explicitly define the public_id for the PUT upload request, which should ensure that the correct image asset is being accessed. Or at least, I think it would do that if Cloudinary was using the numerical public_id that I am trying to explicitly set.
From the PUT route, this is the console log result from the options object before uploading.
unicornBaseImgs/file
Which is what got sent to Cloudinary in the POST upload, but is not what I had specified in the options object for the POST route's upload request.
And the console log from the PUT upload result object:
public_id: 'unicornBaseImgs/file'
And the end result in the Cloudinary folder: an image asset named "file" that does indeed get overwritten to reflect the updated image data from the canvas. However, since my POST route is failing to attach the explicitly defined numerical public_id to the upload request, you can see that each time I POST new image data, Cloudinary will keep reusing public_id: 'unicornBaseImgs/file' and will just continually overwrite the existing "file" asset rather than creating a new one with the next dynamically generated id string.
If I change the preset to Discard Original Filename: false, the result is the same and I get this output in the upload result object in both the POST and PUT routes:
original_filename: 'file'
I'm not sure where this parameter is getting its value from. If I console log the req.files file object that gets passed to NodeJS from the client side form submission, I get this parameter:
originalname: 'image.png'
Which is clearly different from the original_filename parameter that Cloudinary is returning with the result object.
I am attaching a guest link to my workspace with read-access so you can view my source code in its entirety if needed. The relevant files are app.js, public/scripts/unicornbuilder.js, public/scripts/submitscripts.js, and views/unicornbuilder.ejs.
-
Hi, I've taken a look at your code, thanks for sharing it!
I'm not very familiar with MongoDB and mongoose, so I'd like to confirm something with you that seems a bit strange -
I noticed that when uploading (post on /unicornbuilder route) you've set the public_id to be "newUnicorn._id". According to the MongoDB documentation, _id holds a ObjectId object, and in order to get its value, you'll need to use its "str" attribute.
In addition, looking at the Unicorn schema, I see that you're auto-incrementing "id" (which is not the same as "_id" as far as I can see). According to the "mongoose-sequence" module's documentation -
If you want to increment the
_id
field which is special to mongoose, you have to explicitly specify it as a Number and tell mongoose to not interfere:UserSchema = mongoose.Schema({
_id: Number,
name: String
}, { _id: false });
UserSchema.plugin(AutoIncrement);Did you perform this?
It looks like you meant to use the "newUnicorn.id" attribute on upload, and not "newUnicorn._id", is that correct? Could you try it out?I hope this helps.
I may as well not have enough experience with MongoDB. If this doesn't help, I'll try to see if one of my teammates can further assist. -
Hi, thanks for looking at my code and getting back to me!
In answer to your first question: thanks for catching that detail about ObjectId's str attribute! I'm still getting the hang of MongoDB and Mongoose and I had missed that!
In answer to your second question: the ObjectId _id and the auto-incremented id are intended to be separate fields. They will be used for separate aspects of site functionality. So, I did want to use the value of _id for uploading and not the auto-incremented id.
However, I have set the public_id to be the output of newUnicorn._id.valueOf(), and I still get the same outcome in Cloudinary, unfortunately, so that doesn't seem to have solved the issue!
-
Hi,
I'm following up on some older posts in our support forum where the last comment was not from a Cloudinary team member and found this thread - may I ask were you able to find a solution to your issue?
If not, may I ask you to please share what the current status is, and we'll try to assist
Apologies for the delay in follow-up also; a tool that we use to notify our team about forum updates didn't receive your last comment correctly and we missed it - we're also checking what caused that to resolve it
Regards,
Stephen
Post is closed for comments.
Comments
7 comments