拆分大文件没有副本?

时间:2009-10-06 23:17:43

标签: c++ c windows delphi winapi

问题: 是否有Windows API调用(可能只有NTFS)允许用户将非常大的文件拆分成许多其他文件而不实际复制任何数据(换句话说,指定连接文件之间的逻辑断点,文件名和大小)?

示例: SetFileValidData,NtSetInformationFile

情境: 我需要以编程方式从非本地驱动器(包括网络,USB和DVD驱动器)分发/复制10GB的文件。它由超过100,000个单独文件组成,中值大小约为16千字节,但加入了~2GB块。

然而,使用简单的FileStream api(64kb缓冲区)将文件从非本地驱动器上的块提取到本地硬盘驱动器上的单个文件似乎在我的机器上限制为大约4MB / s,而复制整个块使用Explorer的速度超过80MB / s!

复制整个块似乎是合乎逻辑的,但给Windows足够的信息来逻辑地分割文件(理论上应该能够非常非常快地发生)。

Vista安装不会做这样的事情吗?

6 个答案:

答案 0 :(得分:3)

虽然有卷影复制,但这些是一种全有或全无的方法 - 你不能只删除文件的一部分。它们也只是暂时的。同样,硬链接共享所有内容,没有例外。不幸的是,虽然一些实验性的Linux文件系统(如btrfs)支持它,但Windows上不支持仅删除部分文件。

答案 1 :(得分:3)

你不能在实践中。如果任何新边界与现有集群边界不一致,则数据必须物理移动。

对于高速拷贝,以异步方式读取输入文件,在16KB段中将其分解,将其发布到队列(在内存中)并设置线程池以通过写出这些16KB段来清空队列。考虑到这些大小,写入可能是同步的。考虑到本地I / O和远程I / O的速度,以及您有多个写入器线程的事实,队列溢出的可能性应该非常低。

答案 2 :(得分:0)

想一想:是否有足够的空间将大块复制到本地驱动器,然后使用它作为内存映射文件来处理它?我记得在某些地方进行了一些讨论 - 当这些文件使用Windows文件/页面缓存并且易于设置时非常快。

From Wikipediafrom StackOverflow

答案 3 :(得分:0)

也许这种技术对你有用:复制大块(使用已经建立的高效方法),然后使用类似下面的脚本将大块分割成本地较小的块。

from __future__ import division
import os
import sys
from win32file import CreateFile, SetEndOfFile, GetFileSize, SetFilePointer, ReadFile, WriteFile
import win32con
from itertools import tee, izip, imap

def xfrange(start, stop=None, step=None):
    """
    Like xrange(), but returns list of floats instead

    All numbers are generated on-demand using generators
    """

    if stop is None:
        stop = float(start)
        start = 0.0

    if step is None:
        step = 1.0

    cur = float(start)

    while cur < stop:
        yield cur
        cur += step


# from Python 2.6 docs
def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return izip(a, b)

def get_one_hundred_pieces(size):
    """
    Return start and stop extents for a file of given size
    that will break the file into 100 pieces of approximately
    the same length.

    >>> res = list(get_one_hundred_pieces(205))
    >>> len(res)
    100
    >>> res[:3]
    [(0, 2), (2, 4), (4, 6)]
    >>> res[-3:]
    [(199, 201), (201, 203), (203, 205)]
    """
    step = size / 100
    cap = lambda pos: min(pos, size)
    approx_partitions = xfrange(0, size+step, step)
    int_partitions = imap(lambda n: int(round(n)), approx_partitions)
    partitions = imap(cap, int_partitions)
    return pairwise(partitions)

def save_file_bytes(handle, length, filename):
    hr, data = ReadFile(handle, length)
    assert len(data) == length, "%s != %s" % (len(data), length)
    h_dest = CreateFile(
        filename,
        win32con.GENERIC_WRITE,
        0,
        None,
        win32con.CREATE_NEW,
        0,
        None,
        )
    code, wbytes = WriteFile(h_dest, data)
    assert code == 0
    assert wbytes == len(data), '%s != %s' % (wbytes, len(data))

def handle_command_line():
    filename = sys.argv[1]
    h = CreateFile(
        filename,
        win32con.GENERIC_WRITE | win32con.GENERIC_READ,
        0,
        None,
        win32con.OPEN_EXISTING,
        0,
        None,
        )
    size = GetFileSize(h)
    extents = get_one_hundred_pieces(size)
    for start, end in reversed(tuple(extents)):
        length = end - start
        last = end - 1
        SetFilePointer(h, start, win32con.FILE_BEGIN)
        target_filename = '%s-%d' % (filename, start)
        save_file_bytes(h, length, target_filename)
        SetFilePointer(h, start, win32con.FILE_BEGIN)
        SetEndOfFile(h)

if __name__ == '__main__':
    handle_command_line()

这是一个Python 2.6脚本,利用pywin32来利用Windows API。同样的技术可以很容易地在Delphi或C ++中实现。

主例程在handle_command_line中。它需要一个文件名,并根据get_one_hundred_pieces函数将该文件名拆分为块。您的应用程序将替换更合适的函数来确定适当的范围。

然后将块复制到自己的文件中并调用SetEndOfFile来缩小较大的文件(因为内容现在位于自己的文件中)。

我已经对1GB文件进行了测试,该文件分为100个,并且在不到30秒的时间内运行。此外,理论上这应该以节省空间的方式运行(在任何给定时间不超过总文件大小加上最大块大小)。我怀疑性能有所改善,但这主要是概念验证。

答案 4 :(得分:0)

您可以将文件的第二个块复制到新文件中,而不是截断原始文件。在这种方法中,您只复制文件的一半。

答案 5 :(得分:-1)

是否有理由不能调用操作系统的复制例程进行复制?这应该与Explorer一样。它否定了你奇怪的分裂事物的需要,我认为这不存在。