Жаль, что мы не умеем обмениваться файлами

Довольно часто возникает необходимость в обмене файлами между компьютерами и телефонами, находящимися в одной локальной сети. Например, передать файл другому человеку, или себе, но на другое устройство. Дома или в офисе могут быть настроены сервисы синхронизации наподобие Дропбокса, общие папки и другие подобные средства. Но иногда может возникнуть необходимость во временном (или постоянном) средстве обмена файлами, доступном исключительно внутри сети, не ограниченном в скорости, поддерживающем крупные файлы и не требующем установки клиентских приложений. Я попробую найти решение этой задачи.

Изображение из открытых источников

Изображение из открытых источников

Кто эти люди на КПДВ? Это команда КВН «Союз», в одном из номеров которой прозвучала строка песни, созвучная с моей сегодняшней статьёй. А ещё это талантливые ребята, творчество которых не закончилось с окончанием карьеры в КВН.

Скрытый текст

Ссылка на песню

Сначала разберёмся с возможными способами обмена файлами.

Флешки. Про дискеты, 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 кодов для передачи ссылки на телефон

  • Логгирование, админка с возможностью просматривать и чистить список файлов

  • «Золотые» короткие ссылки на наиболее востребованные файлы

  • Телеграм бот для приёма и отправки файлов извне локальной сети

Полный код доступен по ссылке.

© Habrahabr.ru