如何在Rails控制器中将wicked_pdf生成的PDF上传至S3存储桶
Got it, let's work through this problem together. Heroku's filesystem is ephemeral—any files you save locally will disappear when your dyno restarts—so we can skip writing to public or pdfs directories entirely. Instead, we'll upload the PDF directly from the string generated by wicked_pdf to S3, no local file required. Here's a step-by-step solution:
1. Add the AWS SDK Gem
First, you'll need the AWS SDK for S3 to interact with your bucket. Add this to your Gemfile:
gem 'aws-sdk-s3'
Run bundle install to install it.
2. Configure S3 Credentials
You can set up your S3 credentials either using Rails credentials or environment variables (ideal for Heroku).
Option A: Rails Credentials
Run rails credentials:edit in your terminal and add your AWS details:
aws: access_key_id: YOUR_AWS_ACCESS_KEY secret_access_key: YOUR_AWS_SECRET_KEY region: YOUR_AWS_REGION # e.g., us-east-1 bucket: YOUR_S3_BUCKET_NAME
Option B: Heroku Environment Variables
In your Heroku dashboard (or via CLI), set these config vars:
AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYAWS_REGIONAWS_BUCKET
3. Update Your Controller Code
Replace your current file-saving logic with code that uploads directly to S3. Here's a complete example:
def generate_user_pdf # Generate the PDF string with wicked_pdf (add template/layout options if needed) pdf_content = render_to_string( pdf: "user_detail", template: "users/show" # Replace with your actual template path ) # Initialize the S3 resource s3 = Aws::S3::Resource.new( access_key_id: Rails.application.credentials.dig(:aws, :access_key_id) || ENV['AWS_ACCESS_KEY_ID'], secret_access_key: Rails.application.credentials.dig(:aws, :secret_access_key) || ENV['AWS_SECRET_ACCESS_KEY'], region: Rails.application.credentials.dig(:aws, :region) || ENV['AWS_REGION'] ) # Define your custom S3 path (organize files however you want) # Example: Group by user ID and timestamp for uniqueness s3_file_path = "user_pdfs/#{current_user.id}/#{Time.now.strftime('%Y%m%d_%H%M%S')}_user_detail.pdf" # Upload the PDF content to S3 begin s3_bucket = s3.bucket(Rails.application.credentials.dig(:aws, :bucket) || ENV['AWS_BUCKET']) s3_object = s3_bucket.object(s3_file_path) s3_object.put( body: pdf_content, content_type: 'application/pdf', acl: 'public-read' # Use 'private' if you don't want public access ) # Get the URL to use (public URL or presigned URL for private files) pdf_url = s3_object.public_url # For public files # pdf_url = s3_object.presigned_url(:get, expires_in: 3600) # For private files (expires in 1 hour) # Optional: Save the URL to your database (e.g., current_user.update(pdf_url: pdf_url)) flash[:success] = "PDF uploaded successfully! URL: #{pdf_url}" rescue Aws::S3::Errors::ServiceError => e flash[:error] = "Failed to upload PDF: #{e.message}" end redirect_to your_path end
Key Notes:
- No Local Files: We're using the raw
pdf_contentstring directly, so we avoid Heroku's temporary filesystem limitations entirely. - Custom S3 Path: The
s3_file_pathvariable lets you organize your PDFs in S3 however you like—group by user, date, or any other logic that makes sense for your app. - ACL Settings: Adjust the
aclparameter based on your needs:public-read: Anyone can access the PDF via its URL.private: Only users with AWS credentials or a presigned URL can access it.
- Error Handling: The
begin-rescueblock catches any S3-related errors, so you can notify the user if something goes wrong.
That's it! This approach will let you generate and store your PDFs in S3 without relying on Heroku's local filesystem.
内容的提问来源于stack exchange,提问作者Haseeb Ahmad

