3

我正在尝试在 drupal 中显示与文章关联的相应 PDF 文件,这样如果浏览器配备了呈现 pdf 的功能,它应该直接打开,如果没有显示带有“打开”、“保存”和“取消”的常规对话框允许用户在选项中进行选择。PDF 存在于公共 Web 服务器中,可以访问图像和文件等所有资源。

我用来完成的代码如下:

$pdf_file_name = "http://mysite.com/valid-pdf-file.pdf";
drupal_set_header('Content-type: application/pdf');
drupal_set_header('Content-Disposition: inline; filename="' . $pdf_file_name
 . '"');
$fp = fopen($pdf_file_name, "r");
$file_open_timeout = 60;
if ($fp) {
  stream_set_timeout($fp, $file_open_timeout); 
  while (!feof($fp)) {
    echo fread($fp, 65536);
    flush();
  }
  fclose($fp);
}
else {
  watchdog("pdf logging", "Could not open the file " . $pdf_file_name);
}

尽管此代码适用于我的一些初始 pdf 文件(我假设它们是不超过 10 MB 的小文件),但它在许多其他文件上都失败了,并显示以下错误消息:

fopen(http://mysite.com/valid-pdf-file.pdf):无法打开流:HTTP 请求失败!HTTP/1.1 404 未找到

通过浏览器打开该文件http://mysite.com/valid-pdf-file.pdf的路径会直接呈现该文件,而不会出现任何错误消息。因此,我根据一些谷歌搜索尝试了stream_set_timeout上面的代码,但尽管文件肯定存在,但仍然无法克服 404 错误。

这是一个无法打开的 URL 的示例:

http://fileservername.com/resources/sitename/2013/03/20/bed6e3de-41bf-4bf9-bed8-d21508eaa8ca/Trouble --云纲要优化.pdf”

我还用 url 编码文件路径进行了测试,但结果没有什么不同;urlencode($pdf_file_name)在 fopen 和 readfile 中做了。

此外,我检查了这些麻烦的 pdf 文件的文件/文件夹权限是否与正常打开的文件/文件夹权限不同,但发现并没有什么不同。

此文件处理的替代代码

ob_clean();
flush();
readfile($pdf_file_name);
exit();

给出了相同的结果,其中麻烦的 pdf 返回 404,而其他的工作正常。任何关于我错过了什么来获得这个功能或更好地实现这个功能的任何建议都将不胜感激。

4

1 回答 1

4

您的问题确实是 URL 编码,但您不能只是urlencode()整个字符串,因为这也会转义一些需要保持不变的字符。我会建议这样的事情:

function escape_url($url)
{
    // Check that the input data is sane
    if (!$parts = parse_url($url)) {
        return false;
    }
    if (!isset($parts['scheme'], $parts['host'])) {
        return false;
    }

    // construct site base URL
    $result = $parts['scheme'] . '://';

    if (isset($parts['user'])) {
        $result .= $parts['user'];
        if (isset($parts['pass'])) {
            $result .= ':' . $parts['pass'];
        }
        $result .= '@';
    }

    $result .= $parts['host'];

    // Normalize path
    if (!isset($parts['path'])) {
        // if no path assume domain root
        $parts['path'] = '/';
    }
    $parts['path'] = preg_split('#/+#', $parts['path']); // split to path components
    $parts['path'] = array_map(function($part) { // ensure all components are correctly escaped
        return urlencode(urldecode($part));
    }, $parts['path']);
    $parts['path'] = implode('/', $parts['path']); // reconstruct string
    $result .= $parts['path'];

    // parse the query string an rebuild it
    if (isset($parts['query'])) {
        parse_str($parts['query'], $query);
        if (function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()) {
            // undo magic_quotes
            array_walk_recursive($query, function(&$value) {
                $value = preg_replace('#\\\\([\'"\\\\\\x00])#', '$1', $value);
            });
        }
        $result .= '?' . http_build_query($query);
    }

    // add document fragment if present
    if (isset($parts['fragment'])) {
        $result .= '#' . $parts['fragment'];
    }

    return $result;
}

$url = 'http://fileservername.com/resources/sitename/2013/03/20/bed6e3de-41bf-4bf9-bed8-d21508eaa8ca/Trouble --Cloud Compendium optimized.pdf';
echo escape_url($url);
// output:
// http://fileservername.com/resources/sitename/2013/03/20/bed6e3de-41bf-4bf9-bed8-d21508eaa8ca/Trouble+--Cloud+Compendium+optimized.pdf

看到它工作

注意:这用于parse_str()规范化可能在 URL 上的任何查询字符串,这受magic_quotes_gpc配置选项的影响。此选项已弃用,不安全,应禁用,但您应该注意,如果启用它会影响此函数的输出。无法在运行时调整此设置,您需要确保在您的环境中禁用该设置。

编辑更正了路径组件的潜在双重编码,添加了对搞砸时的修复magic_quotes。请注意,这些修复使用闭包,因此需要 PHP 5.3+,因为create_function()可以替换较低版本或辅助函数。

于 2013-04-02T09:05:48.460 回答