- Change from using `get_object` to `head_object` to check if the sitemap file exists before attempting to retrieve it. - Implement a presigned URL for accessing the sitemap, which is valid for 15 minutes. - Set cache headers to allow for 1 hour of caching. - Improved logging for better error tracking and debugging. This change enhances the efficiency of sitemap retrieval by reducing unnecessary data transfer and provides a more secure way to access the sitemaps through presigned URLs. It also improves error handling by logging specific errors related to missing sitemaps.
127 lines
3.4 KiB
Ruby
127 lines
3.4 KiB
Ruby
class SitemapsController < ApplicationController
|
||
include SitemapsHelper
|
||
before_action :set_bucket_name
|
||
|
||
def index
|
||
@sitemaps = list_sitemaps
|
||
respond_to do |format|
|
||
format.html
|
||
format.xml { render_sitemap_index }
|
||
end
|
||
rescue Aws::S3::Errors::ServiceError => e
|
||
Rails.logger.error "S3 Error: #{e.message}"
|
||
render status: :internal_server_error
|
||
end
|
||
|
||
def show
|
||
path = params[:path]
|
||
key = "sitemaps/#{path}"
|
||
|
||
Rails.logger.info "Requesting sitemap: #{path}"
|
||
|
||
begin
|
||
# 检查文件是否存在
|
||
s3_client.head_object(
|
||
bucket: @bucket_name,
|
||
key: key
|
||
)
|
||
|
||
# 生成预签名URL,设置15分钟有效期
|
||
signer = Aws::S3::Presigner.new(client: s3_client)
|
||
url = signer.presigned_url(
|
||
:get_object,
|
||
bucket: @bucket_name,
|
||
key: key,
|
||
expires_in: 15 * 60, # 15 minutes
|
||
# response_content_type: 'application/xml', # 确保正确的内容类型
|
||
response_content_disposition: "inline" # 在浏览器中直接显示
|
||
)
|
||
|
||
# 设置缓存头
|
||
response.headers["Cache-Control"] = "public, max-age=3600" # 1小时缓存
|
||
|
||
# 重定向到预签名URL
|
||
redirect_to url, allow_other_host: true, status: :found
|
||
|
||
rescue Aws::S3::Errors::NotFound
|
||
Rails.logger.error "Sitemap not found: #{path}"
|
||
render status: :not_found
|
||
rescue Aws::S3::Errors::ServiceError => e
|
||
Rails.logger.error "S3 Error: #{e.message}"
|
||
render status: :internal_server_error
|
||
end
|
||
end
|
||
|
||
# def show
|
||
# path = params[:path]
|
||
# Rails.logger.info "Sitemap: #{path}"
|
||
|
||
# begin
|
||
# response = s3_client.get_object(
|
||
# bucket: @bucket_name,
|
||
# key: "sitemaps/#{path}"
|
||
# )
|
||
|
||
# expires_in 12.hours, public: true
|
||
# content_type = response.content_type || "application/xml"
|
||
|
||
# send_data(
|
||
# response.body.read,
|
||
# filename: path,
|
||
# type: content_type,
|
||
# disposition: "inline"
|
||
# )
|
||
# rescue Aws::S3::Errors::NoSuchKey
|
||
# render status: :not_found
|
||
# rescue Aws::S3::Errors::ServiceError => e
|
||
# Rails.logger.error "S3 Error: #{e.message}"
|
||
# render status: :internal_server_error
|
||
# end
|
||
# end
|
||
|
||
private
|
||
|
||
def set_bucket_name
|
||
@bucket_name = Rails.env.production? ?
|
||
ENV.fetch("AWS_BUCKET", Rails.application.credentials.dig(:minio, :bucket)) :
|
||
ENV.fetch("AWS_DEV_BUCKET", Rails.application.credentials.dig(:minio_dev, :bucket))
|
||
end
|
||
|
||
def s3_client
|
||
@s3_client ||= Aws::S3::Client.new
|
||
end
|
||
|
||
def list_sitemaps
|
||
response = s3_client.list_objects_v2(
|
||
bucket: @bucket_name,
|
||
prefix: "sitemaps/"
|
||
)
|
||
|
||
response.contents.map do |object|
|
||
{
|
||
key: object.key.sub("sitemaps/", ""),
|
||
last_modified: object.last_modified,
|
||
size: object.size,
|
||
url: sitemap_url(object.key.sub("sitemaps/", ""))
|
||
}
|
||
end.reject { |obj| obj[:key].empty? }
|
||
end
|
||
|
||
def render_sitemap_index
|
||
base_url = "#{request.protocol}#{request.host_with_port}"
|
||
|
||
builder = Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml|
|
||
xml.sitemapindex(xmlns: "http://www.sitemaps.org/schemas/sitemap/0.9") do
|
||
@sitemaps.each do |sitemap|
|
||
xml.sitemap do
|
||
xml.loc "#{base_url}/sitemaps/#{sitemap[:key]}"
|
||
xml.lastmod sitemap[:last_modified].iso8601
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
render xml: builder.to_xml
|
||
end
|
||
end
|