5

最终目标:单击页面 1 上的链接,最终下载文件并刷新页面 1. 使用 PHP 提供不在公共 html 中的下载。

方法:

第 1 页。 链接转移到第 2 页,获取我正在使用的文件的变量引用。

第 2 页。 使用刷新第 1 页之前需要更新的信息更新相关 SQL 数据库。设置“firstpass”会话变量。从获取变量中设置会话变量“getvariablereference”。重定向到第 1 页。

第 1 页。 如果第一次通过会话变量集。设置第二遍会话变量。取消设置第一遍变量。刷新页面。重新加载时,页面将使用更新的 SQL 数据库信息重建(在第 2 页更改。)。

刷新第 1 页。 如果设置了第二遍会话变量。运行下载服务标头序列。

这是第 1 页。我没有显示第 1 页中具有初始链接的部分。既然无所谓。

// REFERSH IF FIRSTPASS IS LIVE
if ($_SESSION["PASS1"] == "YES"){
    $_SESSION["PASS1"] = "no";
    $_SESSION["PASS2"] = "YES";
    echo "<script>document.location.reload();</script>";
    }
if ($_SESSION["PASS2"] == "YES"){
    // Grab reference data from session:
        $id = $_SESSION['passreference'];
                // Serve the file download
                        //First find the file location
                        $query = "SELECT * from rightplace
                              WHERE id = '$id'";
                        $result = mysql_query($query);
                        $row = mysql_fetch_array($result);
                        $filename = $row['file'];
                        $uploader = $row['uploader'];   
                            // Setting up download variables
                                $string1 = "/home/domain/aboveroot/";
                                $string2 = $uploader;
                                $string3 = '/';
                                $string4 = $filename;
                                $file= $string1.$string2.$string3.$string4;
                                $ext = strtolower (end(explode('.', $filename)));
                                //Finding MIME type
                                    if($ext == "pdf" && file_exists($file)) {
                                        header("Content-disposition: attachment; filename= '$filename'");
                                        header('Content-type: application/pdf');
                                        readfile($file);
                                        }                                   
                                    if($ext == "doc" && file_exists($file)) {
                                        header("Content-disposition: attachment; filename= '$filename'");
                                        header('Content-type: application/msword');
                                        readfile($file);
                                        }                   
                                    if($ext == "txt" && file_exists($file)) {
                                        header("Content-disposition: attachment; filename= '$filename'");
                                        header('Content-type: text/plain');
                                        readfile($file);
                                        }                   
                                    if($ext == "rtf" && file_exists($file)) {
                                        header("Content-disposition: attachment; filename= '$filename'");
                                        header('Content-type: application/rtf');
                                        readfile($file);
                                        }
                                    if($ext == "docx" && file_exists($file)) {
                                        header("Content-disposition: attachment; filename= '$filename'");
                                        header('Content-type: application/vnd.openxmlformats-officedocument.wordprocessingml.document');
                                        readfile($file);
                                        }
                                    if($ext == "pptx" && file_exists($file)) {
                                        header("Content-disposition: attachment; filename= '$filename'");
                                        header('Content-type: application/vnd.openxmlformats-officedocument.presentationml.presentation');
                                        readfile($file);
                                        }
                                    if($ext == "ppt" && file_exists($file)) {
                                        header("Content-disposition: attachment; filename= '$filename'");
                                        header('Content-type: application/vnd.ms-powerpoint');
                                        readfile($file);
                                        }
                                        }

第 2 页上的脚本工作正常。它会更新 sql 数据库并正确重定向到主页。我还检查了它是否设置了“$_SESSION['passreference'];” 正确,第 1 页上的任何内容都不会取消它。

所以,这就是对这种情况的全部长解释。我难住了。正如我所说,第 2 页工作正常。然后它踢到第 1 页,刷新然后不推送任何下载。我知道下载脚本有效,并且可以下载文件(在没有整个刷新序列的情况下进行检查)。

我基本上有两个问题:

  1. 谁能发现出了什么问题?

  2. 任何人都可以概念化更好的方法吗?

4

3 回答 3

6

即使给定代码,也很难远程调试这样的东西,你发布的部分就像你说的那样工作。你检查过你的错误日志吗?header()最可能的罪魁祸首是在完成其他输出后发送问题。

