Setting Metadata properties of images with unsigned upload from browser using Javascript

Comments

13 comments

  • Official comment
    Avatar
    Akshay Joshi

    Hi Alan,

    I'm happy to see that you've already managed to find the solution for your use case.

    We truly appreciate you sharing your example here for any other members reaching this thread with the same question. Thank you for your contribution :)

    I will also update the documentation team about this so that they can take a look.

    If you have any other questions, please do not hesitate to let us know.

    Regards,

    Akshay

     

    Comment actions Permalink
  • Avatar
    Aditi Madan

    Hi Noah,

    You can add "context" in the unsigned upload to add the metadata.

    Here is an example:-

    $('.upload_field').unsigned_cloudinary_upload(preset_name, {

    cloud_name: cloud_name,

    public_id: "cup",

    context:{ alt: "my_alt", caption: "my_caption",name:"value"}

    }

    0
    Comment actions Permalink
  • Avatar
    monday.com Ltd

    Can I do the same for sign uploads?

    0
    Comment actions Permalink
  • Avatar
    Aditi Madan

    Yes, you can use it on signed uploads.

    In general, adding metadata to images is done via our API. This article explains it in more detail- https://support.cloudinary.com/hc/en-us/articles/202521142-Can-I-add-metadata-to-images-   

    Thanks,
    Aditi

    0
    Comment actions Permalink
  • Avatar
    Alan Lawrance

    I'm looking to do the same thing using the .NET version of the Cloudinary SDK.

    I've found the Context() method, but it appears this is for setting metadata for previously uploaded images.

    Is it possible to set metadata as part of anUpload call using the .NET SDK?  If so, can you provide an example?

    0
    Comment actions Permalink
  • Avatar
    Eric Pasos

    Hi Alan,

    There is the context parameter that is available on the Upload API, and this can be used as you have described (see https://cloudinary.com/documentation/image_upload_api_reference#:~:text=example%3A%20%5B%27animal%27%2C%20%27dog%27%5D-,context,-String). For example:

    var uploadParams = new ImageUploadParams()
    {
        File = new FileDescription(m_testImagePath),
        PublicId = GetUniquePublicId(),
        Context = new StringDictionary("key=value", "key2=value2"),
        Tags = m_apiTag
    };

    var uploaded = m_cloudinary.Upload(uploadParams);

    Hope this helps. Thanks.

    0
    Comment actions Permalink
  • Avatar
    Alan Lawrance

    Thank you -- I am able to send a Context successfully and the meta-data shows up as expected in the dashboard.

    However, in the response to the Upload there is a fault due to the following exception:

    "System.Exception: Failed to deserialize response with status code: OK ---> Newtonsoft.Json.JsonReaderException: Error reading string. Unexpected token: StartObject. Path \'context.custom\', line 1, position 641.

            var uploadResult = m_Cloudinary.UploadAsync(uploadParams, null);
            uploadResult.ContinueWith(t =>
            {
                if (t.IsFaulted || t.IsCanceled) {
                    callback?.Invoke(t.Result.Error.ToString());
                } else {
                    callback?.Invoke(string.Empty);
                }
            }); 

    So in the above code, t.IsFaulted is true.  Everything looks fine in the Dashboard for this upload, so I am wondering if this is an issue with the .NET SDK.

    0
    Comment actions Permalink
  • Avatar
    Eric Pasos

    Hi Alan,

    It looks like the following object result is Null, and trying to access it will throw a System.NullReferenceException (i.e., since the StatusCode is HttpStatusCode.OK, and the Error is expected to be null) :

    t.Result.Error

    In case, could you please help to capture the stack trace and share it so we can confirm the issue?

    Thanks in advance.

    0
    Comment actions Permalink
  • Avatar
    Eric Pasos

    Hi Alan,

    Thanks for the stack trace, and it shows that since you have a status OK as a response from the UploadAsync, there will be no Error JSON response returned. If you intend to use the uploadResult.ContinueWith, I would recommend using t.Result.Error?.ToString() (i.e., adding the ? in order to dynamically handle the Null value):

    var uploadResult = m_Cloudinary.UploadAsync(uploadParams, null);
            uploadResult.ContinueWith(t =>
            {
                if (t.IsFaulted || t.IsCanceled) {
                    callback?.Invoke(
    t.Result.Error?.ToString()
    );
                } else {
                    callback?.Invoke(string.Empty);
                }
            }); 

    Hope this helps. Thanks.

    0
    Comment actions Permalink
  • Avatar
    Alan Lawrance

    I shouldn't have included that code snipped using t.Result.Error, as that has muddied the waters.  It is indeed not good code, but that isn't the root of the problem.

    I switched to using the synchronous Upload, which plainly shows the error occur in the SDK itself when processing the response from the server.

    The debug output shows:

    Rethrow as Exception: Failed to deserialize response with status code: OK

    So the response from the server is failing to be parsed correctly by the SDK, resulting in the exception.

    EDIT: This only occurs if Context data is included with the upload.  If I remove the Context, the error does not occur.

    Note that when including the Context (metadata), it does appear correctly in the Media Library.  So this appears to be an SDK-specific issue.

    Here are the context values being passed to upload:

    0
    Comment actions Permalink
  • Avatar
    Eric Pasos

    Hi Alan,

    Thanks for the clarification. But just to have the same implementation approach, could you help to check the following code implementation found in this link? You could also see more examples in our .Net SDK from GitHub, which can be found at this link.  

    Thanks.

    0
    Comment actions Permalink
  • Avatar
    Alan Lawrance

    Digging into this a bit more, I traced into the code in the SDK that is parsing the response.  The json response is:

    "{\r\n  \"asset_id\": \"f08fee955c85ac66d4dfc014150384c0\",\r\n  \"public_id\": \"ymvfuckratxuioisfqed\",\r\n  \"version\": 1645821004,\r\n  \"version_id\": \"7f6a7087b82da698171e27c8faf29d86\",\r\n  \"signature\": \"9a29df11c5a06d2f4c2f454b948c7e2e1bc253ba\",\r\n  \"width\": 480,\r\n  \"height\": 270,\r\n  \"format\": \"webm\",\r\n  \"resource_type\": \"video\",\r\n  \"created_at\": \"2022-02-25T20:30:04Z\",\r\n  \"tags\": [\r\n    \"test_api\"\r\n  ],\r\n  \"pages\": 0,\r\n  \"bytes\": 901185,\r\n  \"type\": \"upload\",\r\n  \"etag\": \"2fb57db76be547667f46283f73fdd414\",\r\n  \"placeholder\": false,\r\n  \"url\": \"http://res.cloudinary.com/dry-cactus/video/upload/v1645821004/ymvfuckratxuioisfqed.webm\",\r\n  \"secure_url\": \"https://res.cloudinary.com/dry-cactus/video/upload/v1645821004/ymvfuckratxuioisfqed.webm\",\r\n  \"context\": {\r\n    \"custom\": {\r\n      \"key\": \"value\",\r\n      \"key2\": \"value2\"\r\n    }\r\n  },\r\n  \"audio\": {\r\n    \"codec\": \"vorbis\",\r\n    \"frequency\": 48000,\r\n    \"channels\": 2,\r\n    \"channel_layout\": \"stereo\"\r\n  },\r\n  \"video\": {\r\n    \"pix_format\": \"yuv420p\",\r\n    \"codec\": \"vp8\",\r\n    \"level\": -99,\r\n    \"profile\": \"0\",\r\n    \"dar\": \"16:9\",\r\n    \"time_base\": \"1/1000\"\r\n  },\r\n  \"is_audio\": false,\r\n  \"frame_rate\": 30.0,\r\n  \"bit_rate\": 236043,\r\n  \"duration\": 30.543,\r\n  \"rotation\": 0,\r\n  \"original_filename\": \"file_example_WEBM_480_900KB\",\r\n  \"api_key\": \"693864168465841\"\r\n}"

    The line of code that fails is:

    var result = jsonObj.ToObject<T>();

    What seems to be tripping things up is the "custom" field after "context" in the json.

    0
    Comment actions Permalink
  • Avatar
    Alan Lawrance

    Got to the bottom of this --

    VideoUploadResult needed to change this:

            /// <summary>
            /// Gets or sets a key-value pairs of context associated with the resource.
            /// </summary>
            [DataMember(Name = "context")]
            public Dictionary<string, string> Context { get; set; }

    To this:

            /// <summary>
            /// Gets or sets a key-value pairs of context associated with the resource.
            /// </summary>
            [DataMember(Name = "context")]
            public JToken Context { get; set; }

    This is how it looks in ImageUploadResult.  This fixes the issue, and appears to have no negative side effects.

    This should probably be updated in the .NET SDK.

    0
    Comment actions Permalink

Please sign in to leave a comment.