Довольно часто возникает необходимость в обмене файлами между компьютерами и телефонами, находящимися в одной локальной сети. Например, передать файл другому человеку, или себе, но на другое устройство. Дома или в офисе могут быть настроены сервисы синхронизации наподобие Дропбокса, общие папки и другие подобные средства. Но иногда может возникнуть необходимость во временном (или постоянном) средстве обмена файлами, доступном исключительно внутри сети, не ограниченном в скорости, поддерживающем крупные файлы и не требующем установки клиентских приложений. Я попробую найти решение этой задачи.
Кто эти люди на КПДВ? Это команда КВН "Союз", в одном из номеров которой прозвучала строка песни, созвучная с моей сегодняшней статьёй. А ещё это талантливые ребята, творчество которых не закончилось с окончанием карьеры в КВН.
Скрытый текст
Сначала разберёмся с возможными способами обмена файлами.
Флешки. Про дискеты, CD и внешние диски вспоминать не будем - суть та же, но эпоха уже другая. Их никогда нет под рукой в нужный момент, USB разъёмы заняты/плохо работают/в офисе отключены админами. Скорость страдает - файлы нужно сначала записать (очистив под них место), затем прочитать. Да и как-то стыдно заниматься подобным при наличии сети.
Общие папки Windows. Какая шикарная была возможность! За исключением того, что разные версии системы плохо между собой соединялись (впрочем, одинаковые иногда тоже), а расшаренная папка довольно быстро превращалась в неорганизованную файлопомойку, которую мог внезапно заразить какой-нибудь вирус. В основном пропала с ужесточением прав доступа и переходом на новую версию протокола, да и на телефонах это не работает.
FTP. Сервер нужно устанавливать, можно запустить временный (например Babyftp). Клиенты доступны для всех платформ, в старых версиях Windows был встроенный консольный клиент. Сейчас поддержку этого протокола убрали из браузеров. Мне доводилось сталкиваться с хитро настроенными файрволлами, которые установить соединение позволяли, а пропускать файлы отказывались. Ещё из интересных случаев - работающий уже 15 лет FTP сервер внезапно ушёл в оффлайн. Оказалось, что доблестный защитник Windows определил его как возможно, вирус и отправил в карантин, сорвав плановое копирование бэкапов.
Электронная почта. Тут с телефонами наоборот всё гораздо проще. Недостаток - файлы уходят наружу и как следствие время передачи по сети удваивается. Ещё есть вопросы с безопасностью, также крупные файлы не помещаются в ящик - придётся загружать в облако или на файлообменник. Обменники могут порадовать нескучной капчей и показать кучу рекламы, а облака требуют авторизации, и место там иногда внезапно заканчивается.
Мессенджеры - на мой взгляд, чемпионы по неумению обмениваться файлами. Они откажутся передавать файл из-за большого размера (целых 30 мегабайт!) или неподдерживаемого типа (чем TXT не угодил?) и пережмут до неузнаваемости фотографии. Те же проблемы с передачей наружу, ещё использование десктоп и веб-версий не всегда уместно.
В общем, рассматриваю кейс: компьютеры и телефоны в локальной сети. Браузер есть везде, поэтому логично использовать его. Необходим сервер, способный принимать и отдавать файлы достаточно большого объёма (гигабайты), с возможностью формирования коротких ссылок и/или кодов для скачивания. От концепции файлопомойки хотелось бы уйти: возможности просматривать список файлов не будет, для скачивания нужны ссылка или код. Получается файлообменник, только локальный и без рекламы. Отдельно уточню про имена файлов: русскоязычные имена поддерживаться должны, но без фанатизма: эмодзи, иероглифы и спецсимволы скорее всего нет.
Язык для реализации проекта - Python, в качестве веб-сервера буду использовать FastAPI. Он асинхронный и умеет передавать файлы частями, не загружая в память целиком. Хранить имена файлов и сгенерированные коды буду в базе данных SQLite, для небольшого сервиса её вполне достаточно. Тем более что чтение будет производиться только при запуске, а запись при добавлении файлов. Для работы с базой использую асинхронную библиотеку aiosqlite.
Алгоритм работы такой: пользователь на странице отправки выбирает файл и нажимает кнопку "Загрузить", файл отправляется на сервер и сохраняется в папку, имя которой генерируется в виде случайного 5-значного числа. Это число является кодом для скачивания, оно вместе с именем файла записывается в базу данных. После этого файл доступен для скачивания по ссылке, содержащей этот код, или на странице с полем ввода кода.
Скрытый текст
@app.get("/")
@app.get('/fo', response_class=HTMLResponse)
async def fo_page(file: str = Query(default="")):
if file:
fileid = int(file)
filename = files.get(fileid, '')
if filename:
return FileResponse(path=f'files/{fileid}/{filename}', filename=filename)
else:
return HTMLResponse(nof_page)
else:
return HTMLResponse(u_page)
@app.post("/fo")
async def upload(file: UploadFile = File(...)):
if file.filename:
fileid = randint(10000, 99999)
while fileid in files.keys():
fileid = randint(10000, 99999)
save_dir = f'files\\{fileid}'
if not os.path.exists(save_dir):
os.makedirs(save_dir)
try:
async with aiofiles.open(f'{save_dir}\\{file.filename}', 'wb') as f:
while contents := file.file.read(1024 * 1024):
await f.write(contents)
except Exception:
return {"message": "There was an error uploading the file"}
finally:
file.file.close()
files.update({fileid: file.filename})
await db.add_file_to_table(file.filename, fileid)
return HTMLResponse(done_page.format(fileid = fileid, filename = file.filename))
else:
return HTMLResponse(u_page)
Веб-сервер уместился в 50 строк кода (не считая шаблонов страниц), реализация работы с БД примерно столько же. Но сервис получился реально удобный. Файл размером 4 Гб загрузился и скачался без проблем на полной скорости сети. Возможны дальнейшие пути развития:
Добавить автоудаление файлов по таймеру или после определённого количества скачиваний
Генератор QR кодов для передачи ссылки на телефон
Логгирование, админка с возможностью просматривать и чистить список файлов
"Золотые" короткие ссылки на наиболее востребованные файлы
Телеграм бот для приёма и отправки файлов извне локальной сети
Полный код доступен по ссылке.