Задача:
Есть ПК без интернета но есть возможность перекинуть файл по 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 скрипт
Комментарии (44)
Squoworode
29.10.2019 18:40Могло не повезти и клиент, обнаружив отсутствие файла, пересоздал бы список загруженных фрагментов. В общем, не для каждого клиента рецепт.
ivan386 Автор
29.10.2019 18:44Если поставить на паузу переместить файл и закрыть клиент, то после открытия и запуска так и происходит.
Angmarets
29.10.2019 19:56+1я думаю имееться в виду повезло с клиентом. С uTorrent это не прокатит например. Файл вы не удалите пока не ОСТАНОВИТЕ закачку. Не пауза, а именно стоп. Затем при попытке продолжить закачку она упадет с ошибкой «файл не найден» пока не запустите «обновить хэш»
khim
30.10.2019 00:51Файл вы не удалите пока не ОСТАНОВИТЕ закачку.
Это кто ж вам запретит его удалить?
Другое дело, что удалить-то вы его удалите, а вот место не освободится… можно попробоватьfallocate
, конечно, но чем это закончится не очень ясно: там были проблемы с безопасностью и на многих телефонахfallocate
выпилили… даже CTS тест есть.
zzzzzzzzzzzz
31.10.2019 00:34Torrent-клиент очень странный. Мало того, что позволяет удалить находящийся «в работе» файл, так ещё и выставление флажка sparse — это, по сути, ошибка в клиенте.
ivan386 Автор
31.10.2019 08:48+1Почему установка sparse флага это ошибка?
zzzzzzzzzzzz
31.10.2019 10:21Этот флажок программа должна выставлять на файле, если она предполагает, что там кучи нулей будут в содержимом. Для торрента это (в общем случае) неверно. Функциональности типа «скачать только вторую половину файла» я в торрент-клиентах тоже не видел. Поэтому торрент-клиенту, вроде бы, незачем помечать файл sparse. Единственная возможная причина — «не выделять сразу весь объём файла, а то система из соображений безопасности начнёт его весь заполнять нулями, а это долго». Но это сомнительная оптимизация, поскольку в результате файл получится сильно фрагментированным.
ivan386 Автор
31.10.2019 10:46+1Ещё вариант что пользователь может отменить скачивание нескольких файлов из торрента а начало и конец файла могут находиться в одном блоке с соседними файлами. Их всеравно прийдётся скачать а без sparse флага файл будет занимать место полностью по большей части являясь пустым.
Так-же если отменили скачивание когда уже в файле есть несколько частей то они останутся и будут раздаваться а файл опять же не будет занимать много места.
Ну и удаление наверно доступно по тем же причинам. Так как в уже не нужном файле может быть много загруженных данных. Его можно удалить и освободить место для остальных.
Баг здесь только в том что клиент возможно не проверяет то что отдаёт. Хотябы то что блок заполнен нулями а должны быть данные.
Ну и клиент будет вечно бороться с антивирусом если втророй будет удалять файл а первый его перекачивать автоматически.
zzzzzzzzzzzz
31.10.2019 22:17Куски начал и концов ненужных файлов uTorrent (например) помещает в отдельный файл со странным именем ("~uTorrentPartFile_207807000.dat"). И это достаточно удобный вариант, т.к. недозагруженные куски не перемешиваются с полными файлами при просмотре каталога пользователем.
В целом, разные ситуации, в которых sparse полезен, придумать можно, но они выглядят не слишком значительными. А вот фрагментация получающихся таким образом файлов должна быть просто ужасающая (интересно было бы проверить численно). С другой стороны, если это фильм на одноразовый просмотр, то и не жалко, пусть себе винчестер тарахтит при просмотре…
А возможность удаления файла — это всё-таки очень странно. Логично было бы в начале скачивания открыть файл на запись и закрыть только после окончания загрузки (и тогда он так просто не удалится). А они, получается, на каждый кусочек: открыли файл — записали кусочек — закрыли файл?khim
31.10.2019 23:49А вот фрагментация получающихся таким образом файлов должна быть просто ужасающая (интересно было бы проверить численно).
На SSD пофиг…
Логично было бы в начале скачивания открыть файл на запись и закрыть только после окончания загрузки (и тогда он так просто не удалится).
Ещё раз: не лезьте со своими Windows-подходами в телефон. Удалится прекрасно.
А они, получается, на каждый кусочек: открыли файл — записали кусочек — закрыли файл?
Разумный подход, на самом деле: файлов в торренте может быть и сто тысяч, а файловые дескрипторы — ограниченный ресурс.
Demosfen
29.10.2019 21:43Планшет рутануть, замонтировать с компа директорию через самбу и хоть терабайты можно выносить. Или через otg (если планшет умеет) внешний диск зацепить. Но блин так из кусочков каждый раз собирать.....
ivan386 Автор
29.10.2019 21:53Внешний жёсткий диск по otg не получилось потключить так как диску нужно было внешнее питание. Без него планшет диск не тянет.
EndUser
30.10.2019 00:07Подскажите, пожалуйста, как? DroidSMB, SambaDroid и IceColdApps как-то не работают, LANdrive приходится каждый час, каждый раз руками подталкивать.
Demosfen
30.10.2019 05:47Честно говоря, уже не вспомню каким именно клиентом это делал на стике мк802-iii, но работало стабильно. Помню только что гуглил что-то типа android mount smb и дальше перебирал все известные клиенты на 4пда.
nochkin
29.10.2019 22:33+2Если я правильно понял, то после паузы клиент будет отдавать нули другим peer'ам, думая что эти блоки он уже скачал. Или я что-то напутал?
ivan386 Автор
29.10.2019 22:45Скорей всего так и есть. Поэтому после полного скачивания нужно обязательно запустить перепроверку торрента чтоб клиент понял какие блоки у него действительно есть и раздавал только их. Ну и надо снять галочку с файла чтоб он не пытался его опять докачать.
nochkin
29.10.2019 23:29Как запустить перепроверку после полного скачивания если полное скачивание невозможно из-за нехватки места на планшете?
ivan386 Автор
29.10.2019 23:35Так после окончания процедуры описанной в статье у торрент клиента будет иллюзия что скачан торрент полностью(что в общем то правда). Только вот данные ему доступны не полностью. Поэтому снимаем галку с файла и запускаем перепроверку.
nochkin
30.10.2019 05:20Так торрент клиент на планшете не сможет проверить, так как куски файла не доступны полностью.
Самый простой пример — торрентом является один какой-нибудь ISO файл.ivan386 Автор
30.10.2019 07:16В последнем цикле после проверки останутся части которые полнстью скачаны за последний цикл а также части которые которые были скачаны частично на начало цикла. Части которые были скачаны частично на момент запуска последнего цикла скачаются 2 раза так как хеш в первый раз не сойдётся.
Можете провести эсперимент. У меня всё получилось с клиентом Flud.
mayorovp
30.10.2019 10:15А что помешает ему проверить? Нули на месте тех кусков файла вполне доступны клиенту.
gromazeka
30.10.2019 16:25-1Где то в азии начинаются проекты по 6g со скоростью до 1 Тбит/с, миниатюрные флеш накопители обьемом 256-512 Гб уже давно стали ширпотребом, а он тут файлы по 4Гб кусками перекидывает, мог бы еще усложнить fat системой и кидать по 2Гб (но это наверное в след. статье ?), куда же ты паря смотришь, что ты делаешь, иди учись, работай и не занимайся ерундой!
Bersano
30.10.2019 17:05есть торрент-клиенты с последовательной скачкой данных (например, qbittorent), то есть не пришлось бы потом искать части и сравнивать их с null'ами. Не знаю правда, есть ли такая версия торрент-клиента для планшета.
ivan386 Автор
30.10.2019 17:19Во Flud тоже есть последовательная скачка. Это может ускорить слияние. Но даже при ней не представляю как алгоритм улучшить. На данный момент он упирается только в скорость чтения/записи.
Ну и если каждый после такой процедуры оставит хвост файла на раздаче то может оказаться что доступен останется только он. А при рандомной загрузке у каждого в последней части будут разные места заполнены.
legolegs
30.10.2019 18:21+1Я как всегда не смог сдержать зуд реализовать всё то-же самое шеллскриптом.
Если исходные версии файла из-под торрента числом 4 и называются payload_1, payload_2, payload_3 и payload_4 и известно, что торрент побит на сегменты в 1Мб (можно посмотреть в клиенте или выставить своё значение, хоть 4кб), то это скрипт склеит файл:
mkdir brokenfragments cd brokenfragments for i in {1..4}; do split --numeric-suffixes --bytes=1M --suffix-length=4 ../payload_${i} payload_${i}_ done for i in payload_1_* ; do N=${i#payload_1_} for j in {1..4}; do if <payload_${j}_${N} tr -d '\0' | read -n 1 ; then cp payload_${j}_${N} payload_x_$N continue; fi done; done cat payload_x_* > ../payload_x
Если в исходном файле свои собственные нули, то возможны нюансы. Команда
<файл tr -d '\0' | read -n 1
(со стековерфло) удаляет из файла "файл" нули и смотрит, не осталось ли чего; возвращает успех если не всё — нули.ivan386 Автор
30.10.2019 19:22Я не спец по шеллскрипту но не думаю что операционная система обрадуется файлу на каждый 1MB данных при обьёме в 16GB каждой части. Пустые сегменты будут занимать 4KB но также будут иметь запись в файловой таблице.
legolegs
30.10.2019 19:28+1Винда не радуется, линуксу норм.
Линейное чтение правильнее, но на шелле я пока не придумал как это сделать элегантно или хотя бы читаемо.
HansHelmut
Не пробовали использовать планшет как usb модем?
ivan386 Автор
Ну в данном случае этот ПК нельзя было выпускать в интернет. Но у других может быть вариант что провайдер ограничивает раздачу интернета с планшета или телефона.
iig
В интернет нельзя, но подключать USB можно, и файлы из интернетов можно :)
Ох уж эта безопасность ;)
vmspike
Вариант, когда провайдер ограничивает раздачу, обычно можно обойти так:
Таким образом у ПК будет доступ в интернет только у программ, настроенных работать через прокси.
bodqhrohro
vmspike
У автора была задача не выпускать ПК в интернет, и такой частичный выпуск, возможно, мог сгодиться.
А так верно замечено, что можно пользоваться соксификаторами, если удобно.
Для обхода провайдерских ограничений на раздачу трафика есть и другие способы, которые могут быть более удобными, но вариант с socks один из самых простых в настройке, не нужно никаких плясок с VPN и файерволом, не нужен рут на мобильном, работает довольно надёжно с любыми ОС.
Из возможных уязвимых мест такого подхода — нешифрованный трафик с ПК, там может быть не мобильный user-agent и другая информация, если провайдер заморочится, могут возникнуть подозрения.
mayorovp
Любой ли? Насколько я знаю, у таких решений всегда проблемы с протоколами, где информация о конечной точке передаётся по сети в сообщениях прикладного уровня. И торренты к этой категории как раз относятся.
bodqhrohro
Ну с некоторыми программами могут проблемы возникнуть, ибо это грязный хак по сути, да.
iBurokrat
При этом оператор не ограничивает торренты?