问题
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>"}
回答1:
I've had to do something similar before. I converted the base64 string back into an image file via imagemagick
through the rmagick
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
and update
methods of the controller and use the @user.signature.attach
method as described here.
回答2:
Yes, this is possible.
Direct uploads are handled by the DirectUpload
class (or ActiveStorage.DirectUpload
depending on how you're accessing things). Normally DirectUpload
wants a File to read the image from but conveniently enough, a File
is a Blob so you can almost use a Blob
instead. Active Storage only wants a few things from its "file" and almost all those things are present in Blob
, the only missing thing is a name
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 a toBlob method:
The
HTMLCanvasElement.toBlob()
method creates aBlob
object representing the image contained in the canvas; this file may be cached on the disk or stored in memory at the discretion of the user agent.
First set up direct uploads as usual. Then add your own submit handler for the overall form that will look roughly like this:
const form = your_form_element;
const canvas = your_canvas_element;
const input = your_file_input;
canvas.toBlob(blob => {
// Fake out DirectUpload by manually adding a name.
blob.name = input.files[0].name;
const uploader = new DirectUpload(blob, input.dataset.directUploadUrl);
uploader.create((error, blob) => {
if(error) {
// Handle the error.
}
else {
// Add the <input type="hidden"> with the signature.
const hiddenField = document.createElement('input')
hiddenField.setAttribute('type', 'hidden');
hiddenField.setAttribute('value', blob.signed_id);
form.appendChild(hiddenField);
// And submit away...
form.submit();
}
});
}, 'image/jpeg', 0.95);
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.
来源:https://stackoverflow.com/questions/52156108/how-can-i-upload-an-image-to-s3-with-rails-active-storage-from-a-canvas-in