Core.Files 模块
Django 的核心模块之一,封装文件操作,提供对文件的上传、下载、删除、查看大小等功能。
提供的功能
- 存储后端解耦,支持多种存储后端,使得在不同存储后端中的文件数据都能以相同的方式操作
- 文件上传、下载、删除、查看大小等功能
支持的文件存储后端
- FileSystemStorage:基于本地文件系统的存储后端,适用于开发环境和小型项目。
- MemoryStorage:基于内存的存储后端,适用于开发环境,小型项目,处理一些临时文件。
第三方存储后端库
- django-storages:支持 S3, LibCloud, Azure, DigitalOcean, Google Cloud Storage, Backblaze B2, SFTP, FTP, Dropbox, Oracle Cloud 等云存储服务。
- django-oss-storage: 支持阿里云 OSS 对象存储服务, 可能已经失效但仍可以 fork 修改。
- django-qiniu-storage: 支持七牛云对象存储服务, 可能已经失效但仍可以 fork 修改。
支持的文件类型
- File:普通文件,可以是任何类型的文件,如图片、视频、文档等。
- ImageFile:图片文件,继承自 File,提供图片处理的功能,也没什么功能就多了,height,width 两个方法,需要安装 Pillow 库。
- UploadedFile: 抽象类,提供文件上传的基本功能,主要是满足 form-data 上传的文件。
- TemporaryUploadedFile(继承自 UploadedFile): 存放在临时文件中
- InMemoryUploadedFile(继承自 UploadedFile): 存储在内存中
可以学习的地方
- 自定义文件存储后端
python
class Storage:
# 文件存储后端无关的接口,可以提供默认实现
def open(self, name, mode="rb"): ...
def save(self, name, content, max_length=None): ...
def get_valid_name(self, name): ...
def get_alternative_name(self, file_root, file_ext): ...
def get_available_name(self, name, max_length=None): ...
def generate_filename(self, filename): ...
def path(self, name): ...
# 与文件存储后端相关的接口,需要子类实现
def _open(self, name, mode="rb"):
raise NotImplementedError(
"subclasses of Storage must provide an _open() method"
)
def delete(self, name):
"""
Delete the specified file from the storage system.
"""
raise NotImplementedError(
"subclasses of Storage must provide a delete() method"
)
def exists(self, name):
"""
Return True if a file referenced by the given name already exists in the
storage system, or False if the name is available for a new file.
"""
raise NotImplementedError(
"subclasses of Storage must provide an exists() method"
)
def listdir(self, path):
"""
List the contents of the specified path. Return a 2-tuple of lists:
the first item being directories, the second item being files.
"""
raise NotImplementedError(
"subclasses of Storage must provide a listdir() method"
)
def size(self, name):
"""
Return the total size, in bytes, of the file specified by name.
"""
raise NotImplementedError("subclasses of Storage must provide a size() method")
def url(self, name):
"""
Return an absolute URL where the file's contents can be accessed
directly by a web browser.
"""
raise NotImplementedError("subclasses of Storage must provide a url() method")
def get_accessed_time(self, name):
"""
Return the last accessed time (as a datetime) of the file specified by
name. The datetime will be timezone-aware if USE_TZ=True.
"""
raise NotImplementedError(
"subclasses of Storage must provide a get_accessed_time() method"
)
def get_created_time(self, name):
"""
Return the creation time (as a datetime) of the file specified by name.
The datetime will be timezone-aware if USE_TZ=True.
"""
raise NotImplementedError(
"subclasses of Storage must provide a get_created_time() method"
)
def get_modified_time(self, name):
"""
Return the last modified time (as a datetime) of the file specified by
name. The datetime will be timezone-aware if USE_TZ=True.
"""
raise NotImplementedError(
"subclasses of Storage must provide a get_modified_time() method"
)
- 文件读写锁
python
def _fd(f):
"""Get a filedescriptor from something which could be a file or an fd."""
return f.fileno() if hasattr(f, "fileno") else f
if os.name == "nt":
import msvcrt
from ctypes import (
POINTER,
Structure,
Union,
WinDLL,
byref,
c_int64,
c_ulong,
c_void_p,
sizeof,
)
from ctypes.wintypes import BOOL, DWORD, HANDLE
LOCK_SH = 0 # the default
LOCK_NB = 0x1 # LOCKFILE_FAIL_IMMEDIATELY
LOCK_EX = 0x2 # LOCKFILE_EXCLUSIVE_LOCK
# --- Adapted from the pyserial project ---
# detect size of ULONG_PTR
if sizeof(c_ulong) != sizeof(c_void_p):
ULONG_PTR = c_int64
else:
ULONG_PTR = c_ulong
PVOID = c_void_p
# --- Union inside Structure by stackoverflow:3480240 ---
class _OFFSET(Structure):
_fields_ = [("Offset", DWORD), ("OffsetHigh", DWORD)]
class _OFFSET_UNION(Union):
_anonymous_ = ["_offset"]
_fields_ = [("_offset", _OFFSET), ("Pointer", PVOID)]
class OVERLAPPED(Structure):
_anonymous_ = ["_offset_union"]
_fields_ = [
("Internal", ULONG_PTR),
("InternalHigh", ULONG_PTR),
("_offset_union", _OFFSET_UNION),
("hEvent", HANDLE),
]
LPOVERLAPPED = POINTER(OVERLAPPED)
# --- Define function prototypes for extra safety ---
kernel32 = WinDLL("kernel32")
LockFileEx = kernel32.LockFileEx
LockFileEx.restype = BOOL
LockFileEx.argtypes = [HANDLE, DWORD, DWORD, DWORD, DWORD, LPOVERLAPPED]
UnlockFileEx = kernel32.UnlockFileEx
UnlockFileEx.restype = BOOL
UnlockFileEx.argtypes = [HANDLE, DWORD, DWORD, DWORD, LPOVERLAPPED]
def lock(f, flags):
hfile = msvcrt.get_osfhandle(_fd(f))
overlapped = OVERLAPPED()
ret = LockFileEx(hfile, flags, 0, 0, 0xFFFF0000, byref(overlapped))
return bool(ret)
def unlock(f):
hfile = msvcrt.get_osfhandle(_fd(f))
overlapped = OVERLAPPED()
ret = UnlockFileEx(hfile, 0, 0, 0xFFFF0000, byref(overlapped))
return bool(ret)
else:
try:
import fcntl
LOCK_SH = fcntl.LOCK_SH # shared lock
LOCK_NB = fcntl.LOCK_NB # non-blocking
LOCK_EX = fcntl.LOCK_EX
except (ImportError, AttributeError):
# File locking is not supported.
LOCK_EX = LOCK_SH = LOCK_NB = 0
# Dummy functions that don't do anything.
def lock(f, flags):
# File is not locked
return False
def unlock(f):
# File is unlocked
return True
else:
def lock(f, flags):
try:
fcntl.flock(_fd(f), flags)
return True
except BlockingIOError:
return False
def unlock(f):
fcntl.flock(_fd(f), fcntl.LOCK_UN)
return True
总结
对于 web 服务端开发,文件操作是日常。但是文件的用途有很多,处置方式也很多。例如不同的文件类型放置在不同的存储后端,以及文件的访问控制、时效性也可以通过拓展存储后端来实现。
利用 django 这种分离文件操作和存储后端的框架,在开发中可以很好的解决大部分文件类需求。