1
votes

I'm using Cloudinary without jQuery and trying to make a direct upload through the browser.

I've followed the directions HERE and created an upload preset called seller.

The problem is that when I make posts from the client (using Angular), I get the response:

XMLHttpRequest cannot load https://api.cloudinary.com/v1_1/mycloud/image/upload. Credentials flag is 'true', but the 'Access-Control-Allow-Credentials' header is ''. It must be 'true' to allow credentials.

Which I understand is a CORS issue. I'm making the request as shown below.

req =
  method: 'POST'
  url: 'https://api.cloudinary.com/v1_1/mycloud/image/upload'
  headers:
    'Access-Control-Allow-Credentials': 'true'
  file: scope.file
  data:
    upload_preset: 'seller'
$http(req)
  .success (data, status, headers, config) ->
    console.log 'file is uploaded successfully. Response: ' + data
  .error (err) ->
    console.log 'file error', err

What am I missing? Is it just not possible to do a Cloudinary direct upload without their jQuery plugin?

5

5 Answers

3
votes

Here is a working solution with AngularJS 1.3 to perform a direct unsigned upload on cloudinary without their jQuery plugin:

var file = /* your file */;
var cloud_name = 'your cloud_name';

var fd = new FormData();

fd.append('upload_preset', 'seller');
fd.append('file', file);

$http
    .post('https://api.cloudinary.com/v1_1/' + cloud_name + '/image/upload', fd, {
        headers: {
            'Content-Type': undefined,
            'X-Requested-With': 'XMLHttpRequest'
        }
    })
    .success(function (cloudinaryResponse) {

        // do stuff with cloudinary response
        // cloudinaryResponse = { public_id: ..., etc. }

    })
    .error(function (reponse) {


    });
2
votes

Let's all take a moment to point out how horrible Cloudinary's documentation is. It's easily the worst i've ever seen. Nightmare fuel.

Now that i've got that off my chest... I really needed to be able to do this and I absolutely did not want to install their huge library to do it, nor did I want anything to do with JQuery (lol). I spent way too long banging my head against walls for what should be extremely simple - but - Here it is...

Server (Node.js)

You'll need an endpoint that returns a signature-timestamp pair to the frontend:

import cloudinary from 'cloudinary'

export async function createImageUpload() {
  const timestamp = new Date().getTime()
  const signature = await cloudinary.utils.api_sign_request(
    {
      timestamp,
    },
    process.env.CLOUDINARY_SECRET
  )
  return { timestamp, signature }
}

Client (Browser)

The client makes a request to the server for a signature-timestamp pair and then uses that to upload a file. The file used in the example should come from an <input type='file'/> change event etc.

const CLOUD_NAME = process.env.CLOUDINARY_CLOUD_NAME
const API_KEY = process.env.CLOUDINARY_API_KEY

async function uploadImage(file) {
  const { signature, timestamp } = await api.post('/image-upload')
  const form = new FormData()
  form.append('file', file)
  const res = await fetch(
    `https://api.cloudinary.com/v1_1/${CLOUD_NAME}/image/upload?api_key=${API_KEY}&timestamp=${timestamp}&signature=${signature}`,
    {
      method: 'POST',
      body: form,
    }
  )
  const data = await res.json()
  return data.secure_url
}

That's it. That's all it takes. If only Cloudinary had this in their docs.

1
votes

To solve many CORS related issues and browser incompatibilities, you'll be better off using either the jQuery based uploader from jquery.cloudinary.js or ng-file-upload. You can see both examples here - https://github.com/cloudinary/cloudinary_angular/tree/master/samples/photo_album

In particular, the CORS request is rejected because of the Access-Control-Allow-Credentials header you are passing. Please try to remove it.

0
votes

If anyone is looking for an Angular ng-flow + cloudinary solution I created a basic setup here

https://gist.github.com/maruf89/5528df53b133d442b292

-1
votes

Follow this tutorial for a simple solution, works charm..

http://prasanthco.de/tutorials/cloudinary-image-upload-using-angularjs/

Do this.

Index.html - include the files in the same order..

<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.16.1/lodash.min.js"></script>
<script src="https://github.com/cloudinary/pkg-cloudinary-core/blob/master/cloudinary-core.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/danialfarid-angular-file-upload/12.2.11/ng-file-upload-shim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.3/angular.min.js"></script>
<script src="https://github.com/cloudinary/cloudinary_angular/blob/master/js/angular.cloudinary.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/danialfarid-angular-file-upload/12.2.11/ng-file-upload.min.js"></script>

app.js - Inject the modules

var app = angular.module('myApp', [
  'cloudinary',
  'ngFileUpload'
]);

home.html - any view where you need the image upload option

<div class="container" ng-hide="loader">
  <div class="deal">
    <label class="black bold">Upload an Image</label>
    <br>
    <div id="direct_upload"
         ngf-drop="uploadFiles($files)"
         ngf-drag-over-class="dragOverClass($event)"
         ng-model="files"
         ng-multiple="true">
        <form>
            <div class="form_line">
                <div class="form_controls">
                    <div class="upload_button_holder">
                        <div href="#" class="button" style="width:150px;" ngf-select="uploadFiles($files)" multiple title="upload" resetOnClick="true" >Upload</div>
                    </div>
                </div>
                <br>
            </div>
        </form>
    </div>
    <br>
</div>

home.s - Appropriate Controller

app.controller('homeController', ['$scope','$http', 'Upload', 'cloudinary', function ($scope, $http, $upload, cloudinary) {

var cloud_name = "your cloud name";
var api_key = "your api_key";
var public_id = "your public_id";
var signature = "your signature";
var timestamp = "your timestamp";

$scope.uploadFiles = function(files){

    if(!files){
      return false;
    }        

    angular.forEach(files, function(file){
        if (file && !file.$error) {
            file.upload = $upload.upload({
              url: "https://api.cloudinary.com/v1_1/" + cloud_name + "/upload",
              data: {
                  timestamp: timestamp,
                  public_id: public_id,
                  api_key: api_key,
                  signature: signature,
                  file: file
              }
            }).progress(function (e) {
                file.progress = Math.round((e.loaded * 100.0) / e.total);
                file.status = "Uploading... " + file.progress + "%";
            }).success(function (data) {
                console.log('success');
                console.log(data);
                alert('success');
            }).error(function (data) {
                console.log('failed');
                console.log(data);
                alert('failed');
            });
        }
    });
};

}]);

Hope this helps :)