You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Sidekiq任务场景下渲染erb格式的EPUB内嵌CSS文件?

解决方案:在非控制器环境下渲染EPUB的ERB CSS模板

我来给你几个实用的方案,帮你在服务、Sidekiq任务或者模型回调这类非控制器环境里,顺利渲染epub/style.css.erb模板:

方案一:直接用Ruby ERB库手动渲染(最轻便)

不需要依赖Rails控制器的render_to_string,直接用Ruby标准库的ERB类来处理模板,完全脱离控制器环境,特别适合Sidekiq或回调场景。

代码示例:

class EpubGenerationService
  def generate_css
    # 读取模板文件的绝对路径
    template_path = Rails.root.join('app', 'views', 'epub', 'style.css.erb')
    template_content = File.read(template_path)

    # 初始化ERB对象,绑定当前服务的上下文(模板里可以直接用服务的变量/方法)
    erb = ERB.new(template_content)
    # 渲染出最终的CSS字符串
    erb.result(binding)
  end

  # 举个例子:模板里可以调用服务的方法或变量
  def primary_color
    '#2c3e50'
  end

  attr_reader :font_size
  def initialize(font_size: '16px')
    @font_size = font_size
  end
end

说明:

  • 模板里可以直接使用服务中定义的变量(比如@font_size)和方法(比如primary_color),因为binding会把当前服务的上下文传递给ERB。
  • 这个方法完全不依赖Rails的控制器栈,性能好,也不会有请求上下文的问题。

方案二:用Rails视图渲染器(适合依赖视图助手的场景)

如果你的CSS模板用到了Rails的视图助手(比如asset_path、image_url这类),可以用Rails提供的ActionController::Renderer,它能在非控制器环境下模拟视图渲染环境,比手动创建控制器更优雅。

代码示例:

class EpubGenerationService
  def generate_css
    # 创建渲染器,指定一个控制器作为模板的上下文(用来继承视图助手)
    renderer = ApplicationController.renderer.new(
      http_host: Rails.application.config.action_controller.default_url_options[:host],
      https: Rails.env.production?
    )

    # 渲染模板,传递需要的变量
    renderer.render(
      template: 'epub/style.css',
      formats: [:css],
      layout: false,
      assigns: {
        font_family: 'Georgia',
        primary_color: '#2c3e50'
      }
    )
  end
end

说明:

  • assigns里的键会变成模板中的实例变量(比如assigns: { font_family: 'Georgia' }对应模板里的@font_family)。
  • 必须设置http_host,不然像asset_path这类助手生成的路径会不正确。

方案三:创建轻量控制器专门渲染模板(兼容复杂场景)

如果上面两种方案满足不了你的需求(比如需要复用控制器里的过滤器逻辑,或者模板依赖复杂的请求参数),可以创建一个专门的控制器,然后在服务里模拟请求环境调用它的render_to_string。

第一步:创建控制器

class EpubTemplatesController < ApplicationController
  # 跳过所有不需要的过滤器,避免认证、CSRF检查等干扰
  skip_before_action :verify_authenticity_token, :authenticate_user!

  def style_css
    # 从参数或服务传入的变量设置实例变量
    @font_family = params[:font_family] || 'Arial'
    @primary_color = params[:primary_color] || '#2c3e50'
    # 渲染模板,指定格式为CSS,不使用布局
    render template: 'epub/style.css', formats: [:css], layout: false
  end
end

第二步:在服务里调用控制器

class EpubGenerationService
  def generate_css
    # 模拟请求环境
    env = {
      'HTTP_ACCEPT' => 'text/css',
      'PATH_INFO' => '/epub_templates/style_css',
      'REQUEST_METHOD' => 'GET',
      'QUERY_STRING' => 'font_family=Georgia&primary_color=#2c3e50'
    }

    # 初始化控制器并设置请求/响应对象
    controller = EpubTemplatesController.new
    controller.request = ActionDispatch::Request.new(env)
    controller.response = ActionDispatch::Response.new

    # 调用render_to_string得到CSS内容
    controller.render_to_string(action: :style_css, layout: false)
  end
end

说明:

  • 这个方案适合需要完全模拟HTTP请求的场景,但相对繁琐,尽量优先用前两种方案。
  • 一定要跳过不必要的过滤器,否则可能会出现认证失败、CSRF错误等问题。

内容的提问来源于stack exchange,提问作者Simon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 04:22:14