我正在尝试显示存储在LONGBLOB中的PDF文件。当我执行代码时,我只会得到文件的名称。我想在查看器中显示PDF文件。
有人知道我该如何解决吗?
这是我的剧本:
<?php
$tpurchase_id = $_GET['tpurchase_id'];
$conn = new mysqli("localhost","user","","db");
$sql = "SELECT * FROM temp_purchase WHERE tpurchase_id= '$tpurchase_id'";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
header("Content-type:application/pdf");
$a=$row['content'];
echo '<object data="data:application/pdf;base64,';
echo base64_decode($a);
echo '" type="application/pdf" style="height:200px;width:60%"></object>';
}
}
$conn->close();
?>
答案 0 :(得分:2)
我认为这项艰辛的工作没有问题,提供了更多标题信息
header("Content-type: application/pdf");
header('Content-disposition: attachment; filename="thing.pdf"');
header("Content-Length: " . strlen($row['content']));
print $row['content'];
答案 1 :(得分:2)
某些操作改进和优化顺序。
假设数据库正确存储了LOB
数据的全部内容。
使用ob_start
,ob_clean
和ob_end_flush
进行输出缓冲将提供对脚本中所需响应内容的更多控制。这将有助于减轻错误的空间或发出的警告,避免其包含在二进制输出中。
此外,这使您可以控制在响应中发送哪些header
数据。
由于数据库的响应应该包含单个while($row = $result->fetch_assoc())
数据行的全部,因此无需使用LONGBLOB
。
使用mysqli_stmt::bind_result
和mysqli_stmt::fetch
将减少由于提取到关联数组而导致的一些开销,因为它仅需要检索内容。如果没有结果/数据,mysqli_stmt::fetch
将返回NULL
或错误时返回false
。
我还建议使用prepared statements来防止SQL注入,并建议使用filter_var
来确保用户提供的输入具有预期的数据类型并且有效。
如果浏览器可以理解content-disposition: inline
,则使用content-type
将要求浏览器尝试加载它,否则它将下载它。
最后,您不需要使用可能会导致意外行为的?>
结尾的代码,而只需使用exit;
。除非要从PHP转换到同一文件中的纯文本或标记,否则最好在PHP脚本文件中排除结束标记。
我在还使用LONGBLOB
存储PDF文件并可以正常运行的MySQL数据库表上测试了以下内容。
<?php /*line 1*/
ob_start(); //start output buffering immediately
$conn = new mysqli('localhost','user','','db');
if (mysqli_connect_errno()) {
exit;
}
$tpurchase_id = filter_var($_GET['tpurchase_id'], FILTER_VALIDATE_INT);
$stmt = $conn->prepare('SELECT tp.content
FROM temp_purchase AS tp
WHERE tp.tpurchase_id = ?
AND tp.content > ""'); //ensure content is not empty
if ($stmt && false !== $tpurchase_id) {
$stmt->bind_param('i', $tpurchase_id);
$stmt->execute();
$stmt->bind_result($content);
if ($stmt->fetch()) {
//a record was found, change to a PDF file
ob_clean(); //clear the buffer
header('content-type: application/pdf');
header('content-disposition: inline; filename="Test.pdf"');
echo $content;
ob_end_flush(); //output only the buffered content to the client
}
$stmt->close();
unset($content, $stmt);
}
$conn->close(); //always close the connection
while (ob_get_level() > 0) {
ob_end_clean(); //remove everything else from the buffer
}
exit;
这将仅将标头和内容响应发送到客户端,否则,如果找不到数据库的结果,则发送空白的纯文本/文本响应。
以上脚本可以用作内联对象的源。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
</head>
<body>
<object data="/path/to/above_script.php?tpurchase_id=123" type="application/pdf" style="height:200px;width:60%"></object>
</body>
</html>
除上述以外,还有其他一些可能导致问题的点,我们目前尚不了解。
LOB
数据。LOB
数据。要使用上述PHP脚本显示内联object
。无需输出缓冲。但是,您需要换出base64_decode
才能使用base64_encode
。解码采用base64编码的字符串,并将其转换为原始格式。您实际想要从数据库中获取二进制数据并将其转换为base64编码的字符串的位置,供浏览器稍后进行解码。如果文件内容已经由上传处理脚本base64_encode
处理过,则不需要base64_encode
或base64_decode
。
对下面的内容进行了测试,并且可以正常运行。
<?php /*line 1*/
$conn = new mysqli('localhost','user','','db');
if (mysqli_connect_errno()) {
exit;
}
$tpurchase_id = filter_var($_GET['tpurchase_id'], FILTER_VALIDATE_INT);
$stmt = $conn->prepare('SELECT tp.content
FROM temp_purchase AS tp
WHERE tp.tpurchase_id = ?
AND tp.content > ""'); //ensure content is not empty
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
</head>
<body>
<?php
if ($stmt && false !== $tpurchase_id) {
$stmt->bind_param('i', $tpurchase_id);
$stmt->execute();
$stmt->bind_result($content);
if ($stmt->fetch()) { ?>
<object data="data:application/pdf;base64,<?php echo base64_encode($content); ?>" type="application/pdf" style="height:200px;width:60%"></object>
<?php }
$stmt->close();
unset($content, $stmt);
}
$conn->close();
?>
</body>
</html>
出于检索多个文档的目的,您可以选择更改if ($stmt->fetch())
以便使用while($stmt->fetch())
上传处理器建议
假设您使用问题"Auto submit is not posting data to database"中的代码进行文件上传,我强烈建议您使用当前的标准/最佳做法重写上传处理器,这也将使您的上传处理器与此兼容回答。
使用addslashes
或其他转义技术可能会导致存储在数据库中的LOB
数据产生问题。我想这是您现在遇到的并发症的原因。
您还应该考虑PHP和数据库环境使用的最大数据包大小,它限制了应用程序可以发送或接收的数据大小,这可能导致LOB
数据被截断。由于数据包大小的限制,建议您使用send_long_data
来防止应用程序传输LOB
数据时出现问题。
upload-form.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
</head>
<body>
<form id="target" method="post" enctype="multipart/form-data" name="frmImage" class="frmImageUpload" action="./post.php">
<input type="file" name="userfile" id="userfile" class="userfile"/>
</form>
</body>
</html>
post.php
<?php
$conn = new mysqli('localhost','user','','db');
if (mysqli_connect_errno()) {
exit;
}
if (!session_id()) {
session_start();
}
//never trust data from GLOBALS
$user_id = filter_var($_SESSION['user_id'], FILTER_VALIDATE_INT);
if (false === $user_id ||
!isset($_FILES) ||
!array_key_exists('userfile', $_FILES) ||
UPLOAD_ERR_OK !== $_FILES['userfile']['error'] ||
$_FILES['userfile']['size'] <= 0 ||
!is_uploaded_file($_FILES['userfile']['tmp_name'])
) {
//invalid user or file upload
exit;
}
//params = { 0: user_id, 1: content }
$stmt = $conn->prepare('INSERT INTO temp (user_id, content) VALUES (?, ?)');
if ($stmt) {
//bind default value as NULL
$null = null;
$stmt->bind_param('ib', $user_id, $null);
//max packet size limits can lead to partial file data being inserted
$fp = new SplFileObject($_FILES['userfile']['tmp_name'], 'rb', false);
while (!$fp->eof()) {
//use send_long_data to send the file data in chunks
//be sure the first argument matches the param index for the LOB data column
$stmt->send_long_data(1, $fp->fread(2048));
}
unset($fp);
$stmt->execute();
$stmt->close();
}
$conn->close();
作为个人推荐;多年来,我发现在数据库中存储LOB
数据已引起一些严重的问题。虽然它确实增加了应用程序内的可移植性并简化了文件管理。通过大大增加恢复数据库和硬盘RAID完整性所需的I / O时间,它极大地阻碍了数据的恢复和备份。同样,当与其他数据一起使用时,将大大增加数据库的查询和维护时间。迫使我们从SELECT *
迁移到显式地避免使用LOB
列数据,或者跳过表进行优化或重新编制索引。最后,它还阻止了客户端缓存,而没有创建用于服务文件的特定RESTful URL。总体而言,这变得比花费LOB
数据值得花费的麻烦要多得多。我建议使用您的Web服务器存储物理文件,并使用数据库存储物理文件的相对路径,其中PHP管理物理文件的绝对路径以供查看/操作。例如,当创建可从公共位置缓存和提供缩略图的缩略图时。
答案 2 :(得分:0)
如果要使用浏览器PDF查看器,请注意,一次只能查看一个PDF。
您的代码如下:
<?php
header("Content-type:application/pdf");
$tpurchase_id = $_GET['tpurchase_id'];
$conn = new mysqli("localhost","user","","db");
$sql = "SELECT * FROM temp_purchase WHERE tpurchase_id= '$tpurchase_id'";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc())
ob_clean();
flush();
echo $row['content'];
$conn->close();
exit();
}
}
?>
答案 3 :(得分:0)
这是一个解决方案:它得到编码 LONGBLOB并解码,然后在设置标题后将其显示。它基于fyrye的答案。
<?php
$tpurchase_id = $_GET['tpurchase_id'];
$connection = new mysqli("localhost","user","","db");
$sql = "SELECT content FROM temp_purchase WHERE tpurchase_id = ?";
$statement = $connection->prepare($sql);
$statement->bind_param("s", $tpurchase_id);
$statement->execute();
$statement->bind_result($pdf_encoded);
if($statement->fetch()){
$pdf_decoded = base64_decode($pdf_encoded);
}
$statement->close();
ob_start();
ob_clean(); //Clear the buffer
header('content-type: application/pdf');
echo($pdf_decoded);
ob_end_flush(); //Output only the buffered content to the client
?>