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