При загрузке бинарного файла с помощью fwrite - пропадают байты 0D


Я тестирую этот код для загрузки большие файлы кусками по 10 МБ:

/**
 * Copy remote file over HTTP one small chunk at a time.
 *
 * @param $infile The full URL to the remote file
 * @param $outfile The path where to save the file
 */
function copyfile_chunked($infile, $outfile) {
    $chunksize = 10 * (1024 * 1024); // 10 Megs

    /**
     * parse_url breaks a part a URL into it's parts, i.e. host, path,
     * query string, etc.
     */
    $parts = parse_url($infile);
    $i_handle = fsockopen($parts['host'], 80, $errstr, $errcode, 5);
    $o_handle = fopen($outfile, 'wb');

    if ($i_handle == false || $o_handle == false) {
        return false;
    }

    if (!empty($parts['query'])) {
        $parts['path'] .= '?' . $parts['query'];
    }

    /**
     * Send the request to the server for the file
     */
    $request = "GET {$parts['path']} HTTP/1.1\r\n";
    $request .= "Host: {$parts['host']}\r\n";
    $request .= "User-Agent: Mozilla/5.0\r\n";
    $request .= "Keep-Alive: 115\r\n";
    $request .= "Connection: keep-alive\r\n\r\n";
    fwrite($i_handle, $request);

    /**
     * Now read the headers from the remote server. We'll need
     * to get the content length.
     */
    $headers = array();
    while(!feof($i_handle)) {
        $line = fgets($i_handle);
        if ($line == "\r\n") break;
        $headers[] = $line;
    }

    /**
     * Look for the Content-Length header, and get the size
     * of the remote file.
     */
    $length = 0;
    foreach($headers as $header) {
        if (stripos($header, 'Content-Length:') === 0) {
            $length = (int)str_replace('Content-Length: ', '', $header);
            break;
        }
    }

    /**
     * Start reading in the remote file, and writing it to the
     * local file one chunk at a time.
     */
    $cnt = 0;
    while(!feof($i_handle)) {
        $buf = '';
        $buf = fread($i_handle, $chunksize);
        $bytes = fwrite($o_handle, $buf);
        if ($bytes == false) {
            return false;
        }
        $cnt += $bytes;

        /**
         * We're done reading when we've reached the conent length
         */
        if ($cnt >= $length) break;
    }

    fclose($i_handle);
    fclose($o_handle);
    return $cnt;
}

Сначала я тестирую этот код на небольшом изображении. Изображение загружается в мою учетную запись, но в искаженном виде: все байты кажутся правильными, за исключением того, что из загруженного изображения удаляются байты «0D», что делает его непригодным для использования.
Почему это происходит и как с этим бороться?
Спасибо!


person GreenBear    schedule 05.11.2012    source источник
comment
Вы в конечном итоге читаете столько байтов, сколько указывает Content-Length?   -  person Jon    schedule 06.11.2012
comment
Привет, Джон. Странно с этим. Исходное изображение имеет размер 15444 байта, функция возвращает, что было загружено 15444 байта, но когда я извлекаю загруженное изображение, оказывается, что оно составляет всего 15397 байт, вероятно, из-за отсутствующих байтов 0D.   -  person GreenBear    schedule 06.11.2012
comment
Есть ли особая причина, по которой вы не используете работающую клиентскую библиотеку http? Ваше дурацкое декодирование заголовка не совместимо с HTTP. И не все ответы приходят в разбивке на ТЭ.   -  person mario    schedule 06.11.2012
comment
Неправильно настроенный сервер? Может быть? Отправляет ли сервер правильный Content-Type для изображения?   -  person Jon    schedule 06.11.2012
comment
Я только что удалил с помощью Hex-редактора все байты 0D из исходного изображения. Образ стал размером 15399 байт, так что я предполагаю, что еще какие-то два байта удаляются при загрузке.   -  person GreenBear    schedule 06.11.2012
comment
Привет, Марио. Я использую бесплатный хостинг, и в файле php.ini установлен лимит загрузки, который я не могу изменить, но пытаюсь обойти. Вы знаете какой-нибудь другой способ сделать это?   -  person GreenBear    schedule 06.11.2012
comment
Да, Джон, Content-Type для изображения, возвращаемый сервером, на котором хранится изображение, — image/jpeg.   -  person GreenBear    schedule 06.11.2012
comment
Последние байты полностью идентичны в исходном и загруженном файлах, по крайней мере, насколько я проверял.   -  person GreenBear    schedule 06.11.2012
comment
Кажется, все правильно, если установить, что данные должны интерпретироваться как двоичные. Еще одна вещь, которая кажется мне странной, это использование такого большого размера «куска». Это маловероятно, но что произойдет, если вы используете 2 или 4 КБ?   -  person Sammitch    schedule 06.11.2012


Ответы (1)


Всем доброго времени суток и спасибо за помощь.
Теперь проблема решена, виновник установлен.
Я читал несколько книг и нашел следующее:
ftp_get() копирует файл с удаленного сервера на ваш компьютер. Параметр FTP_ASCII передает файл, как если бы это был текст ASCII. В этом случае окончания перевода строки автоматически преобразуются при переходе с одной операционной системы на другую. Другой вариант — FTP_BINARY, который используется для файлов, отличных от обычного текста, поэтому преобразование перевода строки не происходит.
Код, приведенный в моем вопросе, работает нормально и загружает изображение правильно.
Когда я проверял изображение, я загружал его на свой компьютер с помощью файлового менеджера, написанного на php, который был предоставлен хостинг-провайдерами. Которые, видимо, не очень хорошо разбираются в PHP, так как использовали упомянутый выше параметр FTP_ASCII для передачи бинарного файла. Таким образом, изображение испортилось.
Когда я загрузил изображение напрямую с FTP-аккаунта, оно оказалось идентичным оригиналу.
Итак, в конечном счете, проблема была в PHP-коде, а не в коде, который я скомпилировал.

person GreenBear    schedule 09.11.2012