在处理文件下载时,我认为在新页面/窗口上启动下载更容易,因此不会有破坏标题的风险。可能使用启动实际下载的第三页稍微改变了顺序:

  1. 第 1 页链接到第二页做魔术,它重定向回第 1 页
  2. 然后第 1 页在新窗口中生成第 3 页,这将启动下载。

在这个答案中有一个很好的示例代码,用于加载新窗口以供下载。

于 2012-08-08T01:07:45.823 回答
3

查看您的代码,下载问题可能是该$ext变量包含一个意外的值,或者该$file变量包含一个确实不存在的文件的名称。
在这两种情况下,您的“如果”条件都不成立,因此下载不会开始。我的建议是在 " " 注释行
之前添加以下语句://Finding MIME type

$log  = "file='".$file."'\n";
$log .= "ext='".$ext."'\n";
@file_put_contents("/tmp/page1.log", $log, FILE_APPEND);

这样,查看“/tmp/page1.log”文件,您应该能够检查$file$ext变量是否有效地包含预期值。
我使用“/tmp/page1.log”作为日志文件名,因为我认为您正在使用 linux;如果没有,请使用您环境的有效路径和文件名调整“file_put_contents”函数的第一个参数。
另外,我会用以下代码替换“if”测试的序列:

$content_types = array(
    "pdf"  => "application/pdf",
    "doc"  => "application/msword",
    "txt"  => "text/plain",
    "rtf"  => "application/rtf",
    "docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
    "pptx" => "application/vnd.openxmlformats-officedocument.presentationml.presentation"
);

if (isset($content_types[$ext])) {
    if (file_exists($file)) {
        header("Content-disposition: attachment; filename= '$filename'");
        header('Content-type: '.$content_types[$ext]);
        readfile($file);
        die("");
    } else {
        die("** '".$file."' does not exist **");
    }
} else {
    die("** Unhandled '".$ext."' extension **");
}

显然,您应该以更健壮的方式实现错误处理,而不是像我一样简单地使用“die()”函数,但这只是一个示例。
最后,请注意,还有更好的方法来获取与文件扩展名对应的内容类型;例如,一种解决方案是使用 PHP Fileinfo函数。
查看此答案以获取有关此主题的更多信息。
还要记住,在安全模式下,file_exists 函数总是返回 FALSE,并且 file_exists 函数的结果会被缓存;有关详细信息,请参阅 PHP 手册中的clearstatcache()函数。

于 2012-08-09T20:55:39.000 回答
0

