0

Uploading a file the Rails way looks like this in console:

Started POST "/onboard/add_doc" for 2600:1700:ba00:3970:1900:ab4c:b842:8b5c at 2021-03-13 12:42:47 -0800
Processing by Users::DocsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"[FILTERED]", "doc"=>{"image"=>#<ActionDispatch::Http::UploadedFile:0x00007fe0fef3de78 u/tempfile=#<Tempfile:/var/folders/hj/g_90jx_n7s58m73qlf9h0c4w0000gn/T/RackMultipart20210313-97062-1pd54fh.jpg>, u/original_filename="Gil-Next-Door-Gil-and-Wolly.jpg", u/content_type="image/jpeg", u/headers="Content-Disposition: form-data; name=\"doc[image]\"; filename=\"Gil-Next-Door-Gil-and-Wolly.jpg\"\r\nContent-Type: image/jpeg\r\n">}, "commit"=>"Upload"}

However I am not doing that. A js library called Uppy handles file selection and upload, and inserts the JSON into a hidden form field. When this form is submitted, it is not automatically converted into a ActionDispatch::Http::UploadedFile object. I'm not entirely sure why, so I handle that in the controller:

def create
    image = params.require(:doc).fetch(:image)
 
    file = StringIO.new(image)
    lockbox = Lockbox.new(key: Lockbox.attribute_key(table: "docs", attribute: "image"))
    encrypted_image = lockbox.encrypt_io(file)

    @doc = Doc.new(user_id: current_user.id, image: encrypted_image, mime_type: mime_type)
    @doc.save
end

Console output looks like this:

Processing by Users::IdDocsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"[FILTERED]", "doc"=>{"image"=>"{\"id\":\"6536f20f163a67f5b432d250351eac7c.jpg\",\"storage\":\"cache\",\"metadata\":{\"size\":55368,\"filename\":\"cam-1615814031784.jpg\",\"mime_type\":\"image/jpeg\"}}"}, "commit"=>"Upload"}

That output is fine (I think), but the file is not properly encrypting before upload. I'm getting blank white squares when I view them, and Lockbox needs an ActionDispatch::Http::UploadedFile object. Is my uploaded_file definition missing something? How can I access uploaded_file attributes like headers to make sure its being submitted correctly?

calyxofheld
  • 1,538
  • 3
  • 24
  • 62
  • 1
    I am using shrine and Uppy with direct to S3 upload and don't really need any of what you have in your create method. Maybe you can give us context: direct to S3, XHR, the model name and field name attached to Shrine, where are you saving the file, you talked about encryption but you not seem to be sure you are uploading unencrypted files... ? – Maxence Mar 14 '21 at 23:40
  • This is indeed uploading to S3. You're so right that I didn't need half of what i had in my `#create` action. What happened is: I started out submitting a single `:image` through a `file_field`. I grabbed the `params.require(:doc).fetch(:image)` through the controller, and then encrypted it. Checking the class like `puts image.class` shows that what's being encrypted is an `ActionDispatch::Http::UploadedFile`. – calyxofheld Mar 15 '21 at 05:27
  • When I replaced the `file_field` with Dashboard and Webcam, the first thing I saw was that `image` was no longer posting as an `ActionDispatch::Http::UploadedFile` object - it was going through as a JSON string. Therefore I assumed that I should convert said string into some sort of File or IO object prior to encryption. Encrypting the string give this error: `NoMethodError (undefined method 'read' for #)` which suggests that I am right, and that the string needs to be made into something file-like. – calyxofheld Mar 15 '21 at 05:29
  • Problem is, nothing seems to work? `file = StringIO.new(image)` and the ridiculous confusion in my OP are both incorrectly set up files? I think? Because they're encrypting, but the fact that they're "broken" when decrypted I think implies that I am setting up the file object incorrectly prior to encryption – calyxofheld Mar 15 '21 at 05:31
  • 1
    If your model is File and the Uppy attachment is Image, you can just make Image a strong param and if the form is multipart: true then when creating a new record the image will be attached to the record and uploaded by shrine. Though I am not too sure how the Webcam plugin works. If you just want to take a snapshot from webcam it souldn't be that different .. Maybe you can also check with the Uppy community https://community.transloadit.com/c/uppy/5 – Maxence Mar 15 '21 at 09:04
  • out of curiousity, what is your setup? are you using the Dashboard? that's where my problem starts. instead of submitting through `file_field` it submits through a target on a form element, which means it doesn't submit as a param – calyxofheld Mar 16 '21 at 17:50
  • correction: it submits as a param, but the association to, say, `File.image` is lost. submitted uppy params look like this: `"uppyResult"=>"[{\"successful\":[{\"source\":\"Dashboard\",\"id\":\"uppy-61b2edbc996d484e3688664dbb38fd51/jpg-1e-image/jpeg-12757-1613979890771\"}].........etc.........}]` – calyxofheld Mar 16 '21 at 22:12
  • 1
    Uppy does not to anything on its own. You can make Uppy send with any params you desire. Actually I am using no field at all in my Uppy instance in the DOM. It only looks like this : `
    ` and my Js is like this : https://gist.github.com/maxence33/c89d7f369bb285703522255bdb085ba4 As you can see in my AJAX call I pass data as `profilepic: { image: uploadedFileData }` It will be the name of the param: `params[:profilepic][:image]`
    – Maxence Mar 17 '21 at 09:42
  • this helps so much THANK YOU @Maxence – calyxofheld Mar 17 '21 at 20:50
  • 1
    Glad it helps. As you can see it is Direct to S3 with a presign. Uppy uploads to S3 then pass the newly uploaded file data (just as if it was an image) to the rails controller in the Ajax call. I don't have XHR at hand but it is the same principle (without the presign and upload to S3) – Maxence Mar 18 '21 at 00:20

0 Answers0