wkhtmltopdf 无需在 php 中创建文件
wkhtmltopdf without creating a file in php
我的 Drupal 中有 wkhtmltopdf 模块,它使用 shell_exec
函数通过 运行 'wkhtmltopdf --options URL filename.pdf'
命令生成 pdf 文件。
文件的输出没问题,但我不想将pdf存储在文件系统中。我只是想在浏览器上显示输出,以便用户可以选择是否下载它。
就我的搜索而言,我找不到一种方法来获取缓冲区中的输出而不是将其存储在 pdf 文件中。是否可以在不在 wkhtmltopdf 中创建文件的情况下生成 pdf?
GIF Demonstration (Over-engineered)
这是我专门为您编写的一段过度设计的代码:)
它包括从功能到您可以测试的演示形式的所有内容。
我不保证此代码的稳定性,您可以随意查看
并修改它供您自己使用,但我不能保证 100% 的稳定性或安全性。
阅读有关 shell_exec 等功能的文档,以及为什么由于存在潜在的安全风险,这是一种不好的做法。
我的建议是用 C++ 编写一个 PHP 库并加载并使用它
在 PHP.
我不确定 wkhtmltopdf 是否存在,如果我错了,评论中的人会纠正我。
更新 1
我在 http://ifconfig.me 上测试了这个脚本,它 returns 是一个格式错误的 PDF 文档。
所以你可能有 3 个选择,或者用 C++ 编写一个 PHP 库,等待有人提出更好的解决方案,或者只是将文件下载到 /tmp
并使用 PHP 读取文件] 然后删除它。
GIF Demonstration (Simple)
代码(简单)
<?php
/**
* --- DO NOT REMOVE THIS DOCBLOCK ---
* @WebCrawlTrackingId cf9e8c67.3cb7269c.60b1d84b.5b2e5450
*/
/**
* @file
* Code for ni_wkhtmltopdf_simple function.
* Includes a demonstration at the end.
*/
/**
* Function that saves a PDF file
* to a temporary directory and
* returns it.
* All of this by using wkhtmltopdf.
*
* @author t3ap0t@whosebug.com
*
* @param string $url
* URL to convert
*
* @param string $download
* Decide whether to download the
* file by specifying a filename
* or don't specify anything to
* display it in the browsers
* built-in PDF viewer.
*
* @return int|file
* Return (int) -1 if URL is empty
* Return (int) -2 if URL is not a string
* Return (int) -3 if URL is not a URL
*/
function ni_wkhtmltopdf_simple($url = "", $download = false) {
// URL can't be empty
if ($url == "") {
return -1;
}
// URL must be a string
if (gettype($url) !== "string") {
return -2;
}
// Remove whitespace
$url = trim($url);
// Explode URL by ':' to Array
$urla = explode(":", $url, 2);
// URL must be an actual URL
if (strtolower(substr($urla[0], 0, 4)) !== "http" || substr($urla[1], 0, 2) !== "//") {
return -3;
}
// Escape Shell Arguments
$url = escapeshellarg($url);
// Random file name
$fname = "/tmp/" . bin2hex(random_bytes(10)) . ".pdf";
// Generate a PDF file
shell_exec("wkhtmltopdf \"$url\" \"$fname\"");
// Load file
$buffer = file_get_contents("$fname");
// Delete the file after loading
unlink("$fname");
$buffsz = strlen($buffer);
// Prepare headers
header("Content-Type:application/pdf");
if ($download) {
$download = trim($download);
header("Content-Disposition:attachment;filename=\"$download\"");
} else {
header("Content-Disposition:inline");
}
header("Content-Length:" . $buffsz);
exit($buffer);
}
// Demonstrate ni_wkhtmltopdf_simple
// Are we getting the URL parameter?
if (isset($_GET["url"])) {
// Convert array to string
if (is_array($_GET["url"])) {
$_GET["url"] = $_GET["url"][0];
}
// Remove whitespace
$url = trim($_GET["url"]);
// URL is empty so unset it
if ($url == "") {
unset($_GET["url"], $url);
header("Location:" . basename(__FILE__));
}
// Get PDF output
if (isset($url)) {
ni_wkhtmltopdf_simple($url);
}
} else {
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<title>PHP wkhtmltopdf_simple Demo (t3ap0t@whosebug.com)</title>
<style>
*{outline:0}
html,body{
zoom:1.25
}
</style>
</head>
<body>
<form action="<?= basename(__FILE__) ?>" method="GET">
<label for="url">URL:</label>
<input id="url" name="url" type="text" value="https://" minlength="8" required autofocus />
<button id="btn" type="submit">Generate PDF</button>
<script type="text/javascript">
function urlhandler(e) {
// URL Value must begin with https://
if (url.value.trim() == "") {
url.value = "https://" + url.value;
}
// Prevent removal of https://
if (e.keyCode == 8 && url.value == "https://") {
e.preventDefault();
}
// Prevent Delete key
if (e.keyCode == 46) {
e.preventDefault();
}
// Add https:// if it was removed during Paste operation
if (url.value.substr(0, 8).toLowerCase() !== "https://") {
url.value = "https://" + url.value;
}
}
function btnhandler(e) {
if (url.value.substr(0, 8).toLowerCase() !== "https://") {
url.value = "https://" + url.value;
}
// Prevent submission of the form
e.preventDefault();
// Make sure we've provided a URL
if (8 >= url.value.trim().length ||
url.value.trim()[9] == ".") {
alert("You must provide a URL.");
return;
}
// Automatically guess top-level domain
if (url.value.trim().substr(-4, 1) !== "." &&
url.value.trim().substr(-3, 1) !== ".") {
url.value += ".com";
}
url.parentNode.submit();
}
// Event listeners
url.addEventListener("keydown", function(e) {
urlhandler(e);
});
url.addEventListener("onpaste", function(e) {
urlhandler(e);
});
btn.addEventListener("click", function(e) {
btnhandler(e);
});
</script>
</form>
</body>
<?php
}
?>
代码(过度设计)
<?php
/**
* --- DO NOT REMOVE THIS DOCBLOCK ---
* @WebCrawlTrackingId fcc5094e.ccc3a1df.5eb4dbfa.6c3772e1
*/
/**
* @file
* Code for ni_wkhtmltopdf function.
* Includes a demonstration at the end.
*/
/**
* Function that returns a PDF file
* from a URL using wkhtmltopdf.
*
* @author t3ap0t@whosebug.com
*
* @param string $url
* URL to convert
*
* @param string $https
* Ensures we're giving it HTTPS
*
* @param string $download
* Decide whether to download the
* file by specifying a filename
* or don't specify anything to
* display it in the browsers
* built-in PDF viewer.
*
* @param string $checkcmd
* Ensure we have all commands
* required to fulfil the operation.
*
* * On Windows hosts these commands
* can be acquired on using `scoop`.
*
* @param string $checkos
* Make sure we're running Linux.
*
* * Optional if we have both commands
* available on a Windows host.
*
*
* @return int|file
* Return (int) -1 if URL is empty
* Return (int) -2 if URL is not a string
* Return (int) -3 if URL is not a URL
* Return (int) -4 if protocol is not HTTPS
* Return (int) -5 if OS is not Linux
* Return (int) -6 if command wkhtmltopdf not found
* Return (int) -7 if command cat not found
* Return (int) -8 wkhtmltopdf returned nothing
*/
function ni_wkhtmltopdf($url = "", $https = false, $download = false, $checkcmd = true, $checkos = false) {
// URL can't be empty
if ($url == "") {
return -1;
}
// URL must be a string
if (gettype($url) !== "string") {
return -2;
}
// Remove whitespace
$url = trim($url);
// Explode URL by ':' to Array
$urla = explode(":", $url, 2);
// URL must be an actual URL
if (strtolower(substr($urla[0], 0, 4)) !== "http" || substr($urla[1], 0, 2) !== "//") {
return -3;
}
// Optional: Make sure the URL is HTTPS (Secure)
if ($https && strtolower(substr($url, 0, 8)) !== "https://") {
return -4;
}
// Optional: Check operating system
if ($checkos && strtolower(PHP_OS) !== "linux") {
return -5;
}
// Optional: (Linux) Make sure the `wkhtmltopdf` command exists
if ($checkcmd && !(`which wkhtmltopdf` > 0)) {
return -6;
}
// Optional: (Linux) Make sure the `cat` command exists
if ($checkcmd && !(`which cat` > 0)) {
return -7;
}
// Clear URL to (hopefully) prevent RCE
$rep = array(
" " => "%20",
"%20%20" => "",
"`" => "%60",
";" => "%3B",
":" => "%3A",
">" => "%3E",
"<" => "%3C",
"[" => "%5B",
"]" => "%5D",
"{" => "%7B",
"}" => "%7D",
"(" => "%28",
")" => "%29",
"|" => "%7C",
"$" => "%24",
"&&" => "%26%26",
'"' => "%22",
"\" => "%5C"
);
// Replace $a with $b inside URL
foreach ($rep as $a => $b) {
$url = str_replace($a, $b, $url);
}
unset($rep);
// Generate a PDF file
exec("wkhtmltopdf \"$url\" - | cat", $buffer);
$buffer = implode("\n", $buffer);
$buffsz = strlen($buffer);
// Is buffer empty?
if (0 >= $buffsz) {
return -8;
}
// Prepare headers
header("Content-Type:application/pdf");
if ($download) {
$download = trim($download);
header("Content-Disposition:attachment;filename=\"$download\"");
} else {
header("Content-Disposition:inline");
}
header("Content-Length:" . $buffsz);
exit($buffer);
}
// Demonstrate ni_wkhtmltopdf
// Are we getting the URL parameter?
if (isset($_GET["url"])) {
// Convert array to string
if (is_array($_GET["url"])) {
$_GET["url"] = $_GET["url"][0];
}
// Remove whitespace
$url = trim($_GET["url"]);
// URL is empty so unset it
if ($url == "") {
unset($_GET["url"], $url);
header("Location:" . basename(__FILE__));
}
// Get PDF output
if (isset($url)) {
ni_wkhtmltopdf($url);
}
} else {
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<title>PHP wkhtmltopdf Demo (t3ap0t@whosebug.com)</title>
<style>
*{outline:0}
html,body{
zoom:1.25
}
</style>
</head>
<body>
<form action="<?= basename(__FILE__) ?>" method="GET">
<label for="url">URL:</label>
<input id="url" name="url" type="text" value="https://" minlength="8" required autofocus />
<button id="btn" type="submit">Generate PDF</button>
<script type="text/javascript">
function urlhandler(e) {
// URL Value must begin with https://
if (url.value.trim() == "") {
url.value = "https://" + url.value;
}
// Prevent removal of https://
if (e.keyCode == 8 && url.value == "https://") {
e.preventDefault();
}
// Prevent Delete key
if (e.keyCode == 46) {
e.preventDefault();
}
// Add https:// if it was removed during Paste operation
if (url.value.substr(0, 8).toLowerCase() !== "https://") {
url.value = "https://" + url.value;
}
}
function btnhandler(e) {
if (url.value.substr(0, 8).toLowerCase() !== "https://") {
url.value = "https://" + url.value;
}
// Prevent submission of the form
e.preventDefault();
// Make sure we've provided a URL
if (8 >= url.value.trim().length ||
url.value.trim()[9] == ".") {
alert("You must provide a URL.");
return;
}
// Automatically guess top-level domain
if (url.value.trim().substr(-4, 1) !== "." &&
url.value.trim().substr(-3, 1) !== ".") {
url.value += ".com";
}
url.parentNode.submit();
}
// Event listeners
url.addEventListener("keydown", function(e) {
urlhandler(e);
});
url.addEventListener("onpaste", function(e) {
urlhandler(e);
});
btn.addEventListener("click", function(e) {
btnhandler(e);
});
</script>
</form>
</body>
<?php
}
?>
我的 Drupal 中有 wkhtmltopdf 模块,它使用 shell_exec
函数通过 运行 'wkhtmltopdf --options URL filename.pdf'
命令生成 pdf 文件。
文件的输出没问题,但我不想将pdf存储在文件系统中。我只是想在浏览器上显示输出,以便用户可以选择是否下载它。
就我的搜索而言,我找不到一种方法来获取缓冲区中的输出而不是将其存储在 pdf 文件中。是否可以在不在 wkhtmltopdf 中创建文件的情况下生成 pdf?
GIF Demonstration (Over-engineered)
这是我专门为您编写的一段过度设计的代码:)
它包括从功能到您可以测试的演示形式的所有内容。
我不保证此代码的稳定性,您可以随意查看 并修改它供您自己使用,但我不能保证 100% 的稳定性或安全性。
阅读有关 shell_exec 等功能的文档,以及为什么由于存在潜在的安全风险,这是一种不好的做法。
我的建议是用 C++ 编写一个 PHP 库并加载并使用它 在 PHP.
我不确定 wkhtmltopdf 是否存在,如果我错了,评论中的人会纠正我。
更新 1
我在 http://ifconfig.me 上测试了这个脚本,它 returns 是一个格式错误的 PDF 文档。
所以你可能有 3 个选择,或者用 C++ 编写一个 PHP 库,等待有人提出更好的解决方案,或者只是将文件下载到 /tmp
并使用 PHP 读取文件] 然后删除它。
GIF Demonstration (Simple)
代码(简单)
<?php
/**
* --- DO NOT REMOVE THIS DOCBLOCK ---
* @WebCrawlTrackingId cf9e8c67.3cb7269c.60b1d84b.5b2e5450
*/
/**
* @file
* Code for ni_wkhtmltopdf_simple function.
* Includes a demonstration at the end.
*/
/**
* Function that saves a PDF file
* to a temporary directory and
* returns it.
* All of this by using wkhtmltopdf.
*
* @author t3ap0t@whosebug.com
*
* @param string $url
* URL to convert
*
* @param string $download
* Decide whether to download the
* file by specifying a filename
* or don't specify anything to
* display it in the browsers
* built-in PDF viewer.
*
* @return int|file
* Return (int) -1 if URL is empty
* Return (int) -2 if URL is not a string
* Return (int) -3 if URL is not a URL
*/
function ni_wkhtmltopdf_simple($url = "", $download = false) {
// URL can't be empty
if ($url == "") {
return -1;
}
// URL must be a string
if (gettype($url) !== "string") {
return -2;
}
// Remove whitespace
$url = trim($url);
// Explode URL by ':' to Array
$urla = explode(":", $url, 2);
// URL must be an actual URL
if (strtolower(substr($urla[0], 0, 4)) !== "http" || substr($urla[1], 0, 2) !== "//") {
return -3;
}
// Escape Shell Arguments
$url = escapeshellarg($url);
// Random file name
$fname = "/tmp/" . bin2hex(random_bytes(10)) . ".pdf";
// Generate a PDF file
shell_exec("wkhtmltopdf \"$url\" \"$fname\"");
// Load file
$buffer = file_get_contents("$fname");
// Delete the file after loading
unlink("$fname");
$buffsz = strlen($buffer);
// Prepare headers
header("Content-Type:application/pdf");
if ($download) {
$download = trim($download);
header("Content-Disposition:attachment;filename=\"$download\"");
} else {
header("Content-Disposition:inline");
}
header("Content-Length:" . $buffsz);
exit($buffer);
}
// Demonstrate ni_wkhtmltopdf_simple
// Are we getting the URL parameter?
if (isset($_GET["url"])) {
// Convert array to string
if (is_array($_GET["url"])) {
$_GET["url"] = $_GET["url"][0];
}
// Remove whitespace
$url = trim($_GET["url"]);
// URL is empty so unset it
if ($url == "") {
unset($_GET["url"], $url);
header("Location:" . basename(__FILE__));
}
// Get PDF output
if (isset($url)) {
ni_wkhtmltopdf_simple($url);
}
} else {
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<title>PHP wkhtmltopdf_simple Demo (t3ap0t@whosebug.com)</title>
<style>
*{outline:0}
html,body{
zoom:1.25
}
</style>
</head>
<body>
<form action="<?= basename(__FILE__) ?>" method="GET">
<label for="url">URL:</label>
<input id="url" name="url" type="text" value="https://" minlength="8" required autofocus />
<button id="btn" type="submit">Generate PDF</button>
<script type="text/javascript">
function urlhandler(e) {
// URL Value must begin with https://
if (url.value.trim() == "") {
url.value = "https://" + url.value;
}
// Prevent removal of https://
if (e.keyCode == 8 && url.value == "https://") {
e.preventDefault();
}
// Prevent Delete key
if (e.keyCode == 46) {
e.preventDefault();
}
// Add https:// if it was removed during Paste operation
if (url.value.substr(0, 8).toLowerCase() !== "https://") {
url.value = "https://" + url.value;
}
}
function btnhandler(e) {
if (url.value.substr(0, 8).toLowerCase() !== "https://") {
url.value = "https://" + url.value;
}
// Prevent submission of the form
e.preventDefault();
// Make sure we've provided a URL
if (8 >= url.value.trim().length ||
url.value.trim()[9] == ".") {
alert("You must provide a URL.");
return;
}
// Automatically guess top-level domain
if (url.value.trim().substr(-4, 1) !== "." &&
url.value.trim().substr(-3, 1) !== ".") {
url.value += ".com";
}
url.parentNode.submit();
}
// Event listeners
url.addEventListener("keydown", function(e) {
urlhandler(e);
});
url.addEventListener("onpaste", function(e) {
urlhandler(e);
});
btn.addEventListener("click", function(e) {
btnhandler(e);
});
</script>
</form>
</body>
<?php
}
?>
代码(过度设计)
<?php
/**
* --- DO NOT REMOVE THIS DOCBLOCK ---
* @WebCrawlTrackingId fcc5094e.ccc3a1df.5eb4dbfa.6c3772e1
*/
/**
* @file
* Code for ni_wkhtmltopdf function.
* Includes a demonstration at the end.
*/
/**
* Function that returns a PDF file
* from a URL using wkhtmltopdf.
*
* @author t3ap0t@whosebug.com
*
* @param string $url
* URL to convert
*
* @param string $https
* Ensures we're giving it HTTPS
*
* @param string $download
* Decide whether to download the
* file by specifying a filename
* or don't specify anything to
* display it in the browsers
* built-in PDF viewer.
*
* @param string $checkcmd
* Ensure we have all commands
* required to fulfil the operation.
*
* * On Windows hosts these commands
* can be acquired on using `scoop`.
*
* @param string $checkos
* Make sure we're running Linux.
*
* * Optional if we have both commands
* available on a Windows host.
*
*
* @return int|file
* Return (int) -1 if URL is empty
* Return (int) -2 if URL is not a string
* Return (int) -3 if URL is not a URL
* Return (int) -4 if protocol is not HTTPS
* Return (int) -5 if OS is not Linux
* Return (int) -6 if command wkhtmltopdf not found
* Return (int) -7 if command cat not found
* Return (int) -8 wkhtmltopdf returned nothing
*/
function ni_wkhtmltopdf($url = "", $https = false, $download = false, $checkcmd = true, $checkos = false) {
// URL can't be empty
if ($url == "") {
return -1;
}
// URL must be a string
if (gettype($url) !== "string") {
return -2;
}
// Remove whitespace
$url = trim($url);
// Explode URL by ':' to Array
$urla = explode(":", $url, 2);
// URL must be an actual URL
if (strtolower(substr($urla[0], 0, 4)) !== "http" || substr($urla[1], 0, 2) !== "//") {
return -3;
}
// Optional: Make sure the URL is HTTPS (Secure)
if ($https && strtolower(substr($url, 0, 8)) !== "https://") {
return -4;
}
// Optional: Check operating system
if ($checkos && strtolower(PHP_OS) !== "linux") {
return -5;
}
// Optional: (Linux) Make sure the `wkhtmltopdf` command exists
if ($checkcmd && !(`which wkhtmltopdf` > 0)) {
return -6;
}
// Optional: (Linux) Make sure the `cat` command exists
if ($checkcmd && !(`which cat` > 0)) {
return -7;
}
// Clear URL to (hopefully) prevent RCE
$rep = array(
" " => "%20",
"%20%20" => "",
"`" => "%60",
";" => "%3B",
":" => "%3A",
">" => "%3E",
"<" => "%3C",
"[" => "%5B",
"]" => "%5D",
"{" => "%7B",
"}" => "%7D",
"(" => "%28",
")" => "%29",
"|" => "%7C",
"$" => "%24",
"&&" => "%26%26",
'"' => "%22",
"\" => "%5C"
);
// Replace $a with $b inside URL
foreach ($rep as $a => $b) {
$url = str_replace($a, $b, $url);
}
unset($rep);
// Generate a PDF file
exec("wkhtmltopdf \"$url\" - | cat", $buffer);
$buffer = implode("\n", $buffer);
$buffsz = strlen($buffer);
// Is buffer empty?
if (0 >= $buffsz) {
return -8;
}
// Prepare headers
header("Content-Type:application/pdf");
if ($download) {
$download = trim($download);
header("Content-Disposition:attachment;filename=\"$download\"");
} else {
header("Content-Disposition:inline");
}
header("Content-Length:" . $buffsz);
exit($buffer);
}
// Demonstrate ni_wkhtmltopdf
// Are we getting the URL parameter?
if (isset($_GET["url"])) {
// Convert array to string
if (is_array($_GET["url"])) {
$_GET["url"] = $_GET["url"][0];
}
// Remove whitespace
$url = trim($_GET["url"]);
// URL is empty so unset it
if ($url == "") {
unset($_GET["url"], $url);
header("Location:" . basename(__FILE__));
}
// Get PDF output
if (isset($url)) {
ni_wkhtmltopdf($url);
}
} else {
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<title>PHP wkhtmltopdf Demo (t3ap0t@whosebug.com)</title>
<style>
*{outline:0}
html,body{
zoom:1.25
}
</style>
</head>
<body>
<form action="<?= basename(__FILE__) ?>" method="GET">
<label for="url">URL:</label>
<input id="url" name="url" type="text" value="https://" minlength="8" required autofocus />
<button id="btn" type="submit">Generate PDF</button>
<script type="text/javascript">
function urlhandler(e) {
// URL Value must begin with https://
if (url.value.trim() == "") {
url.value = "https://" + url.value;
}
// Prevent removal of https://
if (e.keyCode == 8 && url.value == "https://") {
e.preventDefault();
}
// Prevent Delete key
if (e.keyCode == 46) {
e.preventDefault();
}
// Add https:// if it was removed during Paste operation
if (url.value.substr(0, 8).toLowerCase() !== "https://") {
url.value = "https://" + url.value;
}
}
function btnhandler(e) {
if (url.value.substr(0, 8).toLowerCase() !== "https://") {
url.value = "https://" + url.value;
}
// Prevent submission of the form
e.preventDefault();
// Make sure we've provided a URL
if (8 >= url.value.trim().length ||
url.value.trim()[9] == ".") {
alert("You must provide a URL.");
return;
}
// Automatically guess top-level domain
if (url.value.trim().substr(-4, 1) !== "." &&
url.value.trim().substr(-3, 1) !== ".") {
url.value += ".com";
}
url.parentNode.submit();
}
// Event listeners
url.addEventListener("keydown", function(e) {
urlhandler(e);
});
url.addEventListener("onpaste", function(e) {
urlhandler(e);
});
btn.addEventListener("click", function(e) {
btnhandler(e);
});
</script>
</form>
</body>
<?php
}
?>