File uploads, access File binary

The context is I upload files and I want to access to the File object (the one you normally get in the browser with event.target.files. I want to do some work on it with the Image or Vix library, something like Vix.Vips.Image.new_from_buffer (no idea if this works at this stage).

The file input is captured in the live_file_input under the attribute uploads. I want to access it in the handle_event("validate"...) stage. It must be accessible somehow since it is pre-rendered, probably via a createObjectURL. If I inspect the socket.assigns at this stage, I don’t find any binary, mostly metadata. Is this accessible at this stage? Or if not, where is the binary of the file accessible?

I tried a few of the keys below:

get_in(socket.assigns, [:uploads, :image_list]) |> Map.keys()
[:external, :name, :accept, :chunk_size, :writer, :__struct__, :errors, :ref,
 :max_entries, :max_file_size, :cid, :entries, :auto_upload?, :progress_event,
 :allowed?, :chunk_timeout, :entry_refs_to_metas, :acceptable_types,
 :acceptable_exts, :client_key, :entry_refs_to_pids]

The entries doesn’t seem to include a binary.

  %Phoenix.LiveView.UploadEntry{
    progress: 0,
    preflighted?: false,
    upload_config: :image_list,
    upload_ref: "phx-F4EePzKBzRijVC6C",
    ref: "0",
    uuid: "3b4fa1c5-ba8a-41d5-adbb-ed4b274b12ad",
    valid?: true,
    done?: false,
    cancelled?: false,
    client_name: "Screenshot 2023-08-04 at 21.04.26.png",
    client_relative_path: "",
    client_size: 16701,
    client_type: "image/png",
    client_last_modified: 1691175871713
  }
]

If you’re using live view uploads, then you can get access to the binary in the consume_uploaded_entry(ies) to work with the Image library. That’s one way I know of and how I currently do it in Metamorphic to encrypt the avatar blob for people.

You can also try the method described in this posting, depending on your use case.

On phone :slight_smile:

I tried consume_uploaded_entries from the ā€œsaveā€ stage but this expects the progress to be true or equivalent, and fails in the ā€œvalidateā€ stage. I will look at the post you mention.

Is that the case with auto_upload: true set?

Yes, in the ā€œvalidateā€ stage,

(ArgumentError) cannot consume uploaded files when entries are still in progress

It also appears consumed in the ā€œsaveā€ stage. Only meta in entry. It only appears in the client Uploaders.S3 as a {file: File} blob.

Maybe the handle_progress can help.

Files being available to the client doesnā€˜t mean theyā€˜re available on the server. You need to upload the file (not just select it for upload) before itā€˜s available on the server.

Depending on what youā€˜re doing the new UploadWriter support might be useful.

Yes, indeed, I was presuming. I don’t necessarily want to save it, but I want to process the image. Plenty of solutions. Could be easier to save it on S3 and use the URL. This UploadWriter could useful too, and I see the Fly.io post talks about it. Thks.

To process it it needs to be stored somewhere. Even if just in memory. It doesnā€˜t need to be stored permanently though.

Yes, memory, When I meant ā€œsaveā€, it was on disk, local or S3.

@f0rest8 @LostKobrakai, thanks. UploadWriter works like a charm in the handle_progress to do what I want.

@impl true
  def write_chunk(data, state) do
    {:ok,
     state
     |> Map.update!(:total_size, &(&1 + byte_size(data)))
     |> Map.update!(:file, &(&1 <> data))}
  end

and then:

defp handle_progress(:image_list, entry, socket) do
    alias Vix.Vips.Image
    alias Vix.Vips.Operation

    if entry.done? do
      uploaded_file =
        consume_uploaded_entry(
           socket, entry, fn %{file: file, total_size: total_size} -> 
        case entry.client_size == total_size do
           true -> 
             {:ok, img} = Vix.Vips.Image.new_from_buffer(file)
             ....