Skip to content

Commit a609d1c

Browse files
committed
add translated article: 'Django, ELB health checks and continuous delivery'
1 parent 2713871 commit a609d1c

8 files changed

Lines changed: 663 additions & 489 deletions
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
原文: [File Uploads](https://docs.djangoproject.com/en/1.9/topics/http/file-uploads/)
2+
3+
When Django handles a file upload, the file data ends up placed in
4+
[`request.FILES`](https://docs.djangoproject.com/en/1.9/ref/request-response/#django.http.HttpRequest.FILES "django.http.HttpRequest.FILES" ) (for
5+
more on the `request` object see the documentation for [_request and response objects_](https://docs.djangoproject.com/en/1.9/ref/request-response/)). This
6+
document explains how files are stored on disk and in memory, and how to
7+
customize the default behavior.
8+
9+
>Warning
10+
11+
>There are security risks if you are accepting uploaded content from untrusted
12+
users! See the security guide's topic on [User-uploaded content](https://docs.djangoproject.com/en/1.9/topics/security/#user-uploaded-content-security) for mitigation details.
13+
14+
## 基本文件上传
15+
16+
Consider a simple form containing a [`FileField`](https://docs.djangoproject.com/en/1.9/ref/forms/fields/#django.forms.FileField "django.forms.FileField" ):
17+
18+
```python
19+
20+
# In forms.py...
21+
from django import forms
22+
23+
class UploadFileForm(forms.Form):
24+
title = forms.CharField(max_length=50)
25+
file = forms.FileField()
26+
27+
```
28+
29+
A view handling this form will receive the file data in
30+
[`request.FILES`](https://docs.djangoproject.com/en/1.9/ref/request-response/#django.http.HttpRequest.FILES "django.http.HttpRequest.FILES" ),
31+
which is a dictionary containing a key for each [`FileField`](https://docs.djangoproject.com/en/1.9/ref/forms/fields/#django.forms.FileField "django.forms.FileField" ) (or [`ImageField`](https://docs.djangoproject.com/en/1.9/ref/forms/fields/#django.forms.ImageField "django.forms.ImageField" ),
32+
or other [`FileField`](https://docs.djangoproject.com/en/1.9/ref/forms/fields/#django.forms.FileField "django.forms.FileField" ) subclass) in the form. So
33+
the data from the above form would be accessible as `request.FILES['file']`.
34+
35+
Note that [`request.FILES`](https://docs.djangoproject.com/en/1.9/ref/request-response/#django.http.HttpRequest.FILES "django.http.HttpRequest.FILES" ) will
36+
only contain data if the request method was `POST` and the `<form>` that
37+
posted the request has the attribute `enctype="multipart/form-data"`.
38+
Otherwise, `request.FILES` will be empty.
39+
40+
Most of the time, you'll simply pass the file data from `request` into the
41+
form as described in [Binding uploaded files to a form](https://docs.djangoproject.com/en/1.9/ref/forms/api/#binding-uploaded-files). This would look something like:
42+
43+
```python
44+
45+
from django.http import HttpResponseRedirect
46+
from django.shortcuts import render
47+
from .forms import UploadFileForm
48+
49+
# Imaginary function to handle an uploaded file.
50+
from somewhere import handle_uploaded_file
51+
52+
def upload_file(request):
53+
if request.method == 'POST':
54+
form = UploadFileForm(request.POST, request.FILES)
55+
if form.is_valid():
56+
handle_uploaded_file(request.FILES['file'])
57+
return HttpResponseRedirect('/success/url/')
58+
else:
59+
form = UploadFileForm()
60+
return render(request, 'upload.html', {'form': form})
61+
62+
```
63+
64+
Notice that we have to pass
65+
[`request.FILES`](https://docs.djangoproject.com/en/1.9/ref/request-response/#django.http.HttpRequest.FILES "django.http.HttpRequest.FILES" ) into
66+
the form's constructor; this is how file data gets bound into a form.
67+
68+
Here's a common way you might handle an uploaded file:
69+
70+
```python
71+
72+
def handle_uploaded_file(f):
73+
with open('some/file/name.txt', 'wb+') as destination:
74+
for chunk in f.chunks():
75+
destination.write(chunk)
76+
77+
```
78+
79+
Looping over `UploadedFile.chunks()` instead of using `read()` ensures that
80+
large files don't overwhelm your system's memory.
81+
82+
There are a few other methods and attributes available on `UploadedFile`
83+
objects; see [`UploadedFile`](https://docs.djangoproject.com/en/1.9/ref/files/uploads/#django.core.files.uploadedfile.UploadedFile "django.core.files.uploadedfile.UploadedFile" ) for a complete reference.
84+
85+
### Handling uploaded files with a model¶
86+
87+
If you're saving a file on a [`Model`](https://docs.djangoproject.com/en/1.9/ref/models/instances/#django.db.models.Model "django.db.models.Model" ) with a
88+
[`FileField`](https://docs.djangoproject.com/en/1.9/ref/models/fields/#django.db.models.FileField "django.db.models.FileField" ), using a [`ModelForm`](https://docs.djangoproject.com/en/1.9/topics/forms/modelforms/#django.forms.ModelForm "django.forms.ModelForm" ) makes this process much easier. The file object
89+
will be saved to the location specified by the [`upload_to`](https://docs.djangoproject.com/en/1.9/ref/models/fields/#django.db.models.FileField.upload_to"django.db.models.FileField.upload_to" ) argument of the corresponding [`FileField`](https://docs.djangoproject.com/en/1.9/ref/models/fields/#django.db.models.FileField "django.db.models.FileField" ) when calling `form.save()`:
90+
91+
```python
92+
93+
from django.http import HttpResponseRedirect
94+
from django.shortcuts import render
95+
from .forms import ModelFormWithFileField
96+
97+
def upload_file(request):
98+
if request.method == 'POST':
99+
form = ModelFormWithFileField(request.POST, request.FILES)
100+
if form.is_valid():
101+
# file is saved
102+
form.save()
103+
return HttpResponseRedirect('/success/url/')
104+
else:
105+
form = ModelFormWithFileField()
106+
return render(request, 'upload.html', {'form': form})
107+
108+
```
109+
110+
If you are constructing an object manually, you can simply assign the file
111+
object from [`request.FILES`](https://docs.djangoproject.com/en/1.9/ref/request-response/#django.http.HttpRequest.FILES "django.http.HttpRequest.FILES" ) to the file field in the model:
112+
113+
```python
114+
115+
from django.http import HttpResponseRedirect
116+
from django.shortcuts import render
117+
from .forms import UploadFileForm
118+
from .models import ModelWithFileField
119+
120+
def upload_file(request):
121+
if request.method == 'POST':
122+
form = UploadFileForm(request.POST, request.FILES)
123+
if form.is_valid():
124+
instance = ModelWithFileField(file_field=request.FILES['file'])
125+
instance.save()
126+
return HttpResponseRedirect('/success/url/')
127+
else:
128+
form = UploadFileForm()
129+
return render(request, 'upload.html', {'form': form})
130+
131+
```
132+
133+
## Upload Handlers¶
134+
135+
When a user uploads a file, Django passes off the file data to an _upload
136+
handler_ - a small class that handles file data as it gets uploaded. Upload
137+
handlers are initially defined in the [`FILE_UPLOAD_HANDLERS`](https://docs.dj
138+
angoproject.com/en/1.9/ref/settings/#std:setting-FILE_UPLOAD_HANDLERS)
139+
setting, which defaults to:
140+
141+
```python
142+
143+
["django.core.files.uploadhandler.MemoryFileUploadHandler",
144+
"django.core.files.uploadhandler.TemporaryFileUploadHandler"]
145+
146+
```
147+
148+
Together [`MemoryFileUploadHandler`](https://docs.djangoproject.com/en/1.9/ref
149+
/files/uploads/#django.core.files.uploadhandler.MemoryFileUploadHandler
150+
"django.core.files.uploadhandler.MemoryFileUploadHandler" ) and [`TemporaryFil
151+
eUploadHandler`](https://docs.djangoproject.com/en/1.9/ref/files/uploads/#djan
152+
go.core.files.uploadhandler.TemporaryFileUploadHandler
153+
"django.core.files.uploadhandler.TemporaryFileUploadHandler" ) provide
154+
Django's default file upload behavior of reading small files into memory and
155+
large ones onto disk.
156+
157+
You can write custom handlers that customize how Django handles files. You
158+
could, for example, use custom handlers to enforce user-level quotas, compress
159+
data on the fly, render progress bars, and even send data to another storage
160+
location directly without storing it locally. See [Writing custom upload
161+
handlers](https://docs.djangoproject.com/en/1.9/ref/files/uploads/#custom-
162+
upload-handlers) for details on how you can customize or completely replace
163+
upload behavior.
164+
165+
### Where uploaded data is stored¶
166+
167+
Before you save uploaded files, the data needs to be stored somewhere.
168+
169+
By default, if an uploaded file is smaller than 2.5 megabytes, Django will
170+
hold the entire contents of the upload in memory. This means that saving the
171+
file involves only a read from memory and a write to disk and thus is very
172+
fast.
173+
174+
However, if an uploaded file is too large, Django will write the uploaded file
175+
to a temporary file stored in your system's temporary directory. On a Unix-
176+
like platform this means you can expect Django to generate a file called
177+
something like `/tmp/tmpzfp6I6.upload`. If an upload is large enough, you can
178+
watch this file grow in size as Django streams the data onto disk.
179+
180+
These specifics - 2.5 megabytes; `/tmp`; etc. - are simply "reasonable
181+
defaults" which can be customized as described in the next section.
182+
183+
### Changing upload handler behavior¶
184+
185+
There are a few settings which control Django's file upload behavior. See
186+
[File Upload Settings](https://docs.djangoproject.com/en/1.9/ref/settings
187+
/#file-upload-settings) for details.
188+
189+
### Modifying upload handlers on the fly¶
190+
191+
Sometimes particular views require different upload behavior. In these cases,
192+
you can override upload handlers on a per-request basis by modifying
193+
`request.upload_handlers`. By default, this list will contain the upload
194+
handlers given by [`FILE_UPLOAD_HANDLERS`](https://docs.djangoproject.com/en/1
195+
.9/ref/settings/#std:setting-FILE_UPLOAD_HANDLERS), but you can modify the
196+
list as you would any other list.
197+
198+
For instance, suppose you've written a `ProgressBarUploadHandler` that
199+
provides feedback on upload progress to some sort of AJAX widget. You'd add
200+
this handler to your upload handlers like this:
201+
202+
```python
203+
204+
request.upload_handlers.insert(0, ProgressBarUploadHandler())
205+
206+
```
207+
208+
You'd probably want to use `list.insert()` in this case (instead of
209+
`append()`) because a progress bar handler would need to run _before_ any
210+
other handlers. Remember, the upload handlers are processed in order.
211+
212+
If you want to replace the upload handlers completely, you can just assign a
213+
new list:
214+
215+
```python
216+
217+
request.upload_handlers = [ProgressBarUploadHandler()]
218+
219+
```
220+
221+
Note
222+
223+
You can only modify upload handlers _before_ accessing `request.POST` or
224+
`request.FILES` - it doesn't make sense to change upload handlers after upload
225+
handling has already started. If you try to modify `request.upload_handlers`
226+
after reading from `request.POST` or `request.FILES` Django will throw an
227+
error.
228+
229+
Thus, you should always modify uploading handlers as early in your view as
230+
possible.
231+
232+
Also, `request.POST` is accessed by [`CsrfViewMiddleware`](https://docs.django
233+
project.com/en/1.9/ref/middleware/#django.middleware.csrf.CsrfViewMiddleware
234+
"django.middleware.csrf.CsrfViewMiddleware" ) which is enabled by default.
235+
This means you will need to use [`csrf_exempt()`](https://docs.djangoproject.c
236+
om/en/1.9/ref/csrf/#django.views.decorators.csrf.csrf_exempt
237+
"django.views.decorators.csrf.csrf_exempt" ) on your view to allow you to
238+
change the upload handlers. You will then need to use [`csrf_protect()`](https
239+
://docs.djangoproject.com/en/1.9/ref/csrf/#django.views.decorators.csrf.csrf_p
240+
rotect "django.views.decorators.csrf.csrf_protect" ) on the function that
241+
actually processes the request. Note that this means that the handlers may
242+
start receiving the file upload before the CSRF checks have been done. Example
243+
code:
244+
245+
```python
246+
247+
from django.views.decorators.csrf import csrf_exempt, csrf_protect
248+
249+
@csrf_exempt
250+
def upload_file_view(request):
251+
request.upload_handlers.insert(0, ProgressBarUploadHandler())
252+
return _upload_file_view(request)
253+
254+
@csrf_protect
255+
def _upload_file_view(request):
256+
... # Process request
257+
258+
```

0 commit comments

Comments
 (0)