Can't update existing image asset using upload_stream method?

Comments

6 comments

  • Avatar
    Eyal Katz Talmon

    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 :)

    0
    Comment actions Permalink
  • Avatar
    sunflamedev

    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.

    0
    Comment actions Permalink
  • Avatar
    Eyal Katz Talmon

    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.

    0
    Comment actions Permalink
  • Avatar
    sunflamedev

    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.

    https://goor.me/eWCcP

    0
    Comment actions Permalink
  • Avatar
    Eyal Katz Talmon

    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.

    0
    Comment actions Permalink
  • Avatar
    sunflamedev

    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!

    0
    Comment actions Permalink

Please sign in to leave a comment.