См. эту страницу . Это загружает данные в поток, который затем можно использовать с функциями f *, включая вывод непосредственно в браузер с помощью fpassthru. Вот пример кода с этой страницы:
<?php
$db = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2');
$stmt = $db->prepare("select contenttype, imagedata from images where id=?");
$stmt->execute(array($_GET['id']));
$stmt->bindColumn(1, $type, PDO::PARAM_STR, 256);
$stmt->bindColumn(2, $lob, PDO::PARAM_LOB);
$stmt->fetch(PDO::FETCH_BOUND);
header("Content-Type: $type");
fpassthru($lob);
?>
Ключевым моментом здесь является то, что после $stmt->execute()
вы вызываете $stmt->bindColumn('columnName', $stream, PDO::PARAM_LOB);
, затем вызываете $stmt->fetch(PDO::FETCH_BOUND)
, чтобы получить строку (где значения хранятся в связанных переменных PHP). Вот как я использовал его в Drupal, протестировал и работал; он включает в себя много дополнительной обработки кэша, которая должна ускорить работу ваших клиентов и требует только отслеживания времени последнего изменения ваших больших двоичных объектов:
<?php
$rfc2822_format = 'D, d M Y H:i:s e';
// This is basically the Drupal 7 way to create and execute a prepared
// statement; the `->execute()` statement returns a PDO::Statement object.
// This is the equivalent SQL:
// SELECT f.fileType,f.fileSize,f.fileData,f.lastModified
// FROM mfiles AS f WHERE fileID=:fileID
// (with :fileID = $fileID)
$statement = db_select('mfiles', 'f')
->fields('f', array('fileType', 'fileSize', 'fileData', 'lastModified'))
->condition('fileID', $fileID, '=')
->execute();
// All of the fields need to be bound to PHP variables with this style.
$statement->bindColumn('fileType', $fileType, PDO::PARAM_STR, 255);
$statement->bindColumn('fileSize', $fileSize, PDO::PARAM_INT);
$statement->bindColumn('fileData', $fileData, PDO::PARAM_LOB);
$statement->bindColumn('lastModified', $lastModified, PDO::PARAM_STR, 19);
$success = false;
// If the row was fetched successfully...
if ($statement->fetch(PDO::FETCH_BOUND)) {
// Allow [public] caching, but force all requests to ask the server if
// it's been modified before serving a cache [no-cache].
header('Cache-Control: public no-cache');
// Format the Last-Modified time according to RFC 2822 and send the
// Last-Modified HTTP header to aid in caching.
$lastModified_datetime = DateTime::createFromFormat('Y-m-d H:i:s',
$lastModified, new DateTimeZone('UTC'));
$lastModified_formatted = $lastModified_datetime->format($rfc2822_format);
header('Last-Modified: ' . $lastModified_formatted);
// If the client requested If-Modified-Since, and the specified date/time
// is *after* $datetime (the Last-Modified date/time of the API call), give
// a HTTP/1.1 304 Not Modified response and exit (do not output the rest of
// the page).
if (array_key_exists('HTTP_IF_MODIFIED_SINCE', $_SERVER)) {
// Ignore anything after a semicolon (old browsers sometimes added stuff
// to this request after a semicolon).
$p = explode(';', $_SERVER['HTTP_IF_MODIFIED_SINCE'], 2);
// Parse the RFC 2822-formatted date.
$since = DateTime::createFromFormat($rfc2822_format, $p[0]);
if ($lastModified_datetime <= $since) {
header('HTTP/1.1 304 Not Modified');
exit;
}
}
// Create an ETag from the hash of it and the Last-Modified time, and send
// it in an HTTP header to aid in caching.
$etag = md5($lastModified_formatted . 'mfile:' . $fileID);
header('ETag: "' . $etag . '"');
// If the client requested If-None-Match, and the specified ETag is the
// same as the hashed ETag, give a HTTP/1.1 304 Not Modified response and
// exit (do not output the rest of the page).
if (array_key_exists('HTTP_IF_NONE_MATCH', $_SERVER) && $etag ==
str_replace('"', '', stripslashes($_SERVER['HTTP_IF_NONE_MATCH']))) {
header('HTTP/1.1 304 Not Modified');
exit;
}
// Set the content type so that Apache or whatever doesn't send it as
// text/html.
header('Content-Type: ' . $fileType);
// Set the content length so that download dialogs can estimate how long it
// will take to load the file.
header('Content-Length: ' . $fileSize);
// According to some comments on the linked page, PDO::PARAM_LOB might
// create a string instead of a stream.
if (is_string($fileData)) {
echo $fileData;
$success = true;
} else {
$success = (fpassthru($fileData) !== false);
}
}
?>
В сторону: если вам нужно указать имя файла, быстрое и грязное решение - добавить имя файла к фактическому URL, ссылающемуся на файл; для http://example.com/fileurl.php
используйте http://example.com/fileurl.php/filename.jpg
. Это может не сработать, если что-то уже интерпретирует информацию о пути (например, Drupal); «лучшее» решение - отправить заголовок Content-Disposition: attachment; filename=filename.jpg
, но это также не позволяет клиентам просматривать изображение непосредственно в браузере (хотя это может быть полезно в зависимости от вашей ситуации).