如何在 Elixir / Plug 中访问文件上传 / POST

问题描述

有很多关于如何使用 Phoenix 执行此操作的信息,但在我了解有关 Elixir 工作原理的更多信息之前,我有意避免使用 Phoenix。

为此,我有以下 Plug.Router 路径:

defmodule ElixirHttpServer do
  use Plug.Router
  use Plug.ErrorHandler

  plug(Plug.Parsers,parsers: [:urlencoded,{:multipart,length: 1_000_000_000}])
  plug(Plug.Logger)
  plug(:match)
  plug(:dispatch)


  post "/upload" do
    IO.inspect(Plug.Conn.read_body(conn),label: "body")
    send_resp(conn,201,"Uploaded")
  end
end

接受从表单上传文件,以 EEx 模板呈现:

<form action="/upload" method="post" enctype="multipart/form-data">
  <input type="file">
  <input type="submit">
</form>

通过此表单上传文件时,我从 IO.inspect(Plug.Conn.read_body(conn)) 获得以下输出

18:11:13.097 [info]  POST /upload                                                                                                                                                                                                                                                [205/3062]
body: {:ok,"",%Plug.Conn{
   adapter: {Plug.Cowboy.Conn,:...},assigns: %{},before_send: [#Function<1.128679493/1 in Plug.Logger.call/2>],body_params: %{},cookies: %Plug.Conn.Unfetched{aspect: :cookies},halted: false,host: "localhost",method: "POST",owner: #PID<0.750.0>,params: %{},path_info: ["upload"],path_params: %{},port: 8080,private: %{
     plug_multipart: :done,plug_route: {"/upload",#Function<1.2199674/2 in ElixirHttpServer.do_match/4>}
   },query_params: %{},query_string: "",remote_ip: {127,1},req_cookies: %Plug.Conn.Unfetched{aspect: :cookies},req_headers: [
     {"accept","text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"},{"accept-encoding","gzip,deflate,br"},{"accept-language","en-US,en;q=0.9"},{"cache-control","no-cache"},{"connection","keep-alive"},{"content-length","44"},{"content-type","multipart/form-data; boundary=----WebKitFormBoundary4wTVqggydpkBg30n"},{"host","localhost:8080"},{"origin","http://localhost:8080"},{"pragma",{"referer","http://localhost:8080/"},{"sec-ch-ua","\" Not;A Brand\";v=\"99\",\"Google Chrome\";v=\"91\",\"Chromium\";v=\"91\""},{"sec-ch-ua-mobile","?0"},{"sec-fetch-dest","document"},{"sec-fetch-mode","navigate"},{"sec-fetch-site","same-origin"},{"sec-fetch-user","?1"},{"upgrade-insecure-requests","1"},{"user-agent","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/91.0.4472.114 Safari/537.36"}
   ],request_path: "/upload",resp_body: nil,resp_cookies: %{},resp_headers: [{"cache-control","max-age=0,private,must-revalidate"}],scheme: :http,script_name: [],secret_key_base: nil,state: :unset,status: nil
 }}

18:11:13.099 [info]  Sent 201 in 2ms

我已多次通读 Plug.Upload 文档,但它似乎主要是您可以使用的结构。

Plug.Parsers 文档说明如下,但我不知道“启动 :plug 应用程序”的实际含义:

文件处理

如果文件是通过任何解析器上传的,Plug 将流式传输 将内容上传到临时目录中的文件以避免 将整个文件加载到内存中。对于这种情况, :plug 应用程序 需要启动才能使文件上传工作。更多详情 如何处理上传文件可以在文档中找到 插件上传

上传文件时,标识该文件的请求参数 文件将是一个 Plug.Upload 结构,其中包含有关上传的信息 文件(例如文件名和内容类型)以及文件的位置 存储。

我在 :plug添加extra_applications,但这似乎没有任何改变:

  def application do
    [
      extra_applications: [:plug,:plug_cowboy,:logger],mod: {ElixirHttpServer.Application,[]}
    ]
  end

  # Run "mix help deps" to learn about dependencies.
  defp deps do
    [
      {:plug_cowboy,"~> 2.4"},{:hackney,"~> 1.17.0"},{:ex_aws,"~> 2.1"},{:ex_aws_s3,"~> 2.0"},{:configparser_ex,"~> 4.0"},{:sweet_xml,"~> 0.6"}
    ]
  end
end

作为参考,这是我的主管申请:

defmodule ElixirHttpServer.Application do
  @moduledoc "OTP application for S3 bucket list/upload"

  use Application
  require Logger

  def start(_type,_args) do
    children = [
      {Plug.Cowboy,plug: ElixirHttpServer,options: [port: cowboy_port()]}
    ]

    opts = [strategy: :one_for_one,name: ElixirHttpServer.Supervisor]

    Logger.info("Starting the application...")
    Supervisor.start_link(children,opts)
  end

  defp cowboy_port(),do: Application.get_env(:elixir_http_server,:cowboy_port,8080)
end

解决方法

我遗漏了 dist\App.exe install dist\App.exe start 元素的 name 属性。

input type="file" 成功了。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...