# Django 5.2.8 documentation

# 管理文件

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

默认情况下，Django 使用 `MEDIA_ROOT` 和 `MEDIA_URL` 设置本地存储。下面的例子假设你在使用这些默认设置。

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

## 在模型中使用文件

当你使用 `FileField` 或 `ImageField` 时，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")
        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` 对象，这意味着它拥有下面所描述的所有方法和属性。

Note

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

例如，您可以通过将文件的 `name` 设置为相对于文件存储位置的路径（如果使用默认的 `FileSystemStorage`，则为 `MEDIA_ROOT`）来更改文件名：

    >>> import os
    >>> from django.conf import settings
    >>> initial_path = car.photo.path
    >>> car.photo.name = "cars/chevy_ii.jpg"
    >>> new_path = os.path.join(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()
    ...

Note

虽然 `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)

自定义存储系统（ Custom storage systems ）的工作方式也一样：将它们作为 `storage` 参数传递给 `FileField` 。

### 使用callable

你可以使用callable作为 `FileField` 或 `ImageField` 的 `storage` 参数。它允许你在运行时修改存储参数，不同环境选择不同存储，例如。

当模型类被加载时，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)

要设置在 `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)

Because the callable is evaluated when your models classes are loaded, if you need to override the `STORAGES` setting in tests, you should use a `LazyObject` subclass instead:

    from django.core.files.storage import storages
    from django.utils.functional import LazyObject

    class OtherStorage(LazyObject):
        def _setup(self):
            self._wrapped = storages["mystorage"]

    my_storage = OtherStorage()

    class MyModel(models.Model):
        upload = models.FileField(storage=my_storage)

The `LazyObject` delays the evaluation of the storage until it's actually needed, allowing `override_settings()` to take effect:

    @override_settings(
        STORAGES={
            "mystorage": {
                "BACKEND": "django.core.files.storage.InMemoryStorage",
            }
        }
    )
    def test_storage():
        model = MyModel()
        assert isinstance(model.upload.storage, InMemoryStorage)