Uploading a File to a Note using REST Api

I’m trying to upload a file to a Note in shotgun using the REST Api. Here is my ajax call. However I keep getting an error. What is a signature? Why do i need this?

The error:

{“errors”:[{“id”:1,“status”:400,“code”:103,“title”:“Signature did not match”,“source”:{},“detail”:null,“meta”:null}]}

My code is below

$.ajax({
    url: HOST + '/api/v1/entity/note/6716/_upload?filename=string&signature=OaHsK%2BrkZk5w1GxgxI3aNmJ0H3Y%3D&user_id=1&user_type=HumanUser&expiration=1532618412',
    method: 'put',
    body: {
    "upload_info": {
    "etags": ["6d2ecc0fe4b5d1bq93ba9546316dd6f87", "32edb9df974fa3154e02q59525440e4b9"],
    "upload_id": "zE1dma8DPHq5ZwtXAbPS1P8C4WWymt3OCX13VUSdonry7BwD7fkWbFIejYByIJqKqS9DTwb5grPmOZy2UxbFe5..8C6XilqMJrPrnuaIMrqU.LRc6pzdRypxqY1FrBfrYExWnO",
    "upload_type": "Attachment",
    "original_filename": "bbb_sunflower_1080p_30fps_normal.mp4",
    "multipart_upload": true,
    "timestamp": "2018-04-04T00:06:48Z",
    "storage_service": "s3"
    },
    "upload_data": {
    "display_name": "Big Buck Bunny"
    }
    },
    headers: headers,
    success: function (data) {
    console.log(JSON.stringify(data));
    }
    });
1 Like

I have not found anywhere in the docs that explains what a Signature is, but yet it’s required.

signature > query > string > true > (Signature to validate the request)

1 Like

Hey @JokerMartini,

The Signature did not match error is not particular to Shotgun but rather to S3. Due to the way objects are stored and accessed from S3 by Shotgun given it’s security measures, I believe there is always a requirement of a pre-signed expiring url, whether downloading/displaying or uploading.

Please go through the file handling section of the docs to understand how to generate a pre-signed url and subsequently upload your file. Please note that any pre-signed url will expire after a set time and you will need to regenerate a pre-signed url everytime you initiate a new upload. (Except perhaps for multi-part upload)

2 Likes

@JokerMartini There are also a few code samples that can help see how to walk through the process of uploading a file.

Specifically here: https://gist.github.com/daigles/ff958b8b3ed695329d371e5d500acb0a#file-rest_upload_download_sample-py-L136, showing that you need to first get the upload link from Shotgun. The signature will be included in this link that you can then use to upload your data.

1 Like

How can I upload an image using multipart with javascript? That is what i need to do.

When i try to use the Multi-part upload. i get the Title Not Found error. But there is no argument required or showing that.

I noticed if the sample online it says for multipart to do the following. However i do not believe the ? is suppose to be in there.

data: '?filename=string&signatur...

It would be great if there was a full example of this using javascript to understand all the parts required. it says you need a user_id but how do i get that? I’m logging in using a secret and key.

Thanks for your help.

1 Like

Did anyone have more information on what was going wrong here?

1 Like

I feel like my original post if very close to working, does the shotgun team have any suggestions on what im doing wrong here?

1 Like

@daigles I’ve been looking deeper into your github sample which has been quite helpful. The one thing I’m curious about is how do you determine the Content-Type dynamically? I see that in your sample you are hard coding jpg, but i would like to give user the ability to choose videos or images. So how can i make the type dynamic in that regard?

Do i even have to supply the type?

1 Like

Hi @JokerMartini There is a Python package that can help with the detection of the media type of a file.

Specifying te content type will help browsers (and possibly other client apps) to know how to display the file (eg. show the file content or pop a “download” dialog).

I hope this helps…

1 Like

I have the following so far

  1. Access token
  2. Get Upload URL
  3. (stuck) Upload file to record

How do i use the actual upload url to upload the file ?

/*******************************************************************************
Methods 
*******************************************************************************/
function get_access_token() {
  /*
  Description:
      Returns the access token
  */
  return $.ajax({
    url: host + '/api/v1/auth/access_token',
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      'Accept': 'application/json',
    },
    data: {
      grant_type: 'client_credentials',
      client_id: client_id,
      client_secret: client_secret
    },
    success: function(data) {
      /* console.log(JSON.stringify(data, null, 3)); */
      /* return data; */
    },
    error: function(data) {
      /* console.log(data.responseText); */
      /* return null; */
    }
  })
}

function get_upload_url(access_token, entity) {
  let body = {
    filename: 'myImage.png'
  }

  return $.ajax({
    url: host + `/api/v1/entity/${entity.type}/${entity.id}/_upload`,
    method: 'get',
    data: 'filename=myImage.png?multipart_upload=true',
    headers: {
      'Accept': 'application/json',
      'Authorization': `Bearer ${access_token}`
    },
    success: function(data) {
      console.log(JSON.stringify(data));
    }
  })
}

function upload_file_to_record(access_token, data) {
// https://developer.shotgunsoftware.com/rest-api/#get-upload-url-for-record
  return $.ajax({
    url: 'https://yoursite.shotgunstudio.com/api/v1/entity/{entity}/{record_id}/_upload',
    method: 'put',
    data: '?filename=string&signature=string',
    headers: {
      'Content-Type': '*/*',
      'Accept': 'application/json',
      'Authorization': `Bearer ${access_token}`
    },
    success: function(data) {
      console.log(JSON.stringify(data));
    }
  })
}

// handle click and add class
button.on("click", function() {
  $("#message").html('Busy...')

  // access token
  get_access_token().done(function(response1) {
    $("#output").val(JSON.stringify(response1, null, 3));
    console.log(response1);

    entity = {
      type: 'note',
      id: 7003
    };

    // get upload url
    get_upload_url(response1.access_token, entity).done(function(response2) {
      $("#output").val(JSON.stringify(response2, null, 3));
      console.log(response2);

      // upload file to record
      upload_file_to_record(response1.access_token, entity).done(function(response3) {
        $("#output").val(JSON.stringify(response3, null, 3));
        console.log(response3);
      })
    })
  })
})
1 Like

@JokerMartini Did you ever figure this out? Like you I’ve been unable to find a clear example.