multipart/form-data Content Parsing Plugin for Nova Framework
View Sourcenova_multipart_plugin is a middleware plugin for the Nova Web Framework that handles multipart/form-data requests.
Features
When processing multipart/form-data content, this plugin automatically uploads files to a temporary directory and augments the Nova Req (Request) object with the following maps:
files— A list of uploaded temporary files.fields— A list of parsed form fields.
Installation
1. Add Dependency
Add nova_multipart_plugin to your rebar.config dependencies:
{deps, [
%% ...
{nova, "0.14.1"},
nova_multipart_plugin,
%% ...
]}.
2. Configure sys.config
Register the plugin under the plugins section for the pre_request stage in your sys.config. You must specify the tmp_dir option (where temporary files will be stored) as a binary string.
[
{kernel, [{logger_level, error}]},
{nova, [
{cowboy_configuration, #{port => 8080}},
{bootstrap_application, my_app},
{json_lib, thoas},
%% ...
{plugins, [
{pre_request, nova_multipart_plugin, #{tmp_dir => <<"/tmp/nova/">>}},
{pre_request, nova_request_plugin, #{decode_json_body => true, parse_qs => true}}
%% ...
]}
]}
].
Usage
You can extract the files and fields maps directly from the Nova Req object within your controllers:
upload_file(#{files := Files, fields := Fields} = Req) ->
Body = lists:foldl(
fun(File, Acc) ->
{FieldName, {FileName, ContentType, TmpPath}} = File,
SubDir = binary:encode_hex(crypto:strong_rand_bytes(16)),
Path = filename:join(SubDir, FileName),
NewPath = filename:join(<<"./uploads/">>, Path),
filelib:ensure_dir(NewPath),
move_file(TmpPath, NewPath),
Acc#{
FieldName => #{
<<"path">> => Path,
<<"file_name">> => FileName,
<<"content_type">> => ContentType
}
}
end,
#{},
Files
),
{json, 200, #{}, Body}.
move_file(Source, Dest) ->
case file:rename(Source, Dest) of
ok -> ok;
{error, exdev} ->
case file:copy(Source, Dest) of
{ok, _Bytes} -> file:delete(Source);
{error, Reason} -> {error, Reason}
end;
{error, Reason} -> {error, Reason}
end.
Design Philosophy
The output generated by this plugin mirrors the exact structure of the incoming multipart/form-data payload without introducing opinions.
Decisions regarding persistent storage architecture, file access control, handling duplicate filenames, or parsing field names into hierarchical structures are highly domain-specific. Therefore, this plugin leaves those responsibilities entirely to the application developer.