Возможно, этот метод подойдет вам: скопируйте большие куски (используя уже установленный эффективный метод), затем используйте что-то вроде следующего сценария, чтобы разделить большие куски на более мелкие куски локально.
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 для использования API-интерфейсов Windows. Та же самая техника может быть легко реализована в Delphi или C ++.
Основная процедура находится в handle_command_line. Он берет имя файла и разбивает его на части в зависимости от функции get_one_hundred_pieces. Ваше приложение заменит более подходящую функцию для определения соответствующих экстентов.
Затем он копирует чанк в свой собственный файл и вызывает SetEndOfFile, чтобы сжать файл большего размера (поскольку содержимое теперь находится в своем собственном файле).
Я проверил это в отношении файла размером 1 ГБ, разбитого на 100 частей, и он запустился менее чем за 30 секунд. Кроме того, теоретически это должно выполняться с эффективным использованием пространства (не потребляя больше, чем общий размер файла плюс наибольший размер чанка в любой момент времени). Я подозреваю, что есть улучшения производительности, но это в основном подтверждение концепции.