mirror of
https://github.com/django/django.git
synced 2024-11-18 15:34:16 +00:00
446 lines
17 KiB
Plaintext
446 lines
17 KiB
Plaintext
============
|
|
File Uploads
|
|
============
|
|
|
|
.. currentmodule:: django.core.files.uploadedfile
|
|
|
|
When Django handles a file upload, the file data ends up placed in
|
|
:attr:`request.FILES <django.http.HttpRequest.FILES>` (for more on the
|
|
``request`` object see the documentation for :doc:`request and response objects
|
|
</ref/request-response>`). This document explains how files are stored on disk
|
|
and in memory, and how to customize the default behavior.
|
|
|
|
Basic file uploads
|
|
==================
|
|
|
|
Consider a simple form containing a :class:`~django.forms.FileField`::
|
|
|
|
from django import forms
|
|
|
|
class UploadFileForm(forms.Form):
|
|
title = forms.CharField(max_length=50)
|
|
file = forms.FileField()
|
|
|
|
A view handling this form will receive the file data in
|
|
:attr:`request.FILES <django.http.HttpRequest.FILES>`, which is a dictionary
|
|
containing a key for each :class:`~django.forms.FileField` (or
|
|
:class:`~django.forms.ImageField`, or other :class:`~django.forms.FileField`
|
|
subclass) in the form. So the data from the above form would
|
|
be accessible as ``request.FILES['file']``.
|
|
|
|
Note that :attr:`request.FILES <django.http.HttpRequest.FILES>` will only
|
|
contain data if the request method was ``POST`` and the ``<form>`` that posted
|
|
the request has the attribute ``enctype="multipart/form-data"``. Otherwise,
|
|
``request.FILES`` will be empty.
|
|
|
|
Most of the time, you'll simply pass the file data from ``request`` into the
|
|
form as described in :ref:`binding-uploaded-files`. This would look
|
|
something like::
|
|
|
|
from django.http import HttpResponseRedirect
|
|
from django.shortcuts import render_to_response
|
|
|
|
# Imaginary function to handle an uploaded file.
|
|
from somewhere import handle_uploaded_file
|
|
|
|
def upload_file(request):
|
|
if request.method == 'POST':
|
|
form = UploadFileForm(request.POST, request.FILES)
|
|
if form.is_valid():
|
|
handle_uploaded_file(request.FILES['file'])
|
|
return HttpResponseRedirect('/success/url/')
|
|
else:
|
|
form = UploadFileForm()
|
|
return render_to_response('upload.html', {'form': form})
|
|
|
|
Notice that we have to pass :attr:`request.FILES <django.http.HttpRequest.FILES>`
|
|
into the form's constructor; this is how file data gets bound into a form.
|
|
|
|
Handling uploaded files
|
|
-----------------------
|
|
|
|
.. class:: UploadedFile
|
|
|
|
The final piece of the puzzle is handling the actual file data from
|
|
:attr:`request.FILES <django.http.HttpRequest.FILES>`. Each entry in this
|
|
dictionary is an ``UploadedFile`` object -- a simple wrapper around an uploaded
|
|
file. You'll usually use one of these methods to access the uploaded content:
|
|
|
|
.. method:: read()
|
|
|
|
Read the entire uploaded data from the file. Be careful with this
|
|
method: if the uploaded file is huge it can overwhelm your system if you
|
|
try to read it into memory. You'll probably want to use ``chunks()``
|
|
instead; see below.
|
|
|
|
.. method:: multiple_chunks()
|
|
|
|
Returns ``True`` if the uploaded file is big enough to require
|
|
reading in multiple chunks. By default this will be any file
|
|
larger than 2.5 megabytes, but that's configurable; see below.
|
|
|
|
.. method:: chunks()
|
|
|
|
A generator returning chunks of the file. If ``multiple_chunks()`` is
|
|
``True``, you should use this method in a loop instead of ``read()``.
|
|
|
|
In practice, it's often easiest simply to use ``chunks()`` all the time;
|
|
see the example below.
|
|
|
|
.. attribute:: name
|
|
|
|
The name of the uploaded file (e.g. ``my_file.txt``).
|
|
|
|
.. attribute:: size
|
|
|
|
The size, in bytes, of the uploaded file.
|
|
|
|
There are a few other methods and attributes available on ``UploadedFile``
|
|
objects; see `UploadedFile objects`_ for a complete reference.
|
|
|
|
Putting it all together, here's a common way you might handle an uploaded file::
|
|
|
|
def handle_uploaded_file(f):
|
|
with open('some/file/name.txt', 'wb+') as destination:
|
|
for chunk in f.chunks():
|
|
destination.write(chunk)
|
|
|
|
Looping over ``UploadedFile.chunks()`` instead of using ``read()`` ensures that
|
|
large files don't overwhelm your system's memory.
|
|
|
|
Where uploaded data is stored
|
|
-----------------------------
|
|
|
|
Before you save uploaded files, the data needs to be stored somewhere.
|
|
|
|
By default, if an uploaded file is smaller than 2.5 megabytes, Django will hold
|
|
the entire contents of the upload in memory. This means that saving the file
|
|
involves only a read from memory and a write to disk and thus is very fast.
|
|
|
|
However, if an uploaded file is too large, Django will write the uploaded file
|
|
to a temporary file stored in your system's temporary directory. On a Unix-like
|
|
platform this means you can expect Django to generate a file called something
|
|
like ``/tmp/tmpzfp6I6.upload``. If an upload is large enough, you can watch this
|
|
file grow in size as Django streams the data onto disk.
|
|
|
|
These specifics -- 2.5 megabytes; ``/tmp``; etc. -- are simply "reasonable
|
|
defaults". Read on for details on how you can customize or completely replace
|
|
upload behavior.
|
|
|
|
Changing upload handler behavior
|
|
--------------------------------
|
|
|
|
Three settings control Django's file upload behavior:
|
|
|
|
:setting:`FILE_UPLOAD_MAX_MEMORY_SIZE`
|
|
The maximum size, in bytes, for files that will be uploaded into memory.
|
|
Files larger than :setting:`FILE_UPLOAD_MAX_MEMORY_SIZE` will be
|
|
streamed to disk.
|
|
|
|
Defaults to 2.5 megabytes.
|
|
|
|
:setting:`FILE_UPLOAD_TEMP_DIR`
|
|
The directory where uploaded files larger than
|
|
:setting:`FILE_UPLOAD_MAX_MEMORY_SIZE` will be stored.
|
|
|
|
Defaults to your system's standard temporary directory (i.e. ``/tmp`` on
|
|
most Unix-like systems).
|
|
|
|
:setting:`FILE_UPLOAD_PERMISSIONS`
|
|
The numeric mode (i.e. ``0644``) to set newly uploaded files to. For
|
|
more information about what these modes mean, see the documentation for
|
|
:func:`os.chmod`.
|
|
|
|
If this isn't given or is ``None``, you'll get operating-system
|
|
dependent behavior. On most platforms, temporary files will have a mode
|
|
of ``0600``, and files saved from memory will be saved using the
|
|
system's standard umask.
|
|
|
|
.. warning::
|
|
|
|
If you're not familiar with file modes, please note that the leading
|
|
``0`` is very important: it indicates an octal number, which is the
|
|
way that modes must be specified. If you try to use ``644``, you'll
|
|
get totally incorrect behavior.
|
|
|
|
**Always prefix the mode with a 0.**
|
|
|
|
:setting:`FILE_UPLOAD_HANDLERS`
|
|
The actual handlers for uploaded files. Changing this setting allows
|
|
complete customization -- even replacement -- of Django's upload
|
|
process. See `upload handlers`_, below, for details.
|
|
|
|
Defaults to::
|
|
|
|
("django.core.files.uploadhandler.MemoryFileUploadHandler",
|
|
"django.core.files.uploadhandler.TemporaryFileUploadHandler",)
|
|
|
|
Which means "try to upload to memory first, then fall back to temporary
|
|
files."
|
|
|
|
Handling uploaded files with a model
|
|
------------------------------------
|
|
|
|
If you're saving a file on a :class:`~django.db.models.Model` with a
|
|
:class:`~django.db.models.FileField`, using a :class:`~django.forms.ModelForm`
|
|
makes this process much easier. The file object will be saved to the location
|
|
specified by the :attr:`~django.db.models.FileField.upload_to` argument of the
|
|
corresponding :class:`~django.db.models.FileField` when calling
|
|
``form.save()``::
|
|
|
|
from django.http import HttpResponseRedirect
|
|
from django.shortcuts import render
|
|
from .forms import ModelFormWithFileField
|
|
|
|
def upload_file(request):
|
|
if request.method == 'POST':
|
|
form = ModelFormWithFileField(request.POST, request.FILES)
|
|
if form.is_valid():
|
|
# file is saved
|
|
form.save()
|
|
return HttpResponseRedirect('/success/url/')
|
|
else:
|
|
form = ModelFormWithFileField()
|
|
return render('upload.html', {'form': form})
|
|
|
|
If you are constructing an object manually, you can simply assign the file
|
|
object from :attr:`request.FILES <django.http.HttpRequest.FILES>` to the file
|
|
field in the model::
|
|
|
|
from django.http import HttpResponseRedirect
|
|
from django.shortcuts import render
|
|
from .forms import UploadFileForm
|
|
from .models import ModelWithFileField
|
|
|
|
def upload_file(request):
|
|
if request.method == 'POST':
|
|
form = UploadFileForm(request.POST, request.FILES)
|
|
if form.is_valid():
|
|
instance = ModelWithFileField(file_field=request.FILES['file'])
|
|
instance.save()
|
|
return HttpResponseRedirect('/success/url/')
|
|
else:
|
|
form = UploadFileForm()
|
|
return render('upload.html', {'form': form})
|
|
|
|
|
|
``UploadedFile`` objects
|
|
========================
|
|
|
|
In addition to those inherited from :class:`File`, all ``UploadedFile`` objects
|
|
define the following methods/attributes:
|
|
|
|
.. attribute:: UploadedFile.content_type
|
|
|
|
The content-type header uploaded with the file (e.g. :mimetype:`text/plain`
|
|
or :mimetype:`application/pdf`). Like any data supplied by the user, you
|
|
shouldn't trust that the uploaded file is actually this type. You'll still
|
|
need to validate that the file contains the content that the content-type
|
|
header claims -- "trust but verify."
|
|
|
|
.. attribute:: UploadedFile.charset
|
|
|
|
For :mimetype:`text/*` content-types, the character set (i.e. ``utf8``)
|
|
supplied by the browser. Again, "trust but verify" is the best policy here.
|
|
|
|
.. attribute:: UploadedFile.temporary_file_path()
|
|
|
|
Only files uploaded onto disk will have this method; it returns the full
|
|
path to the temporary uploaded file.
|
|
|
|
.. note::
|
|
|
|
Like regular Python files, you can read the file line-by-line simply by
|
|
iterating over the uploaded file:
|
|
|
|
.. code-block:: python
|
|
|
|
for line in uploadedfile:
|
|
do_something_with(line)
|
|
|
|
However, *unlike* standard Python files, :class:`UploadedFile` only
|
|
understands ``\n`` (also known as "Unix-style") line endings. If you know
|
|
that you need to handle uploaded files with different line endings, you'll
|
|
need to do so in your view.
|
|
|
|
Upload Handlers
|
|
===============
|
|
|
|
When a user uploads a file, Django passes off the file data to an *upload
|
|
handler* -- a small class that handles file data as it gets uploaded. Upload
|
|
handlers are initially defined in the :setting:`FILE_UPLOAD_HANDLERS` setting,
|
|
which defaults to::
|
|
|
|
("django.core.files.uploadhandler.MemoryFileUploadHandler",
|
|
"django.core.files.uploadhandler.TemporaryFileUploadHandler",)
|
|
|
|
Together the ``MemoryFileUploadHandler`` and ``TemporaryFileUploadHandler``
|
|
provide Django's default file upload behavior of reading small files into memory
|
|
and large ones onto disk.
|
|
|
|
You can write custom handlers that customize how Django handles files. You
|
|
could, for example, use custom handlers to enforce user-level quotas, compress
|
|
data on the fly, render progress bars, and even send data to another storage
|
|
location directly without storing it locally.
|
|
|
|
.. _modifying_upload_handlers_on_the_fly:
|
|
|
|
Modifying upload handlers on the fly
|
|
------------------------------------
|
|
|
|
Sometimes particular views require different upload behavior. In these cases,
|
|
you can override upload handlers on a per-request basis by modifying
|
|
``request.upload_handlers``. By default, this list will contain the upload
|
|
handlers given by :setting:`FILE_UPLOAD_HANDLERS`, but you can modify the list
|
|
as you would any other list.
|
|
|
|
For instance, suppose you've written a ``ProgressBarUploadHandler`` that
|
|
provides feedback on upload progress to some sort of AJAX widget. You'd add this
|
|
handler to your upload handlers like this::
|
|
|
|
request.upload_handlers.insert(0, ProgressBarUploadHandler())
|
|
|
|
You'd probably want to use ``list.insert()`` in this case (instead of
|
|
``append()``) because a progress bar handler would need to run *before* any
|
|
other handlers. Remember, the upload handlers are processed in order.
|
|
|
|
If you want to replace the upload handlers completely, you can just assign a new
|
|
list::
|
|
|
|
request.upload_handlers = [ProgressBarUploadHandler()]
|
|
|
|
.. note::
|
|
|
|
You can only modify upload handlers *before* accessing
|
|
``request.POST`` or ``request.FILES`` -- it doesn't make sense to
|
|
change upload handlers after upload handling has already
|
|
started. If you try to modify ``request.upload_handlers`` after
|
|
reading from ``request.POST`` or ``request.FILES`` Django will
|
|
throw an error.
|
|
|
|
Thus, you should always modify uploading handlers as early in your view as
|
|
possible.
|
|
|
|
Also, ``request.POST`` is accessed by
|
|
:class:`~django.middleware.csrf.CsrfViewMiddleware` which is enabled by
|
|
default. This means you will need to use
|
|
:func:`~django.views.decorators.csrf.csrf_exempt` on your view to allow you
|
|
to change the upload handlers. You will then need to use
|
|
:func:`~django.views.decorators.csrf.csrf_protect` on the function that
|
|
actually processes the request. Note that this means that the handlers may
|
|
start receiving the file upload before the CSRF checks have been done.
|
|
Example code:
|
|
|
|
.. code-block:: python
|
|
|
|
from django.views.decorators.csrf import csrf_exempt, csrf_protect
|
|
|
|
@csrf_exempt
|
|
def upload_file_view(request):
|
|
request.upload_handlers.insert(0, ProgressBarUploadHandler())
|
|
return _upload_file_view(request)
|
|
|
|
@csrf_protect
|
|
def _upload_file_view(request):
|
|
... # Process request
|
|
|
|
|
|
Writing custom upload handlers
|
|
------------------------------
|
|
|
|
All file upload handlers should be subclasses of
|
|
``django.core.files.uploadhandler.FileUploadHandler``. You can define upload
|
|
handlers wherever you wish.
|
|
|
|
Required methods
|
|
~~~~~~~~~~~~~~~~
|
|
|
|
Custom file upload handlers **must** define the following methods:
|
|
|
|
``FileUploadHandler.receive_data_chunk(self, raw_data, start)``
|
|
Receives a "chunk" of data from the file upload.
|
|
|
|
``raw_data`` is a byte string containing the uploaded data.
|
|
|
|
``start`` is the position in the file where this ``raw_data`` chunk
|
|
begins.
|
|
|
|
The data you return will get fed into the subsequent upload handlers'
|
|
``receive_data_chunk`` methods. In this way, one handler can be a
|
|
"filter" for other handlers.
|
|
|
|
Return ``None`` from ``receive_data_chunk`` to sort-circuit remaining
|
|
upload handlers from getting this chunk.. This is useful if you're
|
|
storing the uploaded data yourself and don't want future handlers to
|
|
store a copy of the data.
|
|
|
|
If you raise a ``StopUpload`` or a ``SkipFile`` exception, the upload
|
|
will abort or the file will be completely skipped.
|
|
|
|
``FileUploadHandler.file_complete(self, file_size)``
|
|
Called when a file has finished uploading.
|
|
|
|
The handler should return an ``UploadedFile`` object that will be stored
|
|
in ``request.FILES``. Handlers may also return ``None`` to indicate that
|
|
the ``UploadedFile`` object should come from subsequent upload handlers.
|
|
|
|
Optional methods
|
|
~~~~~~~~~~~~~~~~
|
|
|
|
Custom upload handlers may also define any of the following optional methods or
|
|
attributes:
|
|
|
|
``FileUploadHandler.chunk_size``
|
|
Size, in bytes, of the "chunks" Django should store into memory and feed
|
|
into the handler. That is, this attribute controls the size of chunks
|
|
fed into ``FileUploadHandler.receive_data_chunk``.
|
|
|
|
For maximum performance the chunk sizes should be divisible by ``4`` and
|
|
should not exceed 2 GB (2\ :sup:`31` bytes) in size. When there are
|
|
multiple chunk sizes provided by multiple handlers, Django will use the
|
|
smallest chunk size defined by any handler.
|
|
|
|
The default is 64*2\ :sup:`10` bytes, or 64 KB.
|
|
|
|
``FileUploadHandler.new_file(self, field_name, file_name, content_type, content_length, charset)``
|
|
Callback signaling that a new file upload is starting. This is called
|
|
before any data has been fed to any upload handlers.
|
|
|
|
``field_name`` is a string name of the file ``<input>`` field.
|
|
|
|
``file_name`` is the unicode filename that was provided by the browser.
|
|
|
|
``content_type`` is the MIME type provided by the browser -- E.g.
|
|
``'image/jpeg'``.
|
|
|
|
``content_length`` is the length of the image given by the browser.
|
|
Sometimes this won't be provided and will be ``None``.
|
|
|
|
``charset`` is the character set (i.e. ``utf8``) given by the browser.
|
|
Like ``content_length``, this sometimes won't be provided.
|
|
|
|
This method may raise a ``StopFutureHandlers`` exception to prevent
|
|
future handlers from handling this file.
|
|
|
|
``FileUploadHandler.upload_complete(self)``
|
|
Callback signaling that the entire upload (all files) has completed.
|
|
|
|
``FileUploadHandler.handle_raw_input(self, input_data, META, content_length, boundary, encoding)``
|
|
Allows the handler to completely override the parsing of the raw
|
|
HTTP input.
|
|
|
|
``input_data`` is a file-like object that supports ``read()``-ing.
|
|
|
|
``META`` is the same object as ``request.META``.
|
|
|
|
``content_length`` is the length of the data in ``input_data``. Don't
|
|
read more than ``content_length`` bytes from ``input_data``.
|
|
|
|
``boundary`` is the MIME boundary for this request.
|
|
|
|
``encoding`` is the encoding of the request.
|
|
|
|
Return ``None`` if you want upload handling to continue, or a tuple of
|
|
``(POST, FILES)`` if you want to return the new data structures suitable
|
|
for the request directly.
|