How use Object URL to Upload a file by Javascrpt?

I created an endpoint in my Backend where I securely generate a URL to upload a file, according to the API doc: https://developers.linode.com/api/v4/object-storage-buckets-cluster-id-bucket-object-url/#post

I'am using this javascript code to upload file from my browser:

handleUpload = async e => {
  const file = e.target.files[0];
  if (!file) return;

  const payload = await fetch(`${MY_API}/s3/direct_post`).then(res =>
    res.json()
  );

  const url = payload.url;
  const formData = new FormData();

  formData.append('file', file);

  const xml = await fetch(url, {
    method: 'PUT',
    body: formData
  }).then(res => res.text());

  console.log('RESULT = ', xml);
};

However, it results in a CORS problem:
<img alt="Image" src="https://image.prntscr.com/image/sSgiYg3bS6Sff8dVbRrUHw.png">

When I try to use Postman to upload the image, it results in the following problem:

<?xml version="1.0" encoding="UTF-8"?>
<Error>
    <Code>SignatureDoesNotMatch</Code>
    <RequestId>tx000000000000001eeca5f-005e6065ee-d8efda-default</RequestId>
    <HostId>d8efda-default-default</HostId>
</Error>

3 Replies

Based on this GitHub issue, it looks like there may be an extra HTTPS header included that is affecting the signature. You'll also want to make sure you're using the correct API endpoint for your bucket, i.e. https://api.linode.com/v4/object-storage/buckets/us-east-1/example-bucket/object-url. Postman is not held to the same CORS standards, which explains why you're getting a different message when using it. There's some information here that can help with the SignatureDoesNotMatch issue.

Hi @jyoo

Thank you for your help. I was able to solve the problem using the axios:

async function handleUpload(e){
  const file = e.target.files[0];
  if (!file) return;

  const payload = await fetch(`${MY_API}/s3/direct_post`).then(res =>
    res.json()
  );

  const url = payload.url;

  var options = {
    headers: {
      'Content-Type': 'image/png'
    }
  };

  return axios.put(url, file, options);
}

And in my Ruby server endpoint with this:

def s3_url
  RestClient.log = STDOUT
  begin
    url = "https://api.linode.com/v4/object-storage/buckets/us-east-1/#{ENV['LINODE_BUCKET']}/object-url"
    res = RestClient.post(url,
      {
        method: 'PUT',
        name: 'file.png',
        content_type: 'image/png'
      }.to_json,
      {
        Authorization: "Bearer #{ENV['LINODE_TOKEN']}",
        content_type: :json
      }
    )

    res_json = JSON.parse( res.body )
    render json: res_json
  rescue RestClient::ExceptionWithResponse => e
    render json: {
      error: e.response
    }, status: 422
  end
end

However, I would like to make these files publicly available, for example, that I could access as follows:

https://us-east-1.linodeobjects.com/LINODE_BUCKET/file.png

I've tried to make my Bucket public using s3cmd (s3cmd setacl s3://LINODE_BUCKET --acl-public) but even so the file is not public.

Do you know if there is any way to do this without having to generate a signed URL?

@BrunoMM

Hello, your issue seems very similar to my recent question here:

https://www.linode.com/community/questions/20533/how-to-upload-to-object-storage-from-client-side-then-save-a-public-url-for-that

Have you found a solution to your issue of making the file public?

Reply

Please enter an answer
Tips:

You can mention users to notify them: @username

You can use Markdown to format your question. For more examples see the Markdown Cheatsheet.

> I’m a blockquote.

I’m a blockquote.

[I'm a link] (https://www.google.com)

I'm a link

**I am bold** I am bold

*I am italicized* I am italicized

Community Code of Conduct