Skip to main content

Help with incomplete Android Signed upload documentation

Comments

15 comments

  • Official comment
    Akshay Joshi

    Hi Chanda,

    You can refer to the uploading assets documentation. It has different ways in which you can upload the images as well as multiple language implementations including Android.

    Below is the link to the documentation:

    https://cloudinary.com/documentation/upload_images

    Let us know if it works and if you have any other questions.

  • Chandra

    Hi Akshay, 

    Thanks for your quick reply. As per the link provided by you, we need to follow the below code for signed upload right?

    MediaManager.get().upload("sample.jpg") .option("public_id", "sample_id").dispatch();

    Then, what about other parameters like "digest", "apiKey" and "timeStamp" if we need to update this info along with image upload? And I didn't get what is the use of "SignatureProvider" and when exactly we need to use it. Please provide the complete steps like initializing the MediaManager and uploading files with multiple parameters or guide me to see those details.

    Looking forward for your inputs to complete the signed upload process using Cloudinary Android SDK.

    Regards,

    Chandra.

    0
  • Michal Kuperman

    Hi Chandra,

    In the Android SDK the default upload e.g: `MediaManager.get().upload(file).dispatch()` preforms a signed upload. If you would like to perform an unsigned upload you would need to use this: `MediaManager.get().upload(file).unsigned(upload_preset).dispatch()`
    (Unlike in the iOS SDK, where the default upload is unsigned).

    The signature itself needs to be generated on the server-side. You will receive the timestamp, API key, and signature from your server.

    When using signed uploads with the Android SDK, you need to init the `signatureProvider` and implement your own `provideSignature` method inside this class, to call your server to generate the signature.
    Then on every upload request, the mediaManager will sign the request by calling this signatureProvider automatically (no need to call this on your own) and pass the required parameters for signing. Please note, that you need to init this only once in your application.

    You can refer to this GitHub link for more info on performing signed uploads - https://github.com/cloudinary/cloudinary_android#2-signed-uploads-with-server-based-signature

    Please let us know if you need any further assistance.

    Best regards,
    Michal

    0
  • Chandra

    Hi Michal,

    Thanks for your response. I still wonder how do we need to integrate this SDK in our app. Please find my queries (all related to Signed Upload).

    1) As you mentioned, we need to call MediaManager.init() with SignaturProvider once in our application and I feel the onCreate() of Application class is right place for it as it call only once in the entire app lifetime. Please suggest, whether it's the correct place to call "init" or not.

    Please note, we may use this Image Upload feature in multiple screens (activities/fragments). 

    2) If application class is the right place to call "init", do you suggest to initiate the service call to get the signature (like 'provideSignature') in this class? I don't think, it's a right approach to initiate service call in application class. Not sure about it. Please suggest.

    3) Once we initiate to fetch the signature details, our service provides the response in below format.

    {
    "digest": "<Digest String>",
    "tstamp": 1622261458,
    "pubid": "<To be uploaded Image Name>",
    "uploadPreset": "profile_pic_preset",
    "ak": "<Key>"
    }

    How to create the "Signature" object (return type of provideSignatue method) with the above details? Also want to understand, how these values will be used for each upload (Time Stamp - unique for each upload, "pubid" - to be uploaded as image name, "uploadPreset" - to specify correct preset to identify different places of upload). Please suggest in this case.

    Thanks in advacne,

    Chandra.

    0
  • Chandra

    Hi Michal,

    Also want to add some more points to above:

    4) If we assume the method 'provideSignature' called, it may not return the response immediately and as return type of this method, as we are using Retrofit library for service calls. We may get the response in 'onResponse' call back method. Please suggest how to get response back and create Signature object to return in this case.

    Thanks,

    Chandra.

    0
  • Michal Kuperman

    Hi Chandra,

    Let me try to answer your questions.

    1. As the initialization should take place only once, you should call the init() function on the application level (and not on an activity level) So the onCreate() function is a good place to initiate this.

    2. Initiating the `provideSignature` method should be done here as well. You can write the method itself in a separate class and initiate it here. For example: ` MediaManager.init(this, new provideSignature());`

    3. In the `provideSignature` method, once you get the signature response from your server you can parse it and return it like this:

    ```
    JSONObject obj = new JSONObject(response.body().string());

    return new Signature(obj.getString("signature"), obj.getString("api_key"), obj.getLong("timestamp"));

    ```

    Regarding your last inquiry, If I understand correctly, the `provideSignature` method would wait for the "onResponse" callback to receive the signature. In this case, it should work as expected and you will return the signature once you receive it.

    If you would like to open a ticket to our support and provide your code I will gladly further look into this and assist you with implementing the signed upload.

    Regards,
    Michal

    0
  • Chandra

    Hello Michal,

    I will check what you mentioned and get back to you! Apologies for late reply. 

    Best, Chandra

    0
  • Chandra

    Hi Michal,

    Thanks for your support. Unfortunately, I couldn't resolve the upload issue in Android yet. I have tried the below code as you mentioned in your previous comments.

    private void initMediaManager() {
    MainViewModel viewModel = new ViewModelProvider(this).get(MainViewModel.class);
    MediaManager.init(this, new SignatureProvider() {
    @Override
    public Signature provideSignature(Map map) {
    viewModel.fetchCloudDigest("someToken", "reason_to_identify_type_of_upload");
    viewModel.getCloudDigest().observe(MainActivity.this, new Observer<CloudDigestItem>() {
    @Override
    public void onChanged(CloudDigestItem cloudDigest) {
    String digest = cloudDigest.getDigest();
    String apiKy = cloudDigest.getApiKy();
    long time = cloudDigest.getTimeStamp();
    return new Signature(digest, apiKy, time);
    }
    });
    return null;
    }

    @Override
    public String getName() {
    return null;
    }
    });
    }

    which is used to initialize the MediaManager, but when I tried to call a service (using Retrofit and wait for the response callback (onResponse) to get the response from it to create the new Signature object. But it's not allowing me to return the Signature object in the response callback as it's already returned as "null" and don't wait for the response to come back from service. Please suggest how to proceed here.

    And also please note that the value of "reason_to_identify_type_of_upload" may change on different types of image uploads like "profile pic upload" or "post image upload". As it dynamically need to send to server and get the signature details, do we need to call this "init" method before every type of image upload? Please clarify.

    Best, Chandra.

    0
  • Francis Tagbo

    Hi Chandra,

    From what I understand from your code, the event onChanged on your object Observer is not triggered yet so it will return null. What I could suggest is to use a synchronization like CountDownLatch so it could allow the thread to wait until a set of operations being performed in other threads completes.

    If you are not changing the Cloudinary configuration, you don't need to call the "init" method for every type of image upload. You should handle it in the "provideSignature" method.

    My colleague Yakir has an example project that uses sign upload on Android: https://github.com/yakirp/Cloudinary-Sample/tree/master/app/src/main/java/com/example/ekene/cloudinarsample
    You could check his project as it may help you to give ideas on how to implement the sign upload.

    Please let me know how it goes.

    Regards,
    Francis

    0
  • Chandra

    Hi Francis,

    Thanks for your quick reply. I understand that there is thread synchronization issue. But couldn't find way to solve that. Now, I got some idea with your inputs and sample project. I hope this will help us to solve the problem. Will get back to you with the results soon.

    Best Regards,

    Chandra.

    0
  • Francis Tagbo

    Hi Chandra,

    You're welcome! Please let us know how it goes.

    Regards,
    Francis

    0
  • Chandra

    Hi Francis,

    We have tried using the signature details from server ahead of upload request and pass those details to create Signature object. We are getting the error saying "Invalid Signature". Please find the complete log details for it.

    DefaultRequestProcessor: Unexpected exception for request 3c92a4b1-2c86-4bbb-b278-9a44c2aca20c.
    java.lang.RuntimeException: Server returned unexpected status code - 401 - {"error":{"message":"Invalid Signature c2843fb2f3f9662958730684418aebb1f3843647. String to sign - 'public_id=0r9_NoyWLFtm&timestamp=1624597185'."}}
    at com.cloudinary.android.UploaderStrategy.callApi(UploaderStrategy.java:134)
    at com.cloudinary.Uploader.callApi(Uploader.java:34)
    at com.cloudinary.Uploader.uploadLargeParts(Uploader.java:218)
    at com.cloudinary.Uploader.uploadLarge(Uploader.java:149)
    at com.cloudinary.android.DefaultRequestProcessor.doProcess(DefaultRequestProcessor.java:223)
    at com.cloudinary.android.DefaultRequestProcessor.processRequest(DefaultRequestProcessor.java:87)
    at com.cloudinary.android.MediaManager.processRequest(MediaManager.java:435)
    at com.cloudinary.android.AndroidJobStrategy$UploadJob.onRunJob(AndroidJobStrategy.java:264)
    at com.evernote.android.job.Job.runJob(Job.java:124)
    at com.evernote.android.job.JobExecutor$JobCallable.runJob(JobExecutor.java:183)
    at com.evernote.android.job.JobExecutor$JobCallable.call(JobExecutor.java:168)
    at com.evernote.android.job.JobExecutor$JobCallable.call(JobExecutor.java:151)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
    at java.lang.Thread.run(Thread.java:919)

    FYI, with the same signature in iOS app, we are able to upload the image. Please find the below iOS Swift code:

    // iOS Swift code
    let uploadRequestParams = CLDUploadRequestParams()
    .setPublicId(digestParams["pubid"]!)
    .setUploadPreset(digestParams["uploadPreset"]!)
    .setResourceType("image")
    .setParam("api_key", value: digestParams["apikey"]!)

     

    Unfortunately, we couldn't find a proper way to pass "uploadPreset" and "public_id" parameters in the upload request. We are keeping those values in a HashMap and passing them as "options" like below. Please suggest how to include "public_id" and "uploadPreset" values while uploading.

    Map<String, Object> options = new HashMap<>();
    options.put("digest", "signature_received_from_server");
    options.put("tstamp", time_stamp_received_from_server);
    options.put("public_id", "public_id_received_from_server");
    options.put("preset", "upload_preset_received_from_server");
    options.put("ak", "api_key_received_from_server");

    MediaManager.get().upload(picUri)
    .options(options)
    .callback(new UploadCallback() {
    @Override
    public void onStart(String s) {
    Log.i(TAG, "Image upload started");
    }

    @Override
    public void onProgress(String s, long l, long l1) {
    Log.i(TAG, "Image upload on progress");
    }

    @Override
    public void onSuccess(String requestId, Map resultData) {
    Log.i(TAG, "Image upload success");
    }

    @Override
    public void onError(String s, ErrorInfo errorInfo) {
    Log.e(TAG, "Image upload error: " + s);
    }

    @Override
    public void onReschedule(String s, ErrorInfo errorInfo) {
    Log.e(TAG, "Image upload reschedule: " + s);
    }
    }).dispatch();
    0
  • Chandra

    Please let us know, if any other details required.

    Best Regards,

    Chandra.

    0
  • Chandra

    Hello Support Team,

    Finally my upload from Android app is working with a small correction in the "key" (changed the key "preset" to "upload_preset") of one of the options provided to the upload API.

    options.put("upload_preset", "upload_preset_received_from_server");

    Thanks for your support!

    Best, Chandra

    0
  • Aleksandar Kostadinov

    Hi Chandra,

    Thanks for the update from your side and great to hear it's now working for you.

    0

Post is closed for comments.