
Задача:
Есть ПК без интернета но есть возможность перекинуть файл по 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("[^\0]") -- есть данные в in_part and out_part:find("\0", 1, true) -- есть пустые места в out_part then local find_index = 1 --[[
Функция string.gsub подходит для задачи так как найдёт кусочки заполненные нулями и поставит то что передано ей.
--]] out_part = out_part:gsub("\0+", function(zero_string) if #in_part < find_index then return -- не на что менять end --[[
string.gsub не передаёт позицию в которой был найдено совпадение. Поэтому делаем параллельный поиск позиции zero_string при помощи функции string.find. Достаточно найти первый нулевой байт.
--]] local start_index = out_part:find("\0", 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("[^\0]") 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 скрипт
