1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
|
.. _multipart:
Multipart Forms
===============
Falcon features easy and efficient access to submitted multipart forms by using
:class:`~falcon.media.MultipartFormHandler` to handle the
``multipart/form-data`` :ref:`media <media>` type. This handler is enabled by
default, allowing you to use ``req.get_media()`` to iterate over the
:class:`body parts <falcon.media.multipart.BodyPart>` in a form:
.. tab-set::
.. tab-item:: WSGI
:sync: wsgi
.. code:: python
form = req.get_media()
for part in form:
if part.content_type == 'application/json':
# Body part is a JSON document, do something useful with it
resp.media = part.get_media()
elif part.name == 'datafile':
while True:
# Do something with the uploaded data (file)
chunk = part.stream.read(8192)
if not chunk:
break
feed_data(chunk)
elif part.name == 'imagedata':
# Store this body part in a file.
filename = os.path.join(UPLOAD_PATH, part.secure_filename)
with open(filename, 'wb') as dest:
part.stream.pipe(dest)
else:
# Do something else
form_data[part.name] = part.text
.. tab-item:: ASGI
:sync: asgi
.. code:: python
form = await req.get_media()
async for part in form:
if part.content_type == 'application/json':
# Body part is a JSON document, do something useful with it
resp.media = await part.get_media()
elif part.name == 'datafile':
# Do something with the uploaded data (file)
async for chunk in part.stream:
await feed_data(chunk)
elif part.name == 'imagedata':
# Store this body part in a file.
filename = os.path.join(UPLOAD_PATH, part.secure_filename)
async with aiofiles.open(filename, 'wb') as dest:
await part.stream.pipe(dest)
else:
# Do something else
form_data[part.name] = await part.text
.. note::
Rather than being read in and buffered all at once, the request stream is
only consumed on-demand, while iterating over the body parts in the form.
For each part, you can choose whether to read the whole part into memory,
write it out to a file, or :ref:`upload it to the cloud
<multipart_cloud_upload>`. Falcon offers straightforward support for all
of these scenarios.
Multipart Form and Body Part Types
----------------------------------
.. tab-set::
.. tab-item:: WSGI
:sync: wsgi
.. autoclass:: falcon.media.multipart.MultipartForm
:members:
.. autoclass:: falcon.media.multipart.BodyPart
:members:
.. tab-item:: ASGI
:sync: asgi
.. autoclass:: falcon.asgi.multipart.MultipartForm
:members:
.. autoclass:: falcon.asgi.multipart.BodyPart
:members:
.. _multipart_parser_conf:
Parser Configuration
--------------------
Similar to :class:`falcon.App`\'s :attr:`~falcon.App.req_options` and
:attr:`~falcon.App.resp_options`, instantiating a
:class:`~falcon.media.MultipartFormHandler` also fills its
:attr:`~falcon.media.MultipartFormHandler.parse_options` attribute with a set
of sane default values suitable for many use cases out of the box. If you need
to customize certain form parsing aspects of your application, the preferred
way is to directly modify the properties of this attribute on the media handler
(parser) in question:
.. code:: python
import falcon
import falcon.media
handler = falcon.media.MultipartFormHandler()
# Assume text fields to be encoded in latin-1 instead of utf-8
handler.parse_options.default_charset = 'latin-1'
# Allow an unlimited number of body parts in the form
handler.parse_options.max_body_part_count = 0
# Afford parsing msgpack-encoded body parts directly via part.get_media()
extra_handlers = {
falcon.MEDIA_MSGPACK: falcon.media.MessagePackHandler(),
}
handler.parse_options.media_handlers.update(extra_handlers)
In order to use your customized handler in an app, simply replace the default
handler for ``multipart/form-data`` with the new one:
.. tab-set::
.. tab-item:: WSGI
:sync: wsgi
.. code:: python
app = falcon.App()
# handler is instantiated and configured as per the above snippet
app.req_options.media_handlers[falcon.MEDIA_MULTIPART] = handler
.. tab-item:: ASGI
:sync: asgi
.. code:: python
app = falcon.asgi.App()
# handler is instantiated and configured as per the above snippet
app.req_options.media_handlers[falcon.MEDIA_MULTIPART] = handler
.. tip::
For more information on customizing media handlers, see also:
:ref:`custom_media_handlers`.
Parsing Options
---------------
.. autoclass:: falcon.media.multipart.MultipartParseOptions
:members:
Parsing Errors
--------------
.. autoclass:: falcon.MultipartParseError
:members:
|