我只是稍微修改了你的 PHP 代码。尤其是您将获得有关问题所在的更多信息。只需尝试此代码并阅读以下注释,如果您收到新的错误消息之一,这些注释会解释发生了什么。另请阅读下面的注释部分,它解释了为什么您可能无法从 PHP 访问文件,即使它存在并且位于正确的目录中。

  1. 使用 window.location.reload(); 而不是 document.location ...
  2. 我添加了一个 error() 函数。您可以向其中添加更多 HTML,因此它会以您想要的布局生成页面。您也可以将错误记录到本地文件中。有一个私有信息参数用于将敏感信息作为数据库错误(可以包含 SQL)传递给函数。对于生产性用途,您不应将其显示给用户。相反,您可以将其登录到文件中或只为特权用户(例如管理员)显示它。
  3. 检查天气 $id 是否已设置。如果不是,则返回 error() 消息;如果会话未正确更新,可能会发生。
  4. 我添加了“$id = addlashes($id);” 出于安全原因。例如,如果您的 id 可以设置为 $id = "' OR 1" (SQL-Injection) 之类的值,您可能会遇到麻烦。如果您确定这不会发生,您可以将其删除。
  5. 它在 DB 查询之后检查 $result 变量。例如,如果您的数据库连接未建立或脚本无法连接,这将产生一个错误()输出,通知您。如果您的 SQL 语法有错误,例如错误的表名,也会发生同样的情况。
  6. 它还检查天气是否从数据库中获取了有效的 $row。如果没有返回一行,您的 $id 可能是错误的(您的数据库中没有这样的条目)。
  7. 我将您的字符串操作重写为 $filepath = $rootpath 。“/”。$上传者。“/”。$文件名;其中 $rootpath 之前设置,末尾没有“/”;这更容易阅读......
  8. 扩展和 MIME 类型现在被放入一个数组中,而不是使用大量的“if-then”块,这样更容易维护。这些块中的代码也相似......所以我们只需要编写一次。
  9. 如果文件扩展名未知,则发送默认 MIME 类型(Content-Type:"application/octet-stream)。
  10. 我们检查 file_exists() 并输出错误消息,并给出 $filename 以允许检查天气路径是否正确......

所以这里是源代码:

<?php 

function error($message, $info = "") {
  echo "ERROR: $message<br>";
  echo "PRIVATE-INFO: $info"; // probably you only want to log that into a file?
  exit;
}

// REFERSH IF FIRSTPASS IS LIVE
if ($_SESSION["PASS1"] == "YES") {
  $_SESSION["PASS1"] = "no";
  $_SESSION["PASS2"] = "YES";
  echo "<script>window.location.reload();</script>";
  exit;
}


if ($_SESSION["PASS2"] == "YES") {
  // Grab reference data from session:
  $id = $_SESSION['passreference'];

  if (!$id) error("Internal Error ('id' not set)");

  // Select file location from DB
  $id = addslashes($id);
  $query = "SELECT * from rightplace WHERE id = '$id'";
  $result = mysql_query($query);

  if (!$result) error("DB-query execution error", mysql_error());

  $row = mysql_fetch_array($result);
  mysql_free_result($result);

  if (!$row) error("File with ID '$id' was not found in DB.");

  $filename = $row['file'];
  $uploader = $row['uploader'];

  // Setting up download variables
  $rootpath = "/home/domain/aboveroot";
  $filepath = $rootpath . "/" . $uploader . "/" . $filename;
  $ext = strtolower(end(explode('.', $filename)));

  // Serve the file download

  // List of known extensions and their MIME-types...
  $typelist = array(
      "pdf"  => "application/pdf",
      "doc"  => "application/msword",
      "txt"  => "text/plain",
      "rtf"  => "application/rtf",
      "docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
      "pptx" => "application/vnd.openxmlformats-officedocument.presentationml.presentation",
      "ppt"  => "application/vnd.ms-powerpoint"
  );

  // set default content-type
  $type = "application/octet-stream";

  // for known extensions, assign specific content-type
  if (!isset($typelist[$ext])) $type = $typelist[$ext];

  if (file_exists($filepath)) {
    header("Content-disposition: attachment; filename= '$filename'");
    header("Content-type: $type");
    readfile($filepath);
  } else {
    error("Error: File '$filepath' was not found!", $filepath);
  }
}

?>

笔记:

  1. 即使文件存在,也可能发生文件未找到错误。如果发生这种情况,这很可能是一种防止 PHP 脚本访问 HTML 根目录之外的文件的安全机制。例如,php 脚本可以在“chrooted”环境中执行,其中根目录“/”被映射到例如“/home/username/”。因此,如果您想访问“/home/username/dir/file”,则需要在 PHP 脚本中写入“/dir/file”。如果您的根设置为“/home/username/html”,则情况可能更糟;那么您将无法访问“html”目录下的目录。要解决这个问题,您可以在 HTML 根目录中创建一个目录,并在其中放置一个名为“.htaccess”的文件。在里面写“拒绝所有人”,这可以防止通过浏览器请求访问目录(只有脚本可以访问它)。这仅适用于 apache 服务器。但是对于其他服务器软件也有类似的解决方案......更多信息可以在下面找到:http://www.php.net/manual/en/ini.core.php#ini.open-basedir

  2. 另一种可能性是您的文件访问权限(对于上传的文件)未设置为允许您的脚本访问它们。启用某些安全设置(在 linux 服务器上)后,您的 PHP 脚本只能访问与为脚本文件设置的“所有者”相同的用户所拥有的文件。通过“ftp”上传后,这很可能是 ftp 用户的用户名。如果在 shell 上编辑,这将是当前用户的用户名。=> 但是:上传的文件有时会分配给运行网络服务器的用户(例如“www-data”、“www-run”或“apache”)。所以找出它是什么并将你的脚本分配给这个所有者。

  3. 对于文件上传,您应该使用 move_uploaded_file(...) ,此处解释为:www.php.net/manual/en/function.move-uploaded-file.php;如果您不这样做,文件访问权限可能是错误的,或者您可能无法访问该文件。
于 2012-08-15T13:56:07.917 回答