@Kerm

PHP, как с помощью curl передать файл на удаленный сервер, обязательно с чанками?

PHP 7.2

Не думал что с виду простейшая задача окажется на столько сложным делом и практически без примеров того как это реализовать в интернете, все что находил не работает...

Сейчас у меня файлы передаются одним куском с помощью такой конструкции:

$data = json_decode($data, true);
$stream = fopen(Cb::FILES_DIR . '/' . $taskId . '.' .$data['folder'] . '/' . $data['Name'], 'rb');
$curlOptions[CURLOPT_CONNECTTIMEOUT] = 2;
$curlOptions[CURLOPT_HEADER] = false;
$curlOptions[CURLOPT_BINARYTRANSFER] = 1;
$curlOptions[CURLOPT_URL] = $uri;
$curlOptions[CURLOPT_PUT] = true;
$curlOptions[CURLOPT_VERBOSE] = true;
$curl_log = fopen("curl.txt", 'a');
$curlOptions[CURLOPT_STDERR] = $curl_log;
$curlOptions[CURLOPT_INFILE] = $stream;
$curlOptions[CURLOPT_INFILESIZE] = filesize(Cb::FILES_DIR . '/' . $taskId . '.' .$data['folder'] . '/' . $data['Name']);


Но, файлы стали большими и сервис куда передаю, требует слать чанками файлы и что бы я не пытался сделать, не выходит.

Пример заголовка, который должен быть при отправке файлов чанками:

PUT https://portal5test.cbr.ru/back/rapi2/messages/c84bc346-d57c-482b-b19d-73234492034b/files/8dce3e13-ae01-4df0-ae69-87730a956bce HTTP/1.1
Accept: application/json, application/soap+xml
Authorization: Basic S0xETkVPTEFOVFxPU3ljaGV2YTo4ODkxZHJlYW1mYWxs
Content-Type: application/octet-stream
Content-Range: bytes 0-3207/3208
Content-Length: 3208


Пример моего кода отправки чанками, на который удаленный сервис возвращает ошибку 400:

$fragSize = 10485760;
        $fileSize = filesize(Cb::FILES_DIR . '/' . $taskId . '.' . $data['folder'] . '/' . $data['Name']);
        $numFragments = ceil($fileSize / $fragSize);
        $bytesRemaining = $fileSize;
        $i = 0;

        $ch = curl_init($uploadUrl);

        while ($i < $numFragments) {
            $chunkSize = $numBytes = $fragSize;
            $start = $i * $fragSize;
            $end = $i * $fragSize + $chunkSize - 1;
            $offset = $i * $fragSize;

            if ($bytesRemaining < $chunkSize) {
                $chunkSize = $numBytes = $bytesRemaining;
                $end = $fileSize - 1;
            }

            if ($stream = fopen(Cb::FILES_DIR . '/' . $taskId . '.' . $data['folder'] . '/' . $data['Name'], 'r')) {
                // get contents using offset
                $result = stream_get_contents($stream, $chunkSize, $offset);
                fclose($stream);
            }

            $headers['Accept'] = 'application/json, application/soap+xml';
            $headers['Content-Type'] = 'application/octet-stream';
            $headers['Content-Length'] = $numBytes;
            $headers['Content-Range'] = $content_range;

            curl_setopt($ch, CURLOPT_URL, $uploadUrl);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            //curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 2);
            //curl_setopt($ch, CURLOPT_HEADER, false);
            //curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);
            //curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
            //curl_setopt($ch, CURLOPT_PUT, true);
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $result);
            curl_setopt($ch, CURLOPT_VERBOSE, true);
            curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_USERPWD, $this->apiAuthLogin . ":" . $this->apiAuthPassword);

            $server_output = curl_exec($ch);
            $info = curl_getinfo($ch);

            $bytesRemaining = $bytesRemaining - $chunkSize;
            $i++;
        }
  • Вопрос задан
  • 113 просмотров
Пригласить эксперта
Ответы на вопрос 1
Compolomus
@Compolomus Куратор тега PHP
Комполом-быдлокодер
я бы попробовал поискать примеры работы курла с опциями
CURLOPT_HEADERFUNCTION
A callback accepting two parameters. The first is the cURL resource, the second is a string with the header data to be written. The header data must be written by this callback. Return the number of bytes written.

CURLOPT_READFUNCTION
A callback accepting three parameters. The first is the cURL resource, the second is a stream resource provided to cURL through the option CURLOPT_INFILE, and the third is the maximum amount of data to be read. The callback must return a string with a length equal or smaller than the amount of data requested, typically by reading it from the passed stream resource. It should return an empty string to signal EOF.

https://www.php.net/manual/en/function.curl-setopt.php

что то вроде

$accesstoken = 'secret';
//$data = json_decode($data, true);
$data = ['folder' => './test', 'Name' => '11.json'];
$taskId = 1;
$file = Cb::FILES_DIR . '/' . $taskId . '.' . $data['folder'] . '/' . $data['Name'];

$headers = [];
//$headers[] = 'Content-length: ' . filesize($file);
$headers[] = 'Content-type: application/json';
$headers[] = 'Authorization: Basic ' . $accesstoken;
$headers[] = 'Transfer-Encoding: chunked';

$stream = fopen($file, 'rb');
$log = fopen("curl.txt", 'a');

$url = 'https://portal5test.cbr.ru/back/rapi2/messages....';

$ch = curl_init($url);
//curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
//curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_PUT, true);
curl_setopt($ch, CURLOPT_UPLOAD, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_INFILE, $stream);
curl_setopt($ch, CURLOPT_INFILESIZE, filesize($file));
curl_setopt($ch, CURLOPT_VERBOSE, true);
curl_setopt($ch, CURLOPT_STDERR, $log);
curl_setopt($ch, CURLOPT_READFUNCTION, static function ($ch, $stream, $length) {
    return fgets($stream, $length);
});

$result = curl_exec($ch);
curl_close($ch);

Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы