如果您看到此问题的标题,则几乎没有添加内容。
我有一个从MySQL表中检索单行的查询,我对特定列感兴趣,这是一个BLOB。我希望PHP将它写入输出缓冲区,而不是将~500 KB存储到一个字符串中(,而且我不确定它是二进制安全的)。
PDOStatement功能如:
string PDOStatement::fetchColumn ([ int $column_number = 0 ] )
不要帮助我。
你能帮助至少给我一个方向吗?提前谢谢。
P.S。:我知道在DB表中存储~500 KB的东西并不好,但这不是我的选择,我只需要坚持下去。
答案 0 :(得分:2)
见this page。这会将数据加载到流中,然后可以与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中使用它,测试和工作的方式;它包括许多额外的缓存处理,可以加速你的客户端,只需要你跟踪blob的Last-Modified时间:
<?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
,但这也会阻止客户端直接在浏览器中查看图像(尽管这可能是一件好事,具体取决于您的情况)。
答案 1 :(得分:2)
我坚信使用Doctrine进行批处理或使用MySQL(PDO或mysqli)进行任何迭代都只是一种错觉。
@ dimitri-k提供了一个很好的解释,特别是关于工作单元。问题是导致错过:&#34; $ query-&gt; iterate()&#34;它并没有真正迭代数据源。已经完全获取数据源的只是一个\ Traversable wrapper 。
一个例子表明即使从图片中完全删除Doctrine抽象层,我们仍会遇到内存问题:
echo 'Starting with memory usage: ' . memory_get_usage(true) / 1024 / 1024 . " MB \n";
$pdo = new \PDO("mysql:dbname=DBNAME;host=HOST", "USER", "PW");
$stmt = $pdo->prepare('SELECT * FROM my_big_table LIMIT 100000');
$stmt->execute();
while ($rawCampaign = $stmt->fetch()) {
// echo $rawCampaign['id'] . "\n";
}
echo 'Ending with memory usage: ' . memory_get_usage(true) / 1024 / 1024 . " MB \n";
<强>输出:强>
Starting with memory usage: 6 MB
Ending with memory usage: 109.46875 MB
这里,令人失望的 getIterator()方法:
namespace Doctrine\DBAL\Driver\Mysqli\MysqliStatement
/**
* {@inheritdoc}
*/
public function getIterator()
{
$data = $this->fetchAll();
return new \ArrayIterator($data);
}
您可以使用我的小库来实际使用PHP Doctrine或DQL或纯SQL来传输繁重的表。但是你找到了合适的:https://github.com/EnchanterIO/remote-collection-stream
答案 2 :(得分:-4)
看看下面的php函数。
ob_start()
ob_end_clean()
和
ob_end_flush()