As stated in the title I'm trying to upload an image to my S3 bucket with rails' Active Storage from a element that is nested within a rails form.
So far I've been able to use
<%= f.input :signature, type: file_field(:user, :signature), %>
to upload an image with Active Storage. The User
class has_one_attached :signature
. The images upload correctly when I use a file_field, so that's not part of the problem.
So far my simple_form
has:
<div class="signature_pad text-center form-group">
<div class="signature_pad_heading">
Enter your Signature:
</div>
<div class="signature_pad_body">
<canvas id="signature_pad_input" height="145px" width="370px" style="height: 145px; width: 370px;" class="border" />
</div>
<div class="signature_pad_footer">
<button type="button" class="btn btn-default" onclick="signaturePad.clear()">Clear</button>
</div>
</div>
<%= f.input :signature, type: file_field(:user, :signature), value: "", as: :hidden %>
<%= f.submit "Save", class:'btn-primary btn-lg btn-md-wide', id: "signature_pad_save" %>
And my javascript is:
<script src="https://cdn.jsdelivr.net/npm/signature_pad@2.3.2/dist/signature_pad.min.js"></script>
<script>
const canvas = document.querySelector("canvas");
const signaturePad = new SignaturePad(canvas);
$('#signature_pad_save').click(function(event) {
if (signaturePad.isEmpty()){
alert('Please enter your signature.');
event.preventDefault();
} else {
$('#user_signature').val(
JSON.parse(
signaturePad.toDataURL()
);
}});
</script>
Using .toDataURL I'm able to get the base64 of the image, and everything I've read seems to point out that that's all I need to send to S3 through Active-Storage.
Finally:
What I send when I use .file_field
"signature"=>"<ActionDispatch::Http::UploadedFile:0x007f7a02ad4ef8 @tempfile=#<Tempfile:/tmp/RackMultipart20180903-3527-kked3g.png>, @original_filename="signature1.png", @content_type="image/png", @headers="Content-Disposition: form-data; name=\"user[signature]\"; filename=\"signature1.png\"\r\nContent-Type: image/png\r\n">},"
What I am sending when I try to insert the value just before the form submits
"signature"=>"#<ActiveStorage::Attached::One:0x007f7a01c8b4f0>"}
I've had to do something similar before. I converted the base64 string back into an image file via
imagemagick
through thermagick
gem.See the third answer on this question for the code: How to save a base64 string as an image using ruby.
You'd then have to add this to the
create
andupdate
methods of the controller and use the@user.signature.attach
method as described here.Yes, this is possible.
Direct uploads are handled by the
DirectUpload
class (orActiveStorage.DirectUpload
depending on how you're accessing things). NormallyDirectUpload
wants aFile
to read the image from but conveniently enough, aFile
is aBlob
so you can almost use aBlob
instead. Active Storage only wants a few things from its "file" and almost all those things are present inBlob
, the only missing thing is aname
property and you can add that yourself.So now we need to get a
Blob
out of the canvas. That's actually pretty easy because canvases have atoBlob
method:First set up direct uploads as usual. Then add your own submit handler for the overall form that will look roughly like this:
That assumes you want a JPEG, you can use other content types if you want or you can match it to the original. I've also left out some of the usual submit handler boilerplate. If you have set up the usual global direct upload handlers then they'll be used here for the progress bar and such; if you don't have those then you'll have to handle that yourself, the guide linked to above has pointers on all that.