You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 19:50:14