|
| 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