Search code examples
azurefine-uploaderazure-blob-storage

FineUploader Wrong Getting Azure Blob Storage URI


I'm trying to implement FineUploader React library into my React app to upload files to my Azure Blob Storage.

Looks like for some reason, FineUploader is getting the blob storage URI wrong.

This is how I instanciate a FineUploader in my test component:

import React, { Component } from 'react';

import FineUploaderAzure from 'fine-uploader-wrappers/azure'
import Gallery from './gallery/index';

const uploader = new FineUploaderAzure({
    options: {
        cors: {
            expected: true,
            sendCredentials: true
        },
        signature: {
            endpoint: 'https://myapp.com/api/getsas'
        },
        request: {
            endpoint: 'https://myaccount.blob.core.windows.net/test-container'
        },
        uploadSuccess: {
            endpoint: 'https://myapp.com/api/done'
        }
    }
})

class Index extends Component {
    render() {
        return (
            <Gallery uploader={uploader} />
        )
    }
}

export default Index;

Here's the error I'm seeing in the console. Looks like FineUploader is using the wrong URI for the blob storage. enter image description here

Any idea what may be causing this?

UPDATE: As per @GauravMantri's suggestion, I changed endpoint to containerUrl in the options section but that didn't seem to help either. Here's what it looks like:

const uploader = new FineUploaderAzure({
    options: {
        cors: {
            expected: true,
            sendCredentials: true
        },
        signature: {
            endpoint: 'https://myapp.com/api/getsas'
        },
        request: {
            containerUrl: 'https://myaccount.blob.core.windows.net/test-container'
        },
        uploadSuccess: {
            endpoint: 'https://myapp.com/api/done'
        }
    }
})

Here's the SAS I'm getting when I send a request through Postman: The request I'm sending is:

http://example.com/api/files/get/sas?blobUri=https://myaccount.blob.core.windows.net/test-container/test.txt&_method=put

And here's the SAS I receive:

"?sv=2017-04-17&sr=b&sig=7pXTnI2r8uGyZms12T9cRvHg1XlLI53ZJtwPUwGElnY%3D&st=2017-12-28T14%3A02%3A56Z&se=2017-12-28T14%3A22%3A56Z&sp=w"

Solution

  • I was able to make it work. Basically there're a few things that one need to keep in mind:

    1. In your FineUploader config, you will need endpoint attribute and that should have the URL of the blob container where you want to upload. This is how configuration looks like in my code:

      var uploader = new qq.azure.FineUploader({
          debug: true,
          element: document.getElementById("uploader"),
          cors: {
              expected: true,
              sendCredentials: false
          },
          signature: {
              endpoint: 'http://localhost:63194/users/sas'
          },
          request: {
              endpoint: 'https://account-name.blob.core.windows.net/container-name'
          },
      
      })
      
    2. The API for getting Shared Access Signature (SAS) should return blob URL + SAS Token. The blobUrl parameter to the API should be the absolute URL of the blob. This is the code I used for API (please don't use this as is because the code below does not take into consideration the _method parameter):

      [Route("sas")]
      [HttpGet]
      public async Task<HttpResponseMessage> Sas(string blobUri)
      {
          var credentials = new StorageCredentials("account-name", "account-key");
          var blob = new CloudBlockBlob(new Uri(blobUri), credentials);
          var sasParameters = new SharedAccessBlobPolicy()
          {
              SharedAccessExpiryTime = DateTime.UtcNow.AddHours(1),
              Permissions = SharedAccessBlobPermissions.Write
          };
          var sasToken = blob.GetSharedAccessSignature(sasParameters);
          var returnValue = blob.Uri.AbsoluteUri + sasToken;
          var resp = new HttpResponseMessage(HttpStatusCode.OK);
          resp.Content = new StringContent(returnValue, System.Text.Encoding.UTF8, "text/plain");
          return resp;
      }
      

    I downloaded Fine Uploader Azure related files from here: https://fineuploader.com/customize.html and used it to create a simple HTML page to test it. Here's what my HTML page looks like:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link href="fine-uploader-gallery.min.css" rel="stylesheet">
        <script src="azure.fine-uploader.min.js""></script>
        <script type="text/template" id="qq-template">
            <div class="qq-uploader-selector qq-uploader qq-gallery" qq-drop-area-text="Drop files here">
                <div class="qq-total-progress-bar-container-selector qq-total-progress-bar-container">
                    <div role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" class="qq-total-progress-bar-selector qq-progress-bar qq-total-progress-bar"></div>
                </div>
                <div class="qq-upload-drop-area-selector qq-upload-drop-area" qq-hide-dropzone>
                    <span class="qq-upload-drop-area-text-selector"></span>
                </div>
                <div class="qq-upload-button-selector qq-upload-button">
                    <div>Upload a file</div>
                </div>
                <span class="qq-drop-processing-selector qq-drop-processing">
                    <span>Processing dropped files...</span>
                    <span class="qq-drop-processing-spinner-selector qq-drop-processing-spinner"></span>
                </span>
                <ul class="qq-upload-list-selector qq-upload-list" role="region" aria-live="polite" aria-relevant="additions removals">
                    <li>
                        <span role="status" class="qq-upload-status-text-selector qq-upload-status-text"></span>
                        <div class="qq-progress-bar-container-selector qq-progress-bar-container">
                            <div role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" class="qq-progress-bar-selector qq-progress-bar"></div>
                        </div>
                        <span class="qq-upload-spinner-selector qq-upload-spinner"></span>
                        <div class="qq-thumbnail-wrapper">
                            <img class="qq-thumbnail-selector" qq-max-size="120" qq-server-scale>
                        </div>
                        <button type="button" class="qq-upload-cancel-selector qq-upload-cancel">X</button>
                        <button type="button" class="qq-upload-retry-selector qq-upload-retry">
                            <span class="qq-btn qq-retry-icon" aria-label="Retry"></span>
                            Retry
                        </button>
    
                        <div class="qq-file-info">
                            <div class="qq-file-name">
                                <span class="qq-upload-file-selector qq-upload-file"></span>
                                <span class="qq-edit-filename-icon-selector qq-btn qq-edit-filename-icon" aria-label="Edit filename"></span>
                            </div>
                            <input class="qq-edit-filename-selector qq-edit-filename" tabindex="0" type="text">
                            <span class="qq-upload-size-selector qq-upload-size"></span>
                            <button type="button" class="qq-btn qq-upload-delete-selector qq-upload-delete">
                                <span class="qq-btn qq-delete-icon" aria-label="Delete"></span>
                            </button>
                            <button type="button" class="qq-btn qq-upload-pause-selector qq-upload-pause">
                                <span class="qq-btn qq-pause-icon" aria-label="Pause"></span>
                            </button>
                            <button type="button" class="qq-btn qq-upload-continue-selector qq-upload-continue">
                                <span class="qq-btn qq-continue-icon" aria-label="Continue"></span>
                            </button>
                        </div>
                    </li>
                </ul>
    
                <dialog class="qq-alert-dialog-selector">
                    <div class="qq-dialog-message-selector"></div>
                    <div class="qq-dialog-buttons">
                        <button type="button" class="qq-cancel-button-selector">Close</button>
                    </div>
                </dialog>
    
                <dialog class="qq-confirm-dialog-selector">
                    <div class="qq-dialog-message-selector"></div>
                    <div class="qq-dialog-buttons">
                        <button type="button" class="qq-cancel-button-selector">No</button>
                        <button type="button" class="qq-ok-button-selector">Yes</button>
                    </div>
                </dialog>
    
                <dialog class="qq-prompt-dialog-selector">
                    <div class="qq-dialog-message-selector"></div>
                    <input type="text">
                    <div class="qq-dialog-buttons">
                        <button type="button" class="qq-cancel-button-selector">Cancel</button>
                        <button type="button" class="qq-ok-button-selector">Ok</button>
                    </div>
                </dialog>
            </div>
        </script>
    
        <title>Fine Uploader Gallery UI</title>
    </head>
    <body>
        <div id="uploader"></div>
        <script>
            // Some options to pass to the uploader are discussed on the next page
            var uploader = new qq.azure.FineUploader({
                debug: true,
                element: document.getElementById("uploader"),
                cors: {
                    expected: true,
                    sendCredentials: false
                },
                signature: {
                    endpoint: 'http://localhost:63194/users/sas'
                },
                request: {
                    endpoint: 'https://account-name.blob.core.windows.net/container-name'
                },
    
            })
        </script>
    </body>
    </html> 
    

    Once I ran this code, I was able to upload files in my blob container without any problems.