有很多关于如何使用 Phoenix 执行此操作的信息,但我有意避免使用 Phoenix,直到我了解有关 Elixir 工作原理的更多信息。
为此,我有以下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, 0, 0, 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", "no-cache"},
{"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.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, scheme: :http, 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