Download binary data in POST request to a file

I am sending binary data to Phoenix via a POST request. I need to download the received data to a file for further processing (untar, run pdflatex, send link back to the pdf file). How do I so the download?
the data comes in from an Elm client, The controller appears to accept the data – no errors – but I don’t see the data when I do IO.inspect params

I might have misunderstood what you are trying to achieve. If you want to upload binary data to a file on the server, https://phoenixframework.org/blog/file-uploads might help. Remember to copy the data from the temporary location provided by Plug as shown in the example.

Yes, I’ve seen that post, but it seems to address a different problem, if I have understood it correctly (uploading a file from one’s computer using a form). I am sending binary data (a tar archive) by http from an Elm app to an Elixir/Phoenix app. The Elm app is sending a POST request with the binary data in the body. I have a dummy controller

def process(conn, params) do
    IO.inspect params, label: "params for 'process'"
    conn |> render("pdf.json", url: "OK")
end

that receives the data, From the IO.inspect params line, I know that the client and the server are talking to eachother. But the binary data is not in params. From what I’ve read, I think one has to use Plug.Parser, but I am too much of a noob at this point to know what to do.

I guess the real question is how to extract data from the POST request body,

What I need to do is store the binary data in a file, then process it.

Here is what I get on the Elixir side when I make a POST request from the client

[info] OPTIONS /api/print/pdf/harmonic_oscillator!!
[debug] Preflight CORS request from Origin 'http://localhost:8080' is allowed
[info] Sent 200 in 18ms
[info] POST /api/print/pdf/harmonic_oscillator!!
[debug] Simple CORS request from Origin 'http://localhost:8080' is allowed
[debug] Processing with Koko.Web.PrintController.process/2
  Parameters: %{"title" => "harmonic_oscillator!!"}
  Pipelines: [:api]
params for 'process': %{"title" => "harmonic_oscillator!!"}
[info] Sent 200 in 42ms

Here is the code on the client side that sends the POST request:

sendTarArchiveCmd : String -> List ( String, String ) -> List ( String, Bytes ) -> Cmd DocMsg
sendTarArchiveCmd url stringList dataList =
    let
        data =
            (List.map prepareData dataList) ++ (List.map prepareStringData stringList)

        archive : Bytes
        archive =
            Tar.encodeFiles data |> encode
    in
        Http.request
            { method = "Post"
            , headers = []
            , url = url
            , body = Http.bytesBody "application/tar" archive
            , expect = Http.expectString TexToPdf
            , timeout = Nothing
            , tracker = Nothing
            }

Some progress. I’ve added the line {:ok, body, conn} = Plug.Conn.read_body(conn) below

  def process(conn, params) do
    IO.inspect params, label: "params for 'process'"
    {:ok, body, conn} = Plug.Conn.read_body(conn)
    IO.inspect body, label: "BODY"
    conn |> render("pdf.json", url: "OK")
  end

Now I am seeing data:

[ debug] Processing with Koko.Web.PrintController.process/2
Parameters: %{“title” => “harmonic_oscillator!!”}
Pipelines: [:api]
params for ‘process’: %{“title” => “harmonic_oscillator!!”}
BODY: <<105, 109, 97, 103, 101, 47, 109, 97, 115, 115, 95, 115, 112, 114, 105, 110,
103, 45, 102, 100, 97, 99, 46, 112, 110, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …>>
[info] Sent 200 in 7ms

OK, its saving the POSTed data to file now:

  def process(conn, params) do
    IO.inspect params, label: "params for 'process'"
    {:ok, body, conn} = Plug.Conn.read_body(conn)
    IO.inspect body, label: "BODY"
    {:ok, file} = File.open "tarfileXXX.tar", [:write]
    IO.binwrite file, body
    File.close file  
    conn |> render("pdf.json", url: "OK")
  end

More work to do, but this was the blocker

1 Like