7
votes

I would like to use dropzone.js to upload files directly to Azure Blob Storage, with SAS (example here) to keep the files private.

As I understand it, the workflow would be:

  1. The user chooses a file
  2. The dropzone processing event fires. In the event handler, I call a method on my site's API which creates an Azure Blob URI to upload to, including the SAS query string
  3. The dropzone upload URL is set to the "secured" blob URL
  4. The upload begins

I have found the following wiki article showing how to set the dropzone URL dynamically (https://github.com/enyo/dropzone/wiki/Set-URL-dynamically)

Dropzone.options.myDropzone = {
  init: function() {
    this.on("processing", function(file) {
      // I need to do an async call here, to get the URL...
      this.options.url = "/some-other-url";
    });
  }
};

The problem is that the above example is synchronous. How can I delay the upload until the URL has been requested from my web api asynchronously?

Thanks

2
Great question! We need to do this as well. Before I dig too much into this library, do you mind letting me know if you were successful in using Dropzone to handle the Azure uploads? Were there any challenges along the way? With the URL I assume you can have the upload filename be something other than the original filename correct?John Livermore

2 Answers

4
votes

May be I am a bit late with answer

Use predefined SAS

Just save it in form element with data-sas attribute while rendering a page. The only drawback I see — your SAS could expire if page haven’t refreshed for a while. This is a part from my working code (other variants are from my head)

Dropzone.options.myDropzone = {
  method: "PUT",
  headers: {"x-ms-blob-type": "BlockBlob"},
  sending: (file, xhr) => {
    // To send file without wrappintng it to multipart/form-data,
    // Azure won’t unwrap it
    const _send = xhr.send;
    xhr.send = () => { _send.call(xhr, file) };
  },
  init: function() {
    const dz = this,
          action = dz.element.action,
          sas = dz.element.dataset.sas;

    dz.on("processing", (file) => {
      dz.options.headers["Content-Type"] = file.type;
      dz.options.url = `${action}/${file.name}?${sas}`;
    })
  },
}

Use async call in Dropzone’s init

Dropzone.options.myDropzone = {
  autoProcessQueue: false, // To prevent automatic auploading before getting SAS
  acceptedFiles: ".xls,.xlsx",
  method: "PUT",
  headers: {"x-ms-blob-type": "BlockBlob"},
  sending: (file, xhr) => {
    // To send file without wrappintng it to multipart/form-data,
    // Azure won’t unwrap it
    const _send = xhr.send;
    xhr.send = () => { _send.call(xhr, file) };
  },
  init: function() {
    let sas = null;
    const dz = this,
          xhr = new XMLHttpRequest();

    xhr.addEventListener("load",
        (event) => {
          sas = getSasFromEvent(event);
          dz.options.autoProcessQueue = true;
          dz.processQueue();
        }, true);
    xhr.open("GET", "/get-sas");
    xhr.send();

    dz.on("processing", (file) => {
      dz.options.headers["Content-Type"] = file.type;
      dz.options.url = `${action}/${file.name}?${sas}`;
    })
  },
}

Run Dropzone after you’ve got a SAS asynchrouniosly

See Create dropzones programmatically

2
votes

You could try a synchronous ajax call using jQuery.

function GetUrl() {
    var url = "";
    $.ajax({
        async: false,
        success: function(data) {
            url = data;
        }
        // Other opts   
    });
    return url;
}

Dropzone.options.myDropzone = {
  init: function() {
    this.on("processing", function(file) {
      this.options.url = GetUrl();
    });
  }
};