打开具有有效路径的远程pdf文件将返回404

时间:2013-04-02 08:31:39

标签: php drupal http-status-code-404

我正在尝试显示与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错误,尽管文件肯定存在。

以下是其中一个无法打开的网址如下所示的示例:

http://fileservername.com/resources/sitename/2013/03/20/bed6e3de-41bf-4bf9-bed8-d21508eaa8ca/Trouble --Cloud Compendium optimized.pdf”

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

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

此文件处理的替代代码

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

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

1 个答案:

答案 0 :(得分: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

See it working

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

编辑更正了路径组件的潜在双重编码,添加了magic_quotes解决问题的方法。请注意,这些修补程序使用闭包,因此需要PHP 5.3+,对于较低版本create_function()或辅助函数可以替换。