File Upload with ajax/javascript

I’m desperate for some help with uploading files. I created a Remote File Storage service to an Azure drive, from which I can successfully retrieve files.

I’m using a javascript library called Simple Ajax Uploader for my uploads. I was using it with a php handler successfully before.

I’ve looked at the few posts in this forum on file uploads, but to no avail.

I am also using the dreamfactory javascript sdk by jsykes for the rest of my app. So I’m happy for a solution that uses that or directly calls a url with ajax.

My best guess so far is this:

var fileName = $('#file').val();
var uploader = new ss.SimpleUpload({
      button: upButtons,
      url: 'http://myServer.cloudapp.net/rest/myFileService/myContainer/' + fileName + '?app_name=myAppName',
      name: 'uploadFile',
      cors: true,                  
      responseType: 'json',
      multipart: true,
      onSubmit: function(filename, extension) {
        ... stuff ...
      },
      onComplete: function(file, response) {
        ... stuff ...
      }
});

The result of which is a cors error, which I’ve come to realize with DreamFactory usually means there’s some non-cors error happening - I have cors turned on and it works correctly otherwise.

Any input or examples would be greatly appreciated!

The other method I’ve tried is this:

var file = $("#file")[0].files[0];
console.log(file);
$.ajax({
    beforeSend: function(request) {
        request.setRequestHeader("X-File-Name", file.name);
        request.setRequestHeader("Content-Type", file.type);
        //request.setRequestHeader("Access-Control-Allow-Origin", "*")
    },
    type: 'POST',
    url: 'http://myServer.cloudapp.net/rest/myFileService/myContainer/' + file.name + '?app_name=myAppName',
    data: file,
    cache: false,
    processData: false
});

But I still get a cors error.

Well, the cors issue was related to the header X-File-Name it seems.

I’m working with two code snippets now, and both return a 400 Bad Request error: “The requested URI does not represent any resource on the server.” I abandoned the upload library for now, just trying to get a working upload first.

WITH jsykes sdk:

function uploadFile() {
    var file = $("#file")[0].files[0];
    df.azurefiles.createFile({
        "container": "azurefiles",
        "file_path": file.name,
        "body": file
    });
};

WITHOUT sdk:

function uploadFile() {
    var file = $("#file")[0].files[0];
    $.ajax({
        beforeSend: function(request) {
            request.setRequestHeader("Content-Type", file.type);
            request.setRequestHeader("X-DREAMFACTORY-SESSION-TOKEN", sessionStorage.SESSION_TOKEN);
        },
        type: 'POST',
        url: 'http://myServer.cloudapp.net/rest/azurefiles/azurefiles/' + file.name + '?app_name=myAppName',
        data: file,
        cache: false,
        processData: false
    });
};

Hey Erik,

I need a few details about your files service. It seems as though you have a service called ‘azurefiles’ as well as a container called ‘azurefiles’. Is this correct?

That’s exactly right.

Good stuff. I just want to eliminate the easy things first. Go to api docs in that admin and click on your azurefiles service When it opens there should be three ‘GET’ rows at the top. The third of which will return all containers. If you click and open that option there will be a ‘Try it’ button at the bottom of the panel. When you click the ‘Try it’ button…what is the result?

{
  "container": [
    {
      "name": "azurefiles",
      "path": "azurefiles",
      "last_modified": "Wed, 14 Jan 2015 21:06:00 GMT",
      "access": [
        "GET",
        "POST",
        "PUT",
        "PATCH",
        "MERGE",
        "DELETE",
        "ADMIN"
      ]
    },
    {
      "name": "vhds",
      "path": "vhds",
      "last_modified": "Mon, 05 Jan 2015 22:44:24 GMT",
      "access": [
        "GET",
        "POST",
        "PUT",
        "PATCH",
        "MERGE",
        "DELETE",
        "ADMIN"
      ]
    }
  ]
}

Looks good. Scroll down in the api docs. Let’s check one more thing just for sanity. There should be another ‘GET’ option…This one should be SERVICE_NAME/{container}. There will be a container name field in the panel. Fill that in with your ‘azurefiles’ container name. This should return an empty container as nothing has been saved there.

Returns the following - I added a test image from the admin filemanager interface:

{
  "container": "azurefiles",
  "name": "azurefiles",
  "path": "azurefiles",
  "folder": [],
  "file": [
    {
      "name": "Jellyfish.jpg",
      "last_modified": "Sat, 24 Jan 2015 00:52:39 GMT",
      "content_length": 775702,
      "content_type": "image/jpeg",
      "content_encoding": null,
      "content_language": null,
      "path": "azurefiles/Jellyfish.jpg"
    }
  ]
}

Alright. Service looks configured correctly. Sorry to take you through that but we needed verification. So we should expect the same behavior from an api call. So lets try this. Create a function that calls the service. Just a simple jQuery GET for the service will do. Something along the lines:

$.get(‘YOUR_DSP_URL/rest/azurefiles’, function (data) {console.log(data)})

This will determine if we’re having CORS issues or if the problem lies elsewhere.

Also. Rather then trying to post with the file name in the url…Just post to the service/container with the file as the only thing in an array.

Ok. I did a simple GET on the service:

$.ajax({
    beforeSend: function(request) {
        request.setRequestHeader("X-DREAMFACTORY-SESSION-TOKEN", sessionStorage.SESSION_TOKEN);
    },
    type: 'GET',
    url: 'http://myServer.cloudapp.net/rest/azurefiles/azurefiles/?app_name=myAppName',
}).done(function(data) {
    console.log(data)
});

And got a response:

{"container":"azurefiles","name":"azurefiles","path":"azurefiles","folder":[],"file":[{"name":"Jellyfish.jpg","last_modified":"Sat, 24 Jan 2015 00:52:39 GMT","content_length":775702,"content_type":"image/jpeg","content_encoding":null,"content_language":null,"path":"azurefiles/Jellyfish.jpg"}]}

I also tried the POST without the file name:

var file = $("#file")[0].files[0];
$.ajax({
    beforeSend: function(request) {
        request.setRequestHeader("Content-Type", file.type);
        request.setRequestHeader("X-DREAMFACTORY-SESSION-TOKEN", sessionStorage.SESSION_TOKEN);
    },
    type: 'POST',
    url: 'http://myServer.cloudapp.net/rest/azurefiles/azurefiles/?app_name=myAppName',
    data: file,
    cache: false,
    processData: false
});

And got the response: 400 Bad Request: Invalid empty path.

I finally decided to test a file upload with the local db file service - duh. And it works with the code below:

var file = $("#file")[0].files[0];
$.ajax({
    beforeSend: function(request) {
        request.setRequestHeader("Content-Type", file.type);
        request.setRequestHeader("X-DREAMFACTORY-SESSION-TOKEN", sessionStorage.SESSION_TOKEN);
    },
    type: 'POST',
    url: 'http://myServer.cloudapp.net/rest/files/test/' + file.name + '?app_name=myAppName',
    data: file,
    cache: false,
    processData: false
});

On googling the 400 Bad Request error: “The requested URI does not represent any resource on the server.”, it looks like this is an Azure specific response. So I guess the issue lies there somewhere.

Is it possible that I need to configure my Azure container differently somehow? I’m able to upload files to it via the DF file manager just fine, but that uses a form post as opposed to ajax. Is it possible I need to edit the azurefiles Service Definition is some way? Or is this just a DF bug that no one else has encountered before?

This is a critical issue for me. Any additional help would be much appreciated.

Thanks!

I have alerted the server side guys to the matter. It seems your doing everything correctly on the client side. Someone will reach out to you today.

Thank you vary much Michael!

Instead of including the file name in the URL, put it in a header named X-File-Name. Doing it like that it worked for both Azure and local file storage. I’ve created a bug report for us to investigate why the Azure upload fails when file name is in the URL. It should work both ways.

Add header: request.setRequestHeader(“X-File-Name”, file.name);
Remove file name from URL: ‘http://myServer.cloudapp.net/rest/azurefiles/azurefiles?app_name=myAppName

Hi Todd. Thanks for the reply. I added the header and removed the file name from the url, and I’m now getting the CORS error that I was getting originally. This is true for both Azure and local file storage.

“Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://…cloudapp.net/rest/azurefiles/azurefiles?app_name=… This can be fixed by moving the resource to the same domain or enabling CORS.”

You can add X-File-Name to the list of allowed headers. It’s a file in the DSP server code. Might have to restart apache afterwards, not sure about that. We’re going to add X-File-Name to the next release but you can change it locally for now.

vendor/dreamfactory/lib-php-common-platform/src/Yii/Components/PlatformWebApplication.php

const CORS_DEFAULT_ALLOWED_HEADERS = 'X-File-Name,Content-Type,X-Requested-With,X-DreamFactory-Application-Name,X-Application-Name,X-DreamFactory-Session-Token,X-HTTP-Method,X-HTTP-Method-Override,X-METHOD-OVERRIDE';
2 Likes

Hazzaa! That did it. Thanks Todd!

Just a note, I’m using Bitnami, and the file to edit was found here: /opt/bitnami/apps/dreamfactory/htdocs/vendor/dreamfactory/lib-php-common-platform/src/Yii/Components/PlatformWebApplication.php

Thanks again!