管理文件

这个文档描述 Django 文件访问用于文件的 API,例如用户上传的文件。较底层的API足够通用,你可以为其他目的来使用它们。如果你想处理 "static files" (JS, CSS, etc.),可以查看 管理静态文件(比如图片、JavaScript、CSS)

默认情况下,Django 使用 MEDIA_ROOTMEDIA_URL 设置本地存储。下面的例子假设你在使用这些默认设置。

不过,Django 提供编写自定义 file storage systems 的方法,允许你完全自定义 Django 存储文件的位置和方式。这篇文档的后半部分描述了存储系统的工作方式。

在模型中使用文件

当你使用 FileFieldImageField 时,Django 提供了一组处理文件的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')

任何 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
'http://media.example.com/cars/chevy.jpg'

car.photo 是一个 File 对象,这意味着它拥有下面所描述的所有方法和属性。

注解

文件在数据库中作为保存模型的一部分,因此在模型被保存之前,不能依赖磁盘上使用的实际文件名。

例如,您可以通过将文件名设置为相对于文件存储位置的路径来更改文件名(如果你正在使用默认的 FileSystemStorage ,则为 MEDIA_ROOT )。

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

注解

虽然 ImageField 无图像数据属性,比如 height, width, and 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 的默认文件存储通过 DEFAULT_FILE_STORAGE 配置;如果你不显式地提供存储系统,这里会使用默认配置。

参阅下面内置默认文件存储系统的细节,也可以查看 编写一个自定义存储系统 来了解编写自己的文件存储系统的信息。

存储对象

虽然大部分时间你可以使用 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)

自定义存储系统( Custom storage systems )的工作方式也一样:将它们作为 storage 参数传递给 FileField

使用callable

New in Django 3.1.

你可以使用callable作为 FileFieldImageFieldstorage 参数。它允许你在运行时修改存储参数,不同环境选择不同存储,例如。

当模型类被加载时,callable将进行判断,并返回 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)
Back to Top