Simulating browser-like file upload with Tesla

Please how may we upload a file as described below with Tesla?

For a self-signed certificate an extra parameter is needed, certificate , with the public certificate in PEM format as data.

  • A curl example for a self-signed certificate:
    curl -F "url=https://<YOURDOMAIN.EXAMPLE>/<WEBHOOKLOCATION>" -F "certificate=@<YOURCERTIFICATE>.pem"<YOURTOKEN>/setWebhook

The -F means we’re using the multipart/form-data -type to supply the certificate

I have tried:

      mp =
        |> Multipart.add_field("url", "https://my-sever-ip:88/bots/#{token}")
        |> Multipart.add_field("certificate", "bots.pem")
        |> Multipart.add_file(file, name: "bots.pem")

      post("bot#{token}/setwebhook", mp)

But the operation is not successful.

Please how may I achieve the same as this : manual file upload utility using Tesla.


In a helper script I use to push files to gitlab, I do use this code:

Basically, you do not need o add_field/2, but only add_file/2. :name has to be the name of the target-field, not of the file.

In your curl example you aren’t specifiying a name as well… @ reads the file and sends the content properly encoded.

I also need to set the parameter certificate on the file being uploaded, not sure, but i feel that is where ia ma failing

Parameter Type Required Description
url String Yes HTTPS url to send updates to. Use an empty string to remove webhook integration
certificate InputFile Optional Upload your public key certificate so that the root certificate in use can be checked. See our self-signed guide for details.

This curl should translate into this tesla request (I’ll take your placeholders literally):

body =
  |> MultiPart.add_field("url", "https://<YOURDOMAIN.EXAMPLE>/<WEBHOOKLOCATION>")
  |> MultiPart.add_file("<YOURCERTIFICATE>.pem", name: :certificate, filename: "<YOURCERTIFICATE>.pem") # but `:filename` actually defaults to this…"<YOURTOKEN>/setWebhook", body)

no luck,

13:53:28.417 [info]  POST -> 200 (968.000 ms)

13:53:28.433 [debug]
>>> REQUEST >>>
(no query)
content-type: application/json

boundary: nhPkmImyzsDqsndbfmgBGRn65glBXisu
content_type_params: []
%Tesla.Multipart.Part{body: "", dispositions: [name: "url"], headers: []}
%Tesla.Multipart.Part{body: "100", dispositions: [name: "max_connections"], headers: []}
%Tesla.Multipart.Part{body: %File.Stream{line_or_bytes: 2048, modes: [:raw, :read_ahead, :read, :binary], path: "c:/Apps/BE/_build/dev/lib/tbe/priv/TBE.pem", raw: true}, dispositions: [name: :certificate, filename: "TBE.pem"], headers: []}

<<< RESPONSE <<<
connection: keep-alive
date: Wed, 19 Jun 2019 12:53:31 GMT
server: nginx/1.12.2
content-length: 68
content-type: application/json
strict-transport-security: max-age=31536000; includeSubDomains; preload
access-control-allow-origin: *
access-control-allow-methods: GET, POST, OPTIONS
access-control-expose-headers: Content-Length,Content-Type,Date,Server,Connection

{"ok":true,"result":true,"description":"Webhook is already deleted"}

I do not know the telegram API, all I can tell you is, that from what I can tell both requests should be equivalent.

@NobbZ it only works when i use regular browser upload, like here

Maybe Tesla is not generating multi-part output like a browser would

Maybe browser sends some automatic header, like content type, that you don’t? Can you inspect the request in your browsers tool?

Or paste a --verbose curls output here? (sanitized of course!) Then we can compare headers actually sent.

It works now.

Tesla actually works fine.

setting these two affects the postmethod, should have used instead

plug(Tesla.Middleware.Headers, [{"content-type", "application/json"}])
plug(Tesla.Middleware.BaseUrl, "")
body =
        |> Multipart.add_field("max_connections", "100")
        |> Multipart.add_field("url", "{port}/bots/#{token}")
        |> Multipart.add_file(file, name: :certificate, filename: name)

      x ="{token}/setWebhook", body)