What plug do i need to accept FormData ("multipart/form-data") type of post request?

plug
plugupload

#1

Hey besically the title says it,
I am trying to send a file to my elixir api and, i just noticed that in the router it has this plug: plug(:accepts, ["json"]), but since i am trying to send a file i am sending a multipart request not json.
Thoughts?


#2

You shouldn’t need to change anything in the Plug pipeline - the file should be included as part of the form wrapped in a Plug.Upload struct. If you’re not seeing the file in your params it might be that you’re hitting the upload limits? The blog post on uploads would have more info: https://phoenixframework.org/blog/file-uploads


#3

no i dont think i hit it, i am trying to send the file like this :

  let data = new FormData();
        data.append("xd", {
          recording: rec,
          challenge_id: 1,
          user_id: 1,
          mod_score: 3
        });
        console.log(data);
        axios
          .post(
            config.API_URL + "recordings",
            { data },
            {
              headers: {
                Authorization: "Bearer " + this.props.auth.token,
                "content-type": "multipart/form-data"
              }
            }
          ) 

but only an empty struct %{} show up


#4

Can you also show the code of your controller?


#5

glady :smile:

def create(conn, %{"recording" => recording_params}) do

    with {:ok, %Recording{} = recording} <- Web.create_recording(recording_params) do
      challenge = Web.get_challenge!(recording.challenge_id)
      number_of_days_between = Date.diff(challenge.due_date, recording.inserted_at)
      calculated_score = number_of_days_between * challenge.difficulty * 100
      user = Web.get_user!(recording.user_id)
      IO.inspect(user)
      score_to_insert = user.score + calculated_score

      updated_user = %{
        score: score_to_insert
      }

      Web.update_user(user, updated_user)
      send_resp(conn, 200, [])
      # conn
      # |> put_status(:created)
      # |> render("show.json", recording: recording)
    end
  end

youcould see most of the related code in this post: Sending and recieveing a file(audio) from app to elixir api issue

im chaing the request to try to match but no success yet


#6

And where is it you see only an empty map?


#7

i am runnign the server with foreground command and says this

info] POST /api/recordings
[debug] QUERY OK source="users" db=0.3ms decode=0.1ms queue=0.2ms
SELECT u0."id", u0."name", u0."password_hash", u0."score", u0."avatar", u0."role_id", u0."team_id", u
0."inserted_at", u0."updated_at" FROM "users" AS u0 WHERE (u0."id" = $1) [2]
[debug] QUERY OK source="roles" db=0.2ms queue=0.2ms
SELECT r0."id", r0."name", r0."inserted_at", r0."updated_at", r0."id" FROM "roles" AS r0 WHERE (r0."i
d" = $1) [1]
[debug] QUERY OK source="teams" db=0.2ms queue=0.5ms
SELECT t0."id", t0."name", t0."inserted_at", t0."updated_at", t0."id" FROM "teams" AS t0 WHERE (t0."i
d" = $1) [2]
[debug] Processing with Userteam1Web.RecordingController.create/2
  Parameters: %{}
  Pipelines: [:api, :jwt_authenticated]
[info] Sent 400 in 38ms
[debug] ** (Phoenix.ActionClauseError) could not find a matching Userteam1Web.RecordingController.cre
ate clause
to process request. This typically happens when there is a
parameter mismatch but may also happen when any of the other
action arguments do not match. The request parameters are:
  %{}

here at the end


#8

ok something intersting, if i remove this: ```
"content-type": "multipart/form-data"

then this shows up, still doesnt match but it shows up:

%{"data" =&gt; %{"_parts" =&gt; [["recording", %{"_duration" =&gt; -1, "_fsPath" =&gt; "/data/user/0/com.cobrn/files/filename.mp4", "_lastSync" =&gt; -1, "_options" =&gt; %{"autoDestroy" =&gt; true}, "_path" =&gt; "filename.mp4", "_position" =&gt; -1, "_recorderId" =&gt; 0, "_state" =&gt; -2}], ["challenge_id", 1], ["user_id", 1]]}}


#9

it very much seems like our api does not accept formdata


#10

According to axios’ documentation JS part should be like this:

let data = new FormData();
data.append("recording[file]", rec);
data.append("recording[challenge_id]", 1);
data.append("recording[user_id]", 1);
data.append("recording[mod_score]", 3);
console.log(data);
axios.post(
    config.API_URL + "recordings",
    data,
    {
        headers: {
            Authorization: "Bearer " + this.props.auth.token,
            "Content-Type": "multipart/form-data"
        }
    }
);

To match your controller.


#11

Hey really really appreciate the answer, i tried this code and got this reponse:

`%{"challenge_id" => "1", "mod_score" => "3", "user_id" => "1"}`

and a 422 response code
so it seems like the file is not there for some reason, but the request is correct

could you link that part of their docs? I can’t seem to find it :upside_down_face:


#12

figured it out, the upload was incorrect, it is working like this:

data.append("recording[path_to_recording]", {
      uri: "file://" + rec._fsPath,
      name: "filename.mp4",
      type: "audio/mp4"
    });

#13

figured it out, the upload was incorrect, it is working like this, you need to specify the type

data.append("recording[path_to_recording]", {
      uri: "file://" + rec._fsPath,
      name: "filename.mp4",
      type: "audio/mp4"
    });

#14

Actually it was not the docs but an example https://github.com/axios/axios/tree/master/examples/upload .
You can also check this link https://serversideup.net/uploading-files-vuejs-axios/


#15

Are you using Plug.Parsers? https://hexdocs.pm/plug/Plug.Parsers.html


#16

hm, in endpoint.ex i have it yes