[Git httpform:https://github.com/tulayang/httpform]
Hello, I finished a module for upload images and octet file via http. It can be used with HTML <form> <input file>:
<form action="http://127.0.0.1:8000/" method="POST" enctype="multipart/form-data">
<input type="text" name="name1"/>
<input type="file" name="files" multiple/>
<input type="submit" name="submit"></input>
</form>
The test example:
import httpform, asyncdispatch, asynchttpserver,
threadpool, net, os, strtabs, json
proc server() =
var
server = newAsyncHttpServer()
form = newAsyncHttpForm(getTempDir(), true)
proc cb(req: Request) {.async.} =
var (fields, files) = await form.parseAsync(req.headers["Content-Type"], req.body)
assert fields["username"] == newJString("Tom")
echo files["upload"][0]["path"] # "/home/king/tmp/55cdf98a0fbeb30400000000.txt"
assert files["upload"][0]["size"] == newJInt(6)
assert files["upload"][0]["type"] == newJString("text/plain")
assert files["upload"][0]["name"] == newJString("file1.txt")
echo files["upload"][1]["path"] # "/home/king/tmp/55cdf98a0fbeb30400000001.gif"
assert files["upload"][1]["size"] == newJInt(12)
assert files["upload"][1]["type"] == newJString("image/gif")
assert files["upload"][1]["name"] == newJString("file2.gif")
quit(0)
waitFor server.serve(Port(8000), cb)
proc client() =
var
socket = newSocket()
data = "--AaB03x\r\n" &
"Content-Disposition: form-data; name=\"username\"\r\n\r\n" &
"Tom\r\n" &
"--AaB03x\r\n" &
"Content-Disposition: form-data; name=\"upload\"; filename=\"file1.txt\"\r\n" &
"Content-Type: text/plain\r\n\r\n" &
"000000\r\n" &
"--AaB03x\r\n" &
"Content-Disposition: form-data; name=\"upload\"; filename=\"file2.gif\"\r\n" &
"Content-Type: image/gif\r\n" &
"Content-Transfer-Encoding: base64\r\n\r\n" &
"010101010101\r\n" &
"--AaB03x--\r\n"
socket.connect("127.0.0.1", Port(8000))
socket.send("POST /path HTTP/1.1\r\L")
socket.send("Content-Type: multipart/form-data; boundary=AaB03x\r\L")
socket.send("Content-Length: " & $data.len() & "\r\L\r\L")
socket.send(data)
proc main() =
parallel:
spawn server()
sleep(100)
spawn client()
main()
`form.parseAsync` return a tuple[fileds: JsonNode, files: JsonNode], just like Nodejs. So do just for convenience.
@dom96
There has form.parse for synchronous. I implemented both form.parse and form.parseAsync. form.parseAsync will use asyncfile.write to save the uploaded bytes to some temporary files when Content-Type is "multipart/form-data" or "application/octet-stream" (form.parse use system.writeFile). I think when one request have many files to upload, save only one file for every poll. To give other requests a chance to work.
The synchronous example:
var
data =
"--AaB03x\r\n" &
"Content-Disposition: form-data; name=\"username\"\r\n\r\n" &
"Tom\r\n" &
"--AaB03x\r\n" &
"Content-Disposition: form-data; name=\"upload\"; filename=\"file1.txt\"\r\n" &
"Content-Type: text/plain\r\n\r\n" &
"000000\r\n" &
"--AaB03x\r\n" &
"Content-Disposition: form-data; name=\"upload\"; filename=\"file2.gif\"\r\n" &
"Content-Type: image/gif\r\n" &
"Content-Transfer-Encoding: base64\r\n\r\n" &
"010101010101\r\n" &
"--AaB03x--\r\n"
form = newHttpForm(getTempDir())
(fields, files) = form.parse("multipart/form-data; boundary=AaB03x", data)
assert fields["username"] == newJString("Tom")
echo files["upload"][0]["path"] # "/home/king/tmp/55cdf98a0fbeb30400000000.txt"
assert files["upload"][0]["size"] == newJInt(6)
assert files["upload"][0]["type"] == newJString("text/plain")
assert files["upload"][0]["name"] == newJString("file1.txt")
echo files["upload"][1]["path"] # "/home/king/tmp/55cdf98a0fbeb30400000001.gif"
assert files["upload"][1]["size"] == newJInt(12)
assert files["upload"][1]["type"] == newJString("image/gif")
assert files["upload"][1]["name"] == newJString("file2.gif")