Skip to content

Instantly share code, notes, and snippets.

@stulentsev
Last active November 25, 2015 14:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stulentsev/3c15ad589797e471afc6 to your computer and use it in GitHub Desktop.
Save stulentsev/3c15ad589797e471afc6 to your computer and use it in GitHub Desktop.

Uploading files to TextMaster

TextMaster uses AWS S3 for storage, so s3-compatible libraries/toolkits can be used (for example, EvaporateJS). However, it is quite simple to do it with a basic HTTP client library, which is capable of doing GET/PUT requests.

The process consists of 4 steps:

  1. Obtain a file you want to upload
  2. Construct HTTP PUT request that will upload the file
  3. Obtain a signature for the upload request
  4. Send the signed request

1. Obtain a file

This is rather easy. Normally you'd have a filename and be able to read it. But in some environments (javascript/browser), user has to manually select it.

filename = '/path/to/example.txt'

2. Prepare parameters for the PUT request

Nothing extraordinary here. You need three parts: request uri, request body and request headers.

Request URI

path = 'api_uploads/5655a5a20ed4c0153d000000/example.txt'
#        ^ static  | ^ your document id     |  ^ filename to be used      
s3_uri = "http://files.textmaster.com/#{CGI.escape(path)}"
#                                      ^ url-escape 

Request body

This is where actual file content will be sent.

s3_params = {
  Key: 'example.txt',
  ContentType: 'text/plain',
  Body: File.read(filename),
}

Request headers

s3_headers = {
  'authorization' => authorization, # see below
  'x-amz-acl' => 'public-read', 
  'x-amz-date' => Time.now.httpdate, # => 'Wed, 25 Nov 2015 12:09:15 GMT'
}

3. Authorization / Signature

Value that you need to pass in "authorization" is a S3 REST API header and has this form:

AWS AKIAJEMX5DWXUXX5RX3A:<signature>

To obtain the signature, you need to construct a correct string_to_sign and send it to http://api.textmaster.com/v1/clients/s3_signatures/new. Its construction is very thoroughly explained in S3 documentation. For this example, string to sign will look like this:

string_to_sign = "PUT

application/x-www-form-urlencoded

x-amz-acl:public-read
x-amz-date:Wed, 25 Nov 2015 12:09:15 GMT
/files.textmaster.com/api_uploads/5655a5a20ed4c0153d000000/example.txt"

Note the empty lines: first one is for missing Content-MD5 value (optional). The second one is for Date, which is overridden by x-amz-date header (some toolkits/libs won't let you set the Date header).

application/x-www-form-urlencoded is Content-Type of the request with which you will be sending file data. For multipart uploads it'll be multipart/form-data, naturally.

After you have this string, you send a GET to http://api.textmaster.com/v1/clients/s3_signatures/new, passing string to sign in the to_sign parameter. As this endpoint is not publicly accessible, you will also need to provide a TextMaster API signature.

require 'rest_client'

signer_uri = "http://api.textmaster.com/v1/clients/s3_signatures/new"

signer_headers = {
  # limitation of RestClient used in this example. This will be extracted and
  # sent as proper request parameters, not as a header value.
  params: { 
    to_sign: string_to_sign,
  },
  
  apikey: '<TM api key>',
  date: 'Wed, 25 Nov 2015 12:09:15 GMT',
  signature: '<TM api signature>',
}

signature = RestClient::Request.execute(method: :get, 
                                        url: signer_uri,
                                        headers: signer_headers)

puts signature # 'Oe9O8iBUfUxUrbMNXoAKHfMwd5o='

4. Uploading the file, finally.

This is the simplest step:

RestClient.put s3_uri, s3_params, s3_headers

If you get back an HTTP 200 OK, then upload went successfully and your file is accessible at http://files.textmaster.com/api_uploads/5655a5a20ed4c0153d000000/example.txt. If you get back an HTTP 403 Forbidden, then your signature is invalid (most likely cause is string_to_sign being slightly incorrect).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment