Skip to content

Commit

Permalink
Merge pull request #490 from AnswerDotAI/doc-file-uploads
Browse files Browse the repository at this point in the history
Add file upload to quickstart
  • Loading branch information
jph00 authored Oct 10, 2024
2 parents 0a60cac + 8ca2dcb commit 654d6d9
Showing 1 changed file with 83 additions and 0 deletions.
83 changes: 83 additions & 0 deletions nbs/tutorials/quickstart_for_web_devs.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -1476,6 +1476,89 @@
"10. The `send` function is used here to send HTML back to the page. As the HTML has an `id` of `notifications`, HTMX will overwrite what is already on the page with the same ID\n",
"11. The websocket function can also be used to return a value. In this case, it is a tuple of two HTML elements. HTMX will take the elements and replace them where appropriate. As both have `id` specified (`notifications` and `msg` respectively), they will replace their predecessor on the page."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## File Uploads"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A common task in web development is uploading files. This example is for uploading files to the hosting server, with information about the uploaded file presented to the user.\n",
"\n",
":::{.callout-warning title='File uploads in production can be dangerous'}\n",
"File uploads can be the target of uabuse, accidental or intentional. That means users may attempt to upload files that are too large or present a security risk. This is especially of concern for public facing apps. File upload security is outside the scope of this tutorial, for now we suggest reading the [OWASP File Upload Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html).\n",
":::"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from fasthtml.common import *\n",
"from pathlib import Path\n",
"\n",
"app, rt = fast_app()\n",
"\n",
"upload_dir = Path(\"filez\")\n",
"upload_dir.mkdir(exist_ok=True)\n",
"\n",
"@rt('/')\n",
"def get():\n",
" return Titled(\"File Upload Demo\",\n",
" Div(cls='grid')(\n",
" Article(\n",
" Form(hx_post=\"/upload\", hx_target=\"#result-one\")( # <1>\n",
" Input(type=\"file\", name=\"file\"), # <2>\n",
" Button(\"Upload\", type=\"submit\", cls='secondary'),\n",
" ),\n",
" Div(id=\"result-one\")\n",
" )\n",
" )\n",
" )\n",
"\n",
"def FileMetaDataCard(file):\n",
" return Article(\n",
" Header(H3(file.filename)),\n",
" Ul(\n",
" Li('Size: ', file.size), \n",
" Li('Content Type: ', file.content_type),\n",
" Li('Headers: ', file.headers),\n",
" )\n",
" ) \n",
"\n",
"@rt('/upload')\n",
"async def post(file: UploadFile): # <3>\n",
" card = FileMetaDataCard(file) # <4>\n",
" filebuffer = await file.read() # <5>\n",
" (upload_dir / file.filename).write_bytes(filebuffer) # <6>\n",
" return card\n",
"\n",
"serve()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"1. Every form rendered with the `Form` FT component defaults to `enctype=\"multipart/form-data\"`\n",
"2. Don't forget to set the `Input` FT Component's type to `file`\n",
"3. The upload view should receive a [Starlette UploadFile](https://www.starlette.io/requests/#request-files) type. You can add other form variables\n",
"4. We can access the metadata of the card (filename, size, content_type, headers), a quick and safe process\n",
"5. In order to access the contents contained within a file we use the `await` method to read() it. As files may be quite large or contain bad data, this is a seperate step from accessing metadata\n",
"6. This step shows how to use Python's built-in `pathlib.Path` library to write the file to disk."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
}
],
"metadata": {
Expand Down

0 comments on commit 654d6d9

Please sign in to comment.