Как-то давно писал трансфер файлов по сети.
В этом проекте требовалось создавать файл, который сразу существует на диске, имеет нужный размер но еще не содержит данных.
Вот примеры как создать такой файл:
length = 1024 * 1024 * 1024 * 100
with open(file_path, "wb") as out:
out.seek(length-1)
out.write(b"\0")
with open(file_path, "wb") as out:
out.truncate(1024 * 1024 * 1024 * 120)
truncate -s 100M test
Файл создается моментально и получается полностью состоящий из нулей. Более того, он не занимает место над диске!
Это называется sparse files - разреженные файлы. На таких файловых системах как ext4, XFS, Btrfs, ZFS файл автоматически становится разреженным если процесс пишет за пределы конца файла. В структуре файла создаются "дырки" которые автоматически при чтении вернут нули.
Если запустить тоже самое на Windows, то результат будет другой. Файл будет создаваться долго и реально займет место на диске.
NTFS умеет создавать разреженные файлы, но это надо активировать явно:
import os
import msvcrt
import ctypes
file_path = r"C:\file"
length = 1024 * 1024 * 1024 * 100 # 100 GB
with open(file_path, "wb") as f:
handle = msvcrt.get_osfhandle(f.fileno())
FSCTL_SET_SPARSE = 0x900C4
bytes_returned = ctypes.c_ulong()
ctypes.windll.kernel32.DeviceIoControl(
handle, FSCTL_SET_SPARSE, None, 0, None, 0,
ctypes.byref(bytes_returned), None
)
f.seek(length-1)
f.write(b"\0")
Таким образом мы делаем преалокацию файла с возможностью писать в любое место, например так работают торренты.
В моем случае было многопоточное скачивание разных кусков файлов с возможностью докачки.
При копировании таких файлов чаще всего копия занимает всё положенное ей место.
Чтобы учитывать такое свойство файла нужно использовать специальные опции
shutil.copyfile(src, dst, follow_symlinks=False)
rsync -S ...
robocopy /SPARSE ...
Для тестирования трансфера требовалось создавать реальные файлы с рандомными данными. Сделать это просто:
import os
with open(file_path, "wb") as out:
for _ in range(1024):
out.write(os.urandom(1024*1024*10))
dd if=/dev/urandom of=file.bin bs=1M count=10
Тут, конечно, никаких разреженных файлов быть не может.
#tricks