S3をApacheのファイルシステムとして利用する

mod_rubyでさくっと実装してみた。

/usr/lib/ruby/1.8/apache/s3.rb

require 'base64'
require 'net/http'
require 'openssl'
require 'singleton'
require 'time'

Net::HTTP.version_1_2

module Apache
  class S3
    include Singleton

    BucketName = "my_bucket"
    AWSAccessKeyId = "<MyAWSAccessKeyId>"
    AWSSecretAccessKey = "<MyAWSSecretAccessKey>"

    def aws_sign(secret_access_key_id, date, bucket, path)
      string_to_sign = "GET\n\n\n#{date}\n/#{bucket}#{path}"
      digest = OpenSSL::HMAC.digest(OpenSSL::Digest::SHA1.new, secret_access_key_id, string_to_sign)
      Base64.encode64(digest).gsub("\n", '')
    end
  
    def get(bucket, path, access_key_id, secret_access_key_id)
      host = "#{bucket}.s3.amazonaws.com"
      date = Time.now.rfc2822
      signature = aws_sign(secret_access_key_id, date, bucket, path)

      header = {
        'Host' => host,
        'Date' => date,
        'Authorization' => "AWS #{access_key_id}:#{signature}"
      }

      content = nil
      response_header = {}

      Net::HTTP.start(host, 80) do |http|
        res = http.get(path, header)

        res.canonical_each do |k, v|
          response_header[k.downcase] = v
        end

        content = res.body
      end

      return content, response_header
    end

    def handler(r)
      content, header = get(BucketName, r.path_info, AWSAccessKeyId, AWSSecretAccessKey)
      r.content_type = header['content-type'] || 'application/octet-stream'
      r.headers_out['Content-Length'] = header['content-length'] if header.has_key?('content-length')
      r.print(content)
      return OK
    end
  end
end

httpd/conf.d/ruby.conf

LoadModule ruby_module modules/mod_ruby.so

RubyRequire apache/s3

<Location /s3>
  SetHandler  ruby-object
  RubyHandler Apache::S3.instance
</Location>


こんな感じで、S3にファイルを置いとくと…


~$ s3cmd ls -r s3://my_bucket/
2010-06-19 10:05 63037 s3://my_bucket/hanabi.jpg
2010-06-19 10:18 66449 s3://my_bucket/sub/tomato.jpg

こんな感じで参照できる。

http://apache-hostname/s3/hanabi.jpg
http://apache-hostname/s3/sub/tomato.jpg