将WickedPdf生成的PDF保存到Active Storage后损坏的排查与修复
问题描述
我希望将生成的PDF保存为Active Storage附件。
生成PDF并存储的代码:
pdf = WickedPdf.new.pdf_from_string( render_to_string(pdf: 'SurveyReportForm', template: '/screenings/test', layout: 'pdf', formats: :pdf) ) @screening.report.attach( io: StringIO.new(pdf), filename: "#{@screening.id}-report", content_type: 'application/pdf' )
代码运行无报错,但点击下载链接查看PDF时:
<%= link_to "Download Report", rails_blob_path(@screening.report, disposition: "attachment") %>
打开的PDF显示类似二进制/源码内容,开头为:
%PDF-1.4 1 0 obj << /Title (þÿ) /Creator (þÿwkhtmltopdf 0.12.6) /Producer (þÿQt 4.8.7...
请问问题出在哪里?如何排查是PDF生成环节还是存储环节的问题,以及该如何修复?
问题分析与解决
核心问题
你遇到的是编码不匹配问题:WickedPdf生成的PDF是二进制数据,但render_to_string默认返回UTF-8编码的字符串,直接转成StringIO会导致二进制数据被错误解析,存储后打开就会显示乱码的源码内容。
排查步骤
- 验证PDF生成环节:
在生成代码后,先把PDF写入本地文件测试:
打开本地的File.open('/tmp/test.pdf', 'wb') { |f| f.write(pdf) }test.pdf,如果能正常显示,说明生成环节没问题;如果还是乱码,那是WickedPdf的配置问题(比如模板编码、wkhtmltopdf参数)。 - 验证存储环节:
如果本地文件正常,那问题出在Active Storage的存储过程。检查StringIO的初始化是否正确:二进制数据需要用二进制模式的IO对象,而默认StringIO.new(pdf)是文本模式,会破坏二进制数据。
修复方案
方案1:修正StringIO模式
直接指定StringIO为二进制模式初始化:
@screening.report.attach( io: StringIO.new(pdf, 'rb'), # 启用二进制模式 filename: "#{@screening.id}-report.pdf", # 建议添加.pdf后缀,确保系统识别文件类型 content_type: 'application/pdf' )
方案2:使用Tempfile(更稳定)
用临时文件暂存二进制数据,避免字符串编码干扰:
temp_file = Tempfile.new(["#{@screening.id}-report", '.pdf'], binmode: true) begin temp_file.write(pdf) temp_file.rewind # 将文件指针移到开头,确保Active Storage能读取完整内容 @screening.report.attach( io: temp_file, filename: "#{@screening.id}-report.pdf", content_type: 'application/pdf' ) ensure temp_file.close temp_file.unlink # 清理临时文件 end
额外优化建议
- 给文件名加上
.pdf后缀,避免部分系统无法识别文件类型; - 尝试去掉
render_to_string中的formats: :pdf参数,因为WickedPdf的pdf_from_string已经专门处理PDF渲染,该参数可能导致额外的编码转换。
内容的提问来源于stack exchange,提问作者Will
相关产品推荐
相关产品推荐

