Задача:
Есть ПК без интернета но есть возможность перекинуть файл по USB. Есть планшет с интернетом с которого этот файл можно перекинуть. На планшет можно скачать нужный торрент но не достаточно свободного места. Файл в торренте один и большой.
Путь к решению:
Я запустил торрент на загрузку. Когда свободное место почти подошло к концу я поставил загрузку на паузу. Подключил планшет к ПК и переместил файл с планшета на ПК. Отжал паузу и к моему удивлению файл был снова создан и торрент продолжил качаться дальше как ни в чем не бывало.
Благодаря тому что торрент клиент устанавливает sparse флаг файлу в который записывает полученные данные система не пытается зарезервировать сразу 16GB и не возникнет ошибки при попытке записи в файл дальше 4GB.
Повторив процедуру четыре раза я получил на ПК четыре файла в котором разные части одного и того же торрента. Теперь осталось собрать их воедино. Процедура по сути простая. Нужно заменить нуль байты на другое значение если оно есть в данной позиции в одном из четырёх файлов.
Мне казалось что такая простая программка должна быть в интернете. Неужели никто не сталкивался с такой задачей? Но я понял что даже не знаю по каким ключевым словам её искать. Поэтому я быстро накидал Lua скрипт под эту задачу а теперь уже и оптимизировал его. Им и хочу поделиться.
Загружаем торрент по частям
- запускаем загрузку торрента на первом устройстве
- ждём пока заполнится ПЗУ
- ставим загрузку на паузу
- переносим файл на второе устройство и добавляем к имени файла цифру
- возвращаемся к первому пункту до тех пор пока файл не скачается полностью
Сливаем части в один файл
После того как получена последняя часть необходимо собрать их в один целый файл.
Задача простая:
- Читаем все части одновременно
- Если в какой то части в позиции не нулевой байт то пишем на выход его иначе пишем ноль
Функция merge_part
принимает массив потоков streams_in
из которых читает часть размером buffer_length
и возвращает результат слияния частей из разных потоков.
function merge_part(streams_in, buffer_length)
local out_part
for _, stream in ipairs(streams_in) do
local in_part = stream:read(buffer_length)
if not out_part then
out_part = in_part -- просто копируем часть из первого файла
elseif in_part and #in_part > 0 then
if #out_part < #in_part then
out_part, in_part = in_part, out_part
end
if out_part ~= in_part -- данные различаются
and in_part:find("[^]") -- есть данные в in_part
and out_part:find("", 1, true) -- есть пустые места в out_part
then
local find_index = 1
--[[
Функция string.gsub
подходит для задачи так как найдёт кусочки заполненные нулями и поставит то что передано ей.
--]]
out_part = out_part:gsub("+", function(zero_string)
if #in_part < find_index then
return -- не на что менять
end
--[[
string.gsub
не передаёт позицию в которой был найдено совпадение. Поэтому делаем параллельный поиск позиции zero_string
при помощи функции string.find
. Достаточно найти первый нулевой байт.
--]]
local start_index = out_part:find("", find_index, true)
find_index = start_index + #zero_string
--[[
Теперь если в in_part
есть данные для out_part
копируем их.
--]]
if #in_part >= start_index then
local end_index = start_index + #zero_string - 1
--[[
Вырезаем из in_part
часть соответствующую последовательности нулей.
--]]
local part = in_part:sub(start_index, end_index)
if (part:byte(1) ~= 0) or part:find("[^]") then
--[[
В part
есть данные.
--]]
if #part == #zero_string then
return part
else
--[[
part
оказался меньше чем последовательность нулей. Дополняем его ими.
--]]
return part..zero_string:sub(1, end_index - #in_part)
end
end
end
end)
end
end
end
return out_part
end
Заключение
Таким образом удалось скачать и собрать этот файл на ПК. После слияния я вытащил с планшета торрент файл. Установил на ПК торрент клиент и проверил им файл.
Последнюю скачанную часть на планшете можно оставить на раздаче но нужно включить перед этим повторную проверку частей и снять галочку с файла чтобы он заново не скачивался.
Использовались:
- Торрент клиент Flud на планшете.
- Торрент клиент qBittorent на ПК.
- Lua скрипт
Автор: ivan386