Brad Buchanan Brad Buchanan - 7 months ago 29
Ruby Question

How do I create a presigned link to a particular version of an object using the Ruby AWS SDK v2?

I am using the Ruby AWS SDK (v2) to upload log files to a versioned S3 bucket. The log files are not public, but I would like to generate a presigned link to the log to make it available for a limited time via a chat integration. I want to link to a particular version, which this answer says is possible via the S3 console.

Documentation on

shows how to do this for an unversioned object (or the head version of a versioned object) but not for a particular version. The possible parameters to
are not well documented, and reading the source it looks like the parameters are just passed to
which is not S3-specific.


I think I've finally worked this out, though I'm still not sure I could trace the entire code path. In short: You can pass :version_id in the options parameter to presigned_url.

# Uploads a log to S3 at the given key, returning a URL
# to the file that's good for one hour.
# @param [String] bucket
# @param [String] key
# @param [String] body of the log to be uploaded
# @param [Hash] options
# @return [String] public URL of uploaded log, valid for one hour
# @raise [Exception] if the S3 upload fails
def upload_log(bucket, key, body, options={})
  # Upload log
  result = AWS::S3.create_client.put_object(
      bucket: bucket,
      key: key,
      body: body

  # Get presigned URL that expires in one hour
  options = {bucket: bucket, key: key, expires_in: 3600}
  options[:version_id] = result[:version_id] unless result[:version_id].nil?, options)

And here, everything I could trace about why this works:

  1. presigned_url passes its params argument through to @client.build_request (presigner.rb#L48).

  2. build_request eventually pushes those parameters onto request.context.params of the request it returns (documented in client/base_spec.rb#L91).

  3. From here my understanding is fuzzy; I expect that something like Aws::Rest::Request::Builder passes all the params along to create the Endpoint and the particular rules for this operation (which I'm unable to find) allow version_id to be added to the querystring.

In any case, it's working. Thanks for the pointer Michael!