如何在Windows10 NTFS格式SSD上用Python创建真正的稀疏文件?
在Windows 10 NTFS SSD上创建真正的稀疏文件
你的代码存在两个核心问题,导致无法生成真正的稀疏文件:
- 仅设置
FILE_ATTRIBUTE_SPARSE_FILE属性不足以启用稀疏特性,必须通过FSCTL_SET_SPARSE指令明确告知NTFS系统将文件标记为稀疏 - 验证文件稀疏性时,已经关闭了文件句柄,导致验证逻辑完全失效
以下是修正后的完整实现,包含正确的稀疏文件创建与验证流程:
import ctypes from ctypes import wintypes # 定义Windows API常量 GENERIC_WRITE = 0x40000000 CREATE_ALWAYS = 2 FILE_ATTRIBUTE_NORMAL = 0x80 FSCTL_SET_SPARSE = 0x000900C4 FSCTL_QUERY_ALLOCATED_RANGES = 0x000900C8 # 定义FSCTL_SET_SPARSE所需的结构体 class SPARSE_SET_CONTROL_BLOCK(ctypes.Structure): _fields_ = [ ("Size", wintypes.DWORD), ("SetSparse", wintypes.BOOL) ] # 定义FSCTL_QUERY_ALLOCATED_RANGES所需的结构体 class FILE_ALLOCATED_RANGE_BUFFER(ctypes.Structure): _fields_ = [ ("FileOffset", wintypes.LARGE_INTEGER), ("Length", wintypes.LARGE_INTEGER) ] def create_sparse_file(file_name, desired_file_size): # 打开/创建文件,请求写入权限 handle = ctypes.windll.kernel32.CreateFileW( file_name, GENERIC_WRITE, 0, None, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, None ) if handle == wintypes.HANDLE(-1).value: print(f"创建文件失败,错误码: {ctypes.GetLastError()}") return try: # 启用稀疏文件特性(核心步骤) sparse_control = SPARSE_SET_CONTROL_BLOCK() sparse_control.Size = ctypes.sizeof(SPARSE_SET_CONTROL_BLOCK) sparse_control.SetSparse = True bytes_returned = wintypes.DWORD(0) success = ctypes.windll.kernel32.DeviceIoControl( handle, FSCTL_SET_SPARSE, ctypes.byref(sparse_control), ctypes.sizeof(sparse_control), None, 0, ctypes.byref(bytes_returned), None ) if not success: print(f"启用稀疏特性失败,错误码: {ctypes.GetLastError()}") return # 设置文件逻辑大小(仅分配逻辑空间,不占用实际磁盘) file_offset = wintypes.LARGE_INTEGER() file_offset.QuadPart = desired_file_size new_pointer = ctypes.windll.kernel32.SetFilePointerEx( handle, file_offset, None, 0 # FILE_BEGIN ) if new_pointer == 0 and ctypes.GetLastError() != 0: print(f"设置文件指针失败,错误码: {ctypes.GetLastError()}") return success = ctypes.windll.kernel32.SetEndOfFile(handle) if not success: print(f"设置文件结尾失败,错误码: {ctypes.GetLastError()}") return # 验证稀疏文件并查询实际磁盘占用 query_buffer = FILE_ALLOCATED_RANGE_BUFFER() query_buffer.FileOffset.QuadPart = 0 query_buffer.Length.QuadPart = desired_file_size # 先查询所需缓冲区大小 buffer_size = wintypes.DWORD(0) ctypes.windll.kernel32.DeviceIoControl( handle, FSCTL_QUERY_ALLOCATED_RANGES, ctypes.byref(query_buffer), ctypes.sizeof(query_buffer), None, 0, ctypes.byref(buffer_size), None ) # 分配缓冲区并查询实际分配范围 allocated_ranges = (FILE_ALLOCATED_RANGE_BUFFER * (buffer_size.value // ctypes.sizeof(FILE_ALLOCATED_RANGE_BUFFER)))() success = ctypes.windll.kernel32.DeviceIoControl( handle, FSCTL_QUERY_ALLOCATED_RANGES, ctypes.byref(query_buffer), ctypes.sizeof(query_buffer), ctypes.byref(allocated_ranges), buffer_size.value, ctypes.byref(bytes_returned), None ) if success: total_allocated = sum(range_info.Length.QuadPart for range_info in allocated_ranges[:bytes_returned.value // ctypes.sizeof(FILE_ALLOCATED_RANGE_BUFFER)]) print(f"稀疏文件创建成功!") print(f"文件逻辑大小: {desired_file_size / 1024 / 1024:.2f} MB") print(f"实际磁盘占用: {total_allocated / 1024 / 1024:.2f} MB") else: print(f"验证稀疏文件失败,错误码: {ctypes.GetLastError()}") finally: # 确保文件句柄被正确关闭 ctypes.windll.kernel32.CloseHandle(handle) if __name__ == '__main__': file_name = r'G:\test.iso' desired_file_size = 1024 * 1024 * 1024 # 1 GB create_sparse_file(file_name, desired_file_size)
关键修正说明:
- 启用稀疏特性:通过
DeviceIoControl发送FSCTL_SET_SPARSE指令是核心步骤,仅设置文件属性无法让NTFS真正启用稀疏存储 - 64位偏移支持:使用
SetFilePointerEx替代旧版SetFilePointer,支持超过4GB的文件大小 - 有效验证:在文件句柄未关闭时执行
FSCTL_QUERY_ALLOCATED_RANGES,准确获取实际占用的磁盘空间 - 资源安全:使用
try-finally确保文件句柄始终被正确关闭,避免资源泄漏
内容的提问来源于stack exchange,提问作者Bo Wang
相关产品推荐
相关产品推荐

