Unable to dynamically generate file once deployed
Closed this issue · 2 comments
Software | Version |
---|---|
Operating System | macOS Mojave |
Jets | 1.9.30 |
Ruby | 2.5.3 |
Expected Behaviour
I'm expecting to be able to render binary content dynamically and serve it as a file.
Current Behavior
Locally this works just fine. I get this
However, once deployed instead of returning binary data it's returning base64 encoded data.
The differences are a little more clear when I run a console command
Local
HTTP/1.1 200 OK
Content-Type: image/png
Transfer-Encoding: chunked
X-Runtime: 0.048170
x-jets-base64: yes
+-----------------------------------------+
| NOTE: binary data not shown in terminal |
+-----------------------------------------+
Amazon
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 200
Content-Type: image/png
Date: Fri, 19 Jul 2019 18:41:53 GMT
Via: 1.1 8918721f9949345e08455e61518a59ed.cloudfront.net (CloudFront)
X-Amz-Cf-Id: MUNmNGVKbNkoPFZnE-N0LDy8MKS-Tp-9qJAmsl6FFglRt8gHkR38Eg==
X-Amz-Cf-Pop: IAD79-C1
X-Amzn-Trace-Id: Root=1-5d320ef0-482dda58bf0d8278e323c530;Sampled=0
X-Cache: Miss from cloudfront
X-Runtime: 0.045916
x-amz-apigw-id: dFdFpEwaoAMFlwg=
x-amzn-RequestId: e060b6a1-aa54-11e9-aa26-4b05e05efc18
x-jets-base64: yes
x-jets-call-count: 8
x-jets-prewarm-count: 1
iVBORw0KGgoAAAANSUhEUgAAAK8AAAB4AQAAAACs0p9PAAAAWElEQVR4nGP6
jw38Y2LACoau8G3v6KlXp4SlqU3zXX3Zx3t22OStMwaVA0eFR4VHhUeFR4VH
hUeFR4WpKKy6dWm2ds6qWbeyNofqbtmauirXO2NQOZAKwgDXJyraIZqw7QAA
AABJRU5ErkJggg==
Step-by-step reproduction instructions
jets new test1 --mode api --no-database --no-webpacker
cd test1
bundle install
jets generate controller generate
Then add this code to the controller
require 'barby'
require 'barby/barcode/code_39'
require 'barby/outputter/png_outputter'
class GenerateController < ApplicationController
def show
barcode = Barby::Code39.new("1234567890")
render plain: barcode.to_png,
base64: true,
content_type: 'image/png'
end
end
and update the routes.rb
Jets.application.routes.draw do
root "generate#show"
get 'generate', to: 'generate#show'
# The jets/public#show controller can serve static utf8 content out of the public folder.
# Note, as part of the deploy process Jets uploads files in the public folder to s3
# and serves them out of s3 directly. S3 is well suited to serve static assets.
# More info here: https://rubyonjets.com/docs/extras/assets-serving/
any "*catchall", to: "generate#show"
end
Solution Suggestion
I'm not familiar enough with the differences between the deployed environment to know what might be going wrong here.
Thanks for the details in the report.
Yeah, have played around with this a lot before.
It has to do with how API Gateway and binary content work together. API Gateway, will only serve the image correctly if certain Accept
headers are sent.
Does not work:
curl URL -H 'Accept: image/webp,image/*, **/** ;q=0.8' https://api-gateway-dns/image.png # responds with base64 text while
Works:
curl URL -H 'Accept: image/webp' https://api-gateway-dns/image.png # responds with image correclty
This is important because with web browsers, we don't control the Accept header when the URL is being hit directly. IE: An HTML img
tag. So they don't get served correctly.
This is not great behavior of API Gateway.
Some discussion is also in here with links to this known issue: https://community.rubyonjets.com/t/no-send-data-to-send-files-as-response/144/2
It's not ideal, but the recommendation is to upload assets to s3 and serve from there.
This is also a reason why files in the public
folder are automatically uploaded to s3 and jets serves them out of s3 as part of the jets deploy
process: Jets Assets Serving
S3 Lifecycle Policy
Looks like this app generates dynamic images and they are not meant to be kept around. If you end up taking the s3 approach, you may want to use an S3 Lifecycle Policy to automatically remove the images in the bucket.
Idea: Warning to User
Note, just had an idea. It'll be nice if there was some logic, maybe middleware, that throws an error or puts out warnings telling the user that it won't work locally before deploying. Though, unsure if it's worth it because API Gateway might provide a solution eventually.
Got it! Thank you for the very detailed response. We're not able to send custom headers for this project unfortunately. But good to know where the actual issue lies.