文件管理¶
本文档描述了 Django 用于访问文件(例如用户上传的文件)的 API。底层 API 足够通用,您可以将其用于其他目的。如果您想处理“静态文件”(JS、CSS 等),请参阅 如何管理静态文件(例如图像、JavaScript、CSS)。
默认情况下,Django 使用 MEDIA_ROOT
和 MEDIA_URL
设置在本地存储文件。以下示例假设您使用的是这些默认设置。
但是,Django 提供了编写自定义 文件存储系统 的方法,允许您完全自定义 Django 存储文件的位置和方式。本文档的后半部分描述了这些存储系统的工作原理。
在模型中使用文件¶
当您使用 FileField
或 ImageField
时,Django 提供了一组 API,您可以使用这些 API 来处理该文件。
考虑以下模型,使用 ImageField
来存储照片
from django.db import models
class Car(models.Model):
name = models.CharField(max_length=255)
price = models.DecimalField(max_digits=5, decimal_places=2)
photo = models.ImageField(upload_to="cars")
specs = models.FileField(upload_to="specs")
任何 Car
实例都将具有一个 photo
属性,您可以使用它来获取附加照片的详细信息
>>> car = Car.objects.get(name="57 Chevy")
>>> car.photo
<ImageFieldFile: cars/chevy.jpg>
>>> car.photo.name
'cars/chevy.jpg'
>>> car.photo.path
'/media/cars/chevy.jpg'
>>> car.photo.url
'https://media.example.com/cars/chevy.jpg'
此对象(示例中的 car.photo
)是一个 File
对象,这意味着它具有下面描述的所有方法和属性。
注意
文件作为保存数据库中的模型的一部分保存,因此在模型保存后才能依赖实际使用的磁盘文件名。
例如,您可以通过将文件的 name
设置为相对于文件存储位置的路径来更改文件名(如果您使用的是默认的 MEDIA_ROOT
,则为 FileSystemStorage
)。
>>> import os
>>> from django.conf import settings
>>> initial_path = car.photo.path
>>> car.photo.name = "cars/chevy_ii.jpg"
>>> new_path = settings.MEDIA_ROOT + car.photo.name
>>> # Move the file on the filesystem
>>> os.rename(initial_path, new_path)
>>> car.save()
>>> car.photo.path
'/media/cars/chevy_ii.jpg'
>>> car.photo.path == new_path
True
将磁盘上的现有文件保存到 FileField
>>> from pathlib import Path
>>> from django.core.files import File
>>> path = Path("/some/external/specs.pdf")
>>> car = Car.objects.get(name="57 Chevy")
>>> with path.open(mode="rb") as f:
... car.specs = File(f, name=path.name)
... car.save()
...
注意
虽然 ImageField
的非图像数据属性(例如 height
、width
和 size
)在实例中可用,但底层图像数据无法在没有重新打开图像的情况下使用。例如
>>> from PIL import Image
>>> car = Car.objects.get(name="57 Chevy")
>>> car.photo.width
191
>>> car.photo.height
287
>>> image = Image.open(car.photo)
# Raises ValueError: seek of closed file.
>>> car.photo.open()
<ImageFieldFile: cars/chevy.jpg>
>>> image = Image.open(car.photo)
>>> image
<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=191x287 at 0x7F99A94E9048>
File
对象¶
在内部,Django 在任何需要表示文件时都使用 django.core.files.File
实例。
大多数情况下,您将使用 Django 提供的 File
(即如上所述附加到模型的文件,或者可能是上传的文件)。
如果您需要自己构造一个 File
,最简单的方法是使用 Python 内置的 file
对象创建一个。
>>> from django.core.files import File
# Create a Python file object using open()
>>> f = open("/path/to/hello.world", "w")
>>> myfile = File(f)
现在您可以使用 File
类的任何已记录属性和方法。
请注意,以这种方式创建的文件不会自动关闭。可以使用以下方法自动关闭文件:
>>> from django.core.files import File
# Create a Python file object using open() and the with statement
>>> with open("/path/to/hello.world", "w") as f:
... myfile = File(f)
... myfile.write("Hello World")
...
>>> myfile.closed
True
>>> f.closed
True
在对大量对象进行循环访问文件字段时,关闭文件尤其重要。如果在访问文件后没有手动关闭文件,则可能会出现文件描述符不足的风险。这可能导致以下错误:
OSError: [Errno 24] Too many open files
文件存储¶
在幕后,Django 将有关如何以及在何处存储文件的决策委托给文件存储系统。这是实际了解文件系统、打开和读取文件等的个体。
Django 的默认文件存储是 '
django.core.files.storage.FileSystemStorage
'
。如果您没有在 STORAGES
设置的 default
密钥中显式提供存储系统,则将使用此存储系统。
有关内置默认文件存储系统的详细信息,请参见下文,有关编写自己的文件存储系统的信息,请参见 如何编写自定义存储类。
存储对象¶
虽然大多数时候您都想使用 File
对象(它委托给该文件的正确存储),但您可以直接使用文件存储系统。您可以创建某个自定义文件存储类的实例,或者(通常更有用)您可以使用全局默认存储系统。
>>> from django.core.files.base import ContentFile
>>> from django.core.files.storage import default_storage
>>> path = default_storage.save("path/to/file", ContentFile(b"new content"))
>>> path
'path/to/file'
>>> default_storage.size(path)
11
>>> default_storage.open(path).read()
b'new content'
>>> default_storage.delete(path)
>>> default_storage.exists(path)
False
有关文件存储 API,请参阅 文件存储 API。
内置文件系统存储类¶
Django 附带一个 django.core.files.storage.FileSystemStorage
类,该类实现了基本的本地文件系统文件存储。
例如,以下代码将上传的文件存储在 /media/photos
下,而不管您的 MEDIA_ROOT
设置是什么。
from django.core.files.storage import FileSystemStorage
from django.db import models
fs = FileSystemStorage(location="/media/photos")
class Car(models.Model):
...
photo = models.ImageField(storage=fs)
使用可调用对象¶
您可以将可调用对象用作 storage
参数,用于 FileField
或 ImageField
。这允许您在运行时修改使用的存储,例如为不同的环境选择不同的存储。
在加载模型类时将评估您的可调用对象,并且必须返回 Storage
的实例。
例如
from django.conf import settings
from django.db import models
from .storages import MyLocalStorage, MyRemoteStorage
def select_storage():
return MyLocalStorage() if settings.DEBUG else MyRemoteStorage()
class MyModel(models.Model):
my_file = models.FileField(storage=select_storage)
为了设置在 STORAGES
设置中定义的存储,您可以使用 storages
from django.core.files.storage import storages
def select_storage():
return storages["mystorage"]
class MyModel(models.Model):
upload = models.FileField(storage=select_storage)