我正在编写一个程序,它可以从多个不同的服务器一次下载多个文件(当然,每个服务器一个下载线程!)。我担心磁盘上同时增长的多个文件会导致磁盘碎片,我想通过Content-Length
在开始下载之前为整个文件的长度(如标题所报告的)预先分配磁盘空间来缓解这种情况,理想情况下不增加文件的表观长度(所以我可以通过以附加模式打开部分下载的文件来恢复失败的下载)。
这可能以独立于平台的方式吗?
我做了一些谷歌搜索,发现这篇带有一些 C 代码的可爱文章可以完全按照您在 Windows 上的要求进行操作。这是翻译成的 C 代码ctypes
(为便于阅读而编写):
import ctypes
import msvcrt
# https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfileinformationbyhandle
set_file_information = ctypes.windll.kernel32.SetFileInformationByHandle
class AllocationInfo(ctypes.Structure):
_fields_ = [('AllocationSize', ctypes.c_longlong)]
def allocate(file, length):
"""Tell the filesystem to preallocate `length` bytes on disk for the specified `file` without increasing the
file's length.
In other words, advise the filesystem that you intend to write at least `length` bytes to the file.
"""
allocation_info = AllocationInfo(length)
retval = set_file_information(ctypes.c_long(msvcrt.get_osfhandle(file.fileno())),
ctypes.c_long(5), # constant for FileAllocationInfo in the FILE_INFO_BY_HANDLE_CLASS enum
ctypes.pointer(allocation_info),
ctypes.sizeof(allocation_info)
)
if retval != 1:
raise OSError('SetFileInformationByHandle failed')
这将更改文件在磁盘上的大小:如文件资源管理器中所示为您指定的长度(加上几千字节的元数据),但保持大小:不变。
然而,在我用谷歌搜索的半个小时里,我还没有找到在 POSIX 上做到这一点的方法。 fallocate()
实际上与您所追求的完全相反:它将文件的表观长度设置为您给它的长度,但将其分配为磁盘上的稀疏范围,因此同时写入多个文件仍会导致碎片。具有讽刺意味的是,Windows 具有 POSIX 所缺乏的文件管理功能,不是吗?
我只希望被证明是错误的,但我认为这是不可能的。
FILENAME = "somefile.bin"
SIZE = 4200000
with open(FILENAME, "wb") as file:
file.seek(SIZE - 1)
file.write(b"\0")
优点:
mmap
(内存映射)文件以对其执行写入(MADV_SEQUENTIAL
如果需要顺序访问),则非常有效。