管理文件 — Django 文档

来自菜鸟教程
Django/docs/3.2.x/topics/files
跳转至:导航、​搜索

管理文件

本文档描述了 Django 对文件(例如用户上传的文件)的文件访问 API。 较低级别的 API 足够通用,您可以将它们用于其他目的。 如果要处理“静态文件”(JS、CSS 等),请参阅管理静态文件(例如 图片、JavaScript、CSS) .

默认情况下,Django 使用 :setting:`MEDIA_ROOT`:setting:`MEDIA_URL` 设置在本地存储文件。 下面的示例假定您正在使用这些默认值。

但是,Django 提供了编写自定义 文件存储系统 的方法,允许您完全自定义 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 对象,这意味着它具有下面描述的所有方法和属性。

笔记

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


例如,您可以通过将文件的 name 设置为相对于文件存储位置的路径来更改文件名(:setting:`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非图像数据属性,例如heightwidthsize在实例上可用,但不能使用底层图像数据无需重新打开图像。 例如:

>>> 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 的默认文件存储由 :setting:`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 类,它实现了基本的本地文件系统文件存储。

例如,无论您的 :setting:`MEDIA_ROOT` 设置是什么,以下代码都会将上传的文件存储在 /media/photos 下:

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


使用可调用

3.1 版中的新功能。


您可以将可调用对象用作 FileFieldImageFieldstorage 参数。 这允许您在运行时修改使用的存储,例如,为不同的环境选择不同的存储。

您的可调用对象将在您的模型类加载时进行评估,并且必须返回 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)