Лишний байт в конце файла, прочитанного из базы, - откуда???

GRIG

Новичок
Привет всем. Прошу помощь зала по следующей ситуации.
Есть база данных Oracle. В ней есть некая таблица. В этой таблице в одной из колонок типа BLOB хранятся файлы, загруженные откуда-то извне. Файлы могут быть разных типов - картинки, PDF, DOC, DOCX, XLS, XLSX, ... - заранее неочевидно, в какой записи какой файл окажется.
Пользователь через броузер смотрит запись, к которой прикреплен файл, и тыкает на кнопку "скачать файл". Моя задача - выдать ему файл, чтобы пользователь сохранил его у себя для дальнейшей работы с ним.

Действую следующим образом (для простоты все обработки ошибок и несущественные подробности урезаны):



PHP:
  $sql = "SELECT TEMPLATE, FILE_NAME, FILE_TYPE FROM ... WHERE ...";
  $desc = oci_new_descriptor( $conn, OCI_DTYPE_LOB );
  $stmt = oci_parse( $conn, $sql );
  oci_define_by_name( $stmt, "TEMPLATE", $desc, SQLT_BLOB );
  oci_execute( $stmt, OCI_DEFAULT );
  $image = oci_fetch_assoc( $stmt );
  if( $image !== false )
  {
    header( "Content-type: " . $image['FILE_TYPE'] );
    header( "Content-disposition: attachment; filename=" . str_replace( ",", "_", $image['FILE_NAME'] ) );
    $file_content = $image['TEMPLATE']->load();
/* (1) */
    echo $file_content;
  }
  else
    echo "Ошибка чтения";
Запрос обрабатывается без ошибок, файл пользователю приходит. Однако есть проблема: в результате этого кода файл на компьютере пользователя оказывается на 1 байт длиннее оригинального файла, хранившегося в базе. Причем независимо от того, какого типа был переданный файл и какой длины, этот лишний байт всегда один и тот же - 0x0A (перевод строки). В некоторых случаях этим лишним байтом можно пренебречь. Но в других случаях программы просмотра соответствующих файлов считают этот лишний байт нарушением формата файла и ругаются.

Соответственно вопрос: откуда этот лишний байт может взяться? И как сделать так, чтобы его не было?

База данных вне подозрений: если тот же самый файл выгрузить из базы собственными средствами Oracle (например, Oracle SQL*Developer), то файл сохраняется нормально без всяких лишних байтов.
Библиотека доступа к базе данных тоже работает честно: если в тот же код в точку (1) вставить печать длины прочитанного файла - то выдается правильная длина без лишнего байта.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
А код, выполняемый по вызову $image['TEMPLATE']->load(), где $image является ассоциативным массивом типа ключ-строка, ага - это те самые несущественные подробности, как я понимаю. Happy debugging :)
 

GRIG

Новичок
Нет. Это не несущественные подробности - это библиотечная функция. Подробнее - см. https://www.php.net/manual/en/ocilob.load.php
Кроме того, после ее вызова strlen( $file_content ) возвращает в точности длину файла, без лишнего байта
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
после echo $file_content; добавь exit(); и проверь

вероятно, у тебя стандартная ошибка начинающих разработчиков - писать в конце файла закрывающий тег ?>, а редактор кода автоматически добавил перенос строки, который нужен в коде на C
 

GRIG

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

Valick

Новичок
Что бы было понятно необходимо читать стандарты (в данном случаеPSR-2)
2. Общее
2.1 Основной стандарт написания кода
Код ДОЛЖЕН следовать всем правилам изложенным в PSR-1.

2.2 Файлы
Все PHP файлы ДОЛЖНЫ использовать переводы строк Unix LF (linefeed).

Все PHP файлы ДОЛЖНЫ оканчиваться одной пустой строкой.

Закрывающий ?> тег ДОЛЖЕН быть исключен из файлов содержащих только PHP.
 

Тугай

Новичок
Спасибо, это помогло.
Только непонятно, почему это считается ошибкой. По идее, все теги должны быть парными.
это не ошибка, а php так работает, все что вне тегов <?php ... ?> отдается в stdout. Вне тегов обычно html, а у тебя лишний байт,
так что exit() то что надо.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
exit() то что надо.
не совсем, надо убрать закрывающие теги, чтобы не было лишнего байта,
а exit нужен только для дебага - в большинстве приложений на фреймвоках в конце работы после вывода пользователю идет запись логов или еще какие-то служебные операции, если писать exit - они не будут отрабатывать
 

weregod

unserializer
Закрывашки выпилили, ибо пошла мода на вирусню, которая по FTP к закрывашкам всякую хрень дописывала.
А логи и бенчи (если с фаталом не развалилось) пишутся тут PHP: register_shutdown_function - Manual
 
Сверху