This example is a variant of g-upload that
streams uploaded files
one chunk at a time, instead of loading them completely into memory. It counts
the total size of each uploaded file:
let home request =
<html>
<body>
<form method="POST" action="/" enctype="multipart/form-data">
<%s! Dream.csrf_tag request %>
<input name="files" type="file" multiple>
<button>Submit!</button>
</form>
</body>
</html>
let report files =
<html>
<body>
% files |> List.iter begin fun (name, size) ->
% let name =
% match name with
% | None -> "None"
% | Some name -> name
% in
<p><%s name %>, <%i size %> bytes</p>
% end;
</body>
</html>
let () =
Dream.run
@@ Dream.logger
@@ Dream.memory_sessions ()
@@ Dream.router [
Dream.get "/" (fun request ->
Dream.html (home request));
Dream.post "/" (fun request ->
let rec receive file_sizes =
match%lwt Dream.upload request with
| None -> Dream.html (report (List.rev file_sizes))
| Some (_, filename, _) ->
let rec count_size size =
match%lwt Dream.upload_part request with
| None -> receive ((filename, size)::file_sizes)
| Some chunk -> count_size (size + String.length chunk)
in
count_size 0
in
receive []);
]cd example/w-upload-stream
$ opam install --deps-only --yes .
$ dune exec --root . ./upload_stream.exeThe report page shows one file without a name ("None"). This is, in fact, the
CSRF token generated by
Dream.csrf_tag inside the
template. To keep the example simple, we didn't check the CSRF token, nor filter
out the dream.csrf field that it appears in. If you'd like to do so in your
code, see
Dream.verify_csrf_token.
See OWASP File Upload Cheat Sheet for a checklist of additional security precautions.
See also:
g-uploadshows the simplified interface for receiving file uploads.w-multipart-dumpshows the request body that is parsed byDream.upload.