Занимался разработкой проекта на laravel, технически ничего особенного. Сложность была в том, что сервер располагается на стороне клиента, подключаться к нему можно через промежуточный сервер, подключение к которому идет через vpn. Правки заливать можно через архивирование кода, загрузкой на яндекс.диск, отправкой ссылки, на сервер загружают этот архив и его нужно распаковать. Надо ли говорить, что в таком случае там вылезает масса ошибок с правами и путями к файлам. Стороннее ПО на сервер поставить нельзя, буфер обмена не работает, поэтому скопировать файлы не получается. Исходящие запросы с сервера так же заблокированы. Входящие ограничены размером около 300кб
Все что может делать сервер - принимать запрос и отвечать на него.
Исходя из такой ситуации был написан код, который делит файлы на части и отправляет файлы POST запросами. Это позволяет отправлять файлы на сервер напрямую.
Как работает
- fread читает бинарные данные из файла
- bin2hex переводит в строку
- str_split разбивает строку на части, по допустимому размеру запроса
- отправляется обычная форма на файл-приемник
- там каждая часть дописывается во временный файл
- когда приходит параметр last = 1 содержимое файла читается как строка и сохраняется как бинарные данные с помощью hex2bin
Код отправки
protected $signature = 'app:send-file {filepath}'; public function handle() { $filePath = $this->argument('filepath'); $path = base_path($filePath); $fileName = basename($path); $file = fopen($path, "rb"); $bin = fread($file, filesize($path)); $hex = bin2hex($bin); $chunks = str_split($hex, 1024 * 300); $salt = 'some_salt'; echo 'File: ' . $fileName . PHP_EOL; echo 'Chunks: ' . sizeof($chunks) . PHP_EOL; foreach ($chunks as $index => $chunk) { echo 'Sending: ' . ($index + 1 . ' of ' . sizeof($chunks)) . PHP_EOL; $isLast = intval($index == count($chunks) - 1); $hash = md5($filePath . $chunk . $isLast . $salt); $opts = [ 'http' => [ 'method' => "POST", 'header' => "Content-Type: application/x-www-form-urlencoded", 'content' => http_build_query([ 'hash' => $hash, 'file_path' => $filePath, 'chunk' => $chunk, 'last' => $isLast, ]) ] ]; $context = stream_context_create($opts); $result = file_get_contents('https://host/receiver.php?part=' . $index, false, $context); if (!$result) { echo 'Fail'; exit; } sleep(1); } echo 'Sent' . PHP_EOL; }
Отправка выглядит так
php artisan app:send-file some_dir/some-file-path.php
Код получения на стороне сервера. В папке received-files создается временный файл, по завершению приема данных файл переносится в путь some_dir/some-file-path.php
<?php try{ $salt = 'some_salt'; $isLast = $_POST['last'] == '1'; $chunk = $_POST['chunk']; $fileName = $_POST['file_name']; $filePath = $_POST['file_path']; $hash = md5($_POST['file_path'] . $_POST['chunk'] . (int)$isLast . $salt); if ($hash != $_POST['hash']) { echo 0; exit; } $path = __DIR__ . '/received-files/' . $filePath; @mkdir(dirname($path), 0755, true); file_put_contents($path, $chunk, FILE_APPEND); if ($isLast) { $string = file_get_contents($path); // Тут указать где корень проекта file_put_contents(__DIR__ . '/../../' . $filePath, hex2bin($string)); @unlink($path); } echo 1; } catch(Throwadble $e){ file_put_contents('error.log', $e->getMessage() . PHP_EOL . PHP_EOL, FILE_APPEND); }
А теперь про получение файлов с сервера, т.к. иногда нужно было вытащить файл к себе. Команда отправляет путь до файла, отправитель получает этот путь и в ответ читает содержимое файла.
protected $signature = 'app:get-file {filepath} {filename}'; public function handle() { $salt = 'some_salt'; $name = $this->argument('filename'); $path = $this->argument('filepath'); $opts = [ 'http' => [ 'method' => "POST", 'header' => "Content-Type: application/x-www-form-urlencoded", 'content' => http_build_query([ 'path' => $path, 'hash' => md5($path . $salt), ]) ] ]; $context = stream_context_create($opts); $result = file_get_contents('https://host/reader.php', false, $context); if (!empty($result)) { file_put_contents($name, $result); } else { echo 'Failed to read from the file'; } }
Код отправителя, читает файл в stdout
<?php $salt = 'some_salt'; $path = $_POST["path"] ?? ''; $inputHash = $_POST['hash'] ?? ''; $hash = md5( $path . $salt); if ($hash != $inputHash) { echo 0; exit; } header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="'.basename($path).'"'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); header('Content-Length: ' . filesize($path)); readfile($path); exit;
Оставить комментарий