Questions about ARC in phoenix/elixir api

Hey I am trying to save a profile picture from my react native app with phoenix api.
So I started to use arc, following this guide: https://medium.com/@rafamossakowski/arc-carrierwave-for-phoenix-1064b2463fdf since I couldn’t find one that is using react native. It is working nicely with the website part, but my questions would be that:
How to send the picture form my app to the api in json?
And how to return the picture to the app from the api in json?

Thanks

I usually base64 encode the image. The result is just a string. There is a little overhead due to encoding.

On the server side, I use something like this to convert the base64 string to file.

  # decode base64 string to binary
  defp sanitize_params(%{"base64" => base64} = params) do
    params
    |> Map.put("document_source", Base.decode64!(base64))
    |> Map.delete("base64")
  end

The sidenote is I needed to update endpoint like this, to increase the allowed size. This was needed when sending hires pictures from Iphone to backend.

  plug(
    Plug.Parsers,
    parsers: [:urlencoded, :multipart, :json],
    pass: ["*/*"],
    json_decoder: Poison,
    #
    # Increase the size for file uploads
    # https://chodounsky.net/2015/06/05/increase-request-length-in-phoenix-to-upload-large-files/
    # https://hexdocs.pm/plug/Plug.Parsers.html
    #
    length: 100_000_000
  )

PS. This is old code… still using Poison instead of Jason…

On the frontend (React Native) I just prepare data like this before sending to backend.

    const body = {
      document_type: documentType,
      base64: documentSource.base64,
    }

Hey Thanks for the response,
so you send the image as a string to the backend, maybe a dumb question but would it not be possible to just save that string to the database, or is that bad practice? - because then you could just send the string back and decode it and display the image or that is just really bad?

And so you have the file saved, but how do you send it back to the app?

I send it as string to the frontend, and decode the string on the client…

Of course You can save it as string, I just prefer to keep them as file. It is just more practical if You want to persist them on disk, or aws rather than in the db.

so you save it as a file and then save the path to it in the database?

and then when you need it get it from the databse and turn it bac into a string and send that?
Are you using arc too?

hey and would that be possible to send the picture from the app to elixir like if it would be from the web? just attach the picture somehow?

I think yes… Never tried.

The main problem I had at that time was the Plug.upload is not enabled by default for JSON api.

And from client You can use FormData, depending on what You use.

i read the documentation on plug.upload, but how do i send the picture in json? or if i am using form data, how do i send all of my data? because it doesnt seem to match the struct that it is looking for

IIRC You just need to add data, as in…

yes but my avatar is inside the user:

"id" => "2",
    "user" => %{
      "avatar" => %Plug.Upload{
        content_type: "image/jpeg",
        filename: "ConceptArt_SneakySneaky_011 (2).jpg",
        path: "/tmp/plug-1536/multipart-1536590148-521595600991125-4"
      }

how do i append that?

It seems You do receive the data correctly.

What do You want to do with it? Save it to db or on the cloud?

nono, this is from the web view, i want to recieve this from the app too
how do i make the append to get this?

But the web view is going through the :browser pipeline, while the API is JSON.

If You want to send file to API, either You allow the Plug (but I cannot tell You how) or You use string base64 encoding (which is the way I took).

IIRC With formdata You can shape any data You want to send.

And how do you turn the encoded string back to a file?

I use

Base.decode64!(base64)

and how do you save the file after? are you doing that in the schema?

I’m sure this is too little too late, but in case anyone else has the same question, I think the best thing to do, if you need to save that file to S3 or a database, is to turn it into an upload plug. (Stole this from somewhere I can’t remember)

  def get_plug(data) do
    {:ok, binary} = Base.decode64(data)

    with {:ok, path} <- Plug.Upload.random_file("upload"),
         {:ok, file} <- File.open(path, [:write, :binary]),
         :ok <- IO.binwrite(file, binary),
         :ok <- File.close(file) do
      {:ok, %Plug.Upload{path: path}}
    end
  end

That returns the struct you would have gotten had you uploaded a file directly via form-data (though it will be missing mimetype and filename).

2 Likes