S3でホスティングをしている場合、リクエストされたオブジェクトが存在しなかった場合に、trailing slashをつけてリダイレクトする、という挙動をする。しかし、これが期待する動作ではなく、むしろ逆の動作をしてほしい場合には、index.htmlを用意してリダイレクト属性を付けるしかないようだ。

説明書

リクエストに対するS3の挙動

  • リクエスト通りのobjectが存在すれば、S3はそのファイルを返す
  • オブジェクトが存在しない場合、/を追加してディレクトリを見に行く
  • index.htmlが存在すれば、そのファイルを返す
  • index.htmlがなければエラーを返す

方針

  1. /path/to/hogeをcanonical URLとする
  2. /path/to/hoge/を/path/to/hogeにリダイレクトする

やること

  1. /path/to/hoge.html/path/to/hogeにアップロードする
  2. /path/to/hoge/index.htmlに空ファイルをアップロードする(/path/to/hogeへのリダイレクト属性をつける)

1. /path/to/hoge.html/path/to/hogeにアップロードする

require 'aws-sdk'
class S3Uploader
def initialize()
@bucket = Aws::S3::Bucket.new 'host.bucketname.s3'
@dest_dir = "#{Rails.root}/public"
end
def upload_html
paths.each do |path|
upload_article_html(path)
end
end
protected
def paths
['/year/month/day/title']
end
def upload_article_html(path)
file_name = "#{path}.html"
file_path = "#{@dest_dir}#{file_name}"
object_key = path.dup
object_key.slice!(0)
@bucket.object(object_key)
.upload_file(file_path,
cache_control: 'public, max-age=6000',
content_type: 'text/html')
end
end

2. /path/to/hoge/index.htmlに空ファイルをアップロードする(/path/to/hogeへのリダイレクト属性をつける)

空ファイルのindex.htmlは事前にFileUtils.touch(dest)とかで用意しておく。あとはこんな感じのコードでindex.htmlをアップロードできた。

require 'aws-sdk'
class S3RedirectionIndexUploader
def initialize()
@bucket = Aws::S3::Bucket.new 'host.bucketname.s3'
@dest_dir = "#{Rails.root}/public"
end
def upload_html
paths.each do |path|
upload_redirection_index_html("#{path}/index.html", path)
end
end
protected
def paths
['/year/month/day/title']
end
def upload_redirection_index_html(path, redirect_path)
file_path = "#{@dest_dir}#{path}"
object_key = path.dup
object_key.slice!(0)
@bucket.object(object_key).upload_file(file_path,
cache_control: 'public, max-age=6000',
content_type: 'text/html',
website_redirect_location: redirect_path)
end
end