PHP中存储的文件已损坏

时间:2014-05-30 11:37:41

标签: php mysqli

我正在尝试创建一个用户可以在数据库中存储文件的平台。我认为问题在于我没有正确插入数据库中的文件,因为无论何时下载它我都无法打开。

我的数据库结构包括以下内容:

ID        -> INT
file      -> LongBlob (I need to insert files at least at maximum with 10MB)
file_name -> Varchar
file_type -> Varchar
file_size -> Varchar 

要插入文件,请访问mysqli代码:

$_fileName = $_FILES['myfile']['name']; 
$_fileTmp  = $_FILES['myfile']['tmp_name'];
$_fileType = $_FILES['myfile']['type'];
$_fileSize = $_FILES['myfile']['size'];
$_file     = addslashes(file_get_contents($_FILES[0]['tmp_name']));
$query = $mysqli->prepare("INSERT INTO files (file, file_name, file_type, file_size)
                                      VALUES (?, ?, ?, ?)");
/*
    b = blob
    s = string
*/
$query->bind_param("bsss", $_file, $_fileName, $_fileType, $_fileSize);
$query->execute();

我相信这是正确的,但是...... 最后,下载我使用的文件:

header("Content-length:" . $file['file_size']);
header("content-type:" .   $file['file_type']);
header("Content-disposition: download; filename=" . $file['file_name']);

echo $file['file'];

虽然我下载文件并尝试打开其损坏的文件(如.rar文件或zip文件)。 此外,我尝试使用.txt文件并且全部为空......它可以是什么?

感谢。

编辑:我显然知道存储在磁盘中比存储在数据库中更好。但是,在这种情况下,我无法仅仅因为其他用户无权访问它而存储在磁盘中。如果人们出于某种原因猜到了他们可以下载的文件的名称(如图像),那就是为什么我要存储在数据库中以避免这种情况。

我还删除了addslashes(),但它继续损坏文件。

修改 - 已解决

问题发生在bind_param()上。 The documentation of mysqli_stmt::bind-param说''代表Blob BUT它将以数据包形式发送。我相信,这种情况正在搞乱。

我所要做的就是改变' b'到了' (字符串)在我的bind_param()

$query->bind_param("ssss", $_file, $_fileName, $_fileType, $_fileSize);

2 个答案:

答案 0 :(得分:2)

评论显示,问题不是数据损坏,因为原始问题断言,但事实上数据首先没有存储在数据库中。答案会相应改变。

您正在使用bind_param存储blob。相反,您需要使用send_long_data。根据{{​​1}}文档中的a comment

  

如果有人指定' b'在$ types中,相应的变量应该设置为null,并且必须使用mysqli_stmt :: send_long_data()或mysqli_stmt_send_long_data()来发送blob,否则blob值将被视为空。

这意味着实际:

bind-param

应该是:

$_file = addslashes(file_get_contents($_FILES[0]['tmp_name']));
$query = $mysqli->prepare("INSERT INTO files
                       (file, file_name, file_type, file_size)
                       VALUES (?, ?, ?, ?)");
$query->bind_param("bsss", $_file, $_fileName, $_fileType, $_fileSize);
$query->execute();

附加说明

  1. addslashes在需要转义的字符之前返回带有反斜杠的字符串。例如:

    $query = $mysqli->prepare("INSERT INTO files
                               (file, file_name, file_type, file_size)
                               VALUES (?, ?, ?, ?)");
    $query->bind_param("bsss", NULL, $_fileName, $_fileType, $_fileSize);
    $query->send_long_data(0, file_get_contents($_FILES[0]['tmp_name']));
    $query->execute();
    
    如果按原样传递给数据库,

    会出现SQL注入的风险,因为如果用户名是"select * from User where UserName = '" . $userName . "'" ,则执行的查询将是:

    "'; drop table User; '"

    select * from User where UserName = ''; drop table User; '' 将用于避免攻击:

    addslashes

    给出:

    "select * from User where UserName = '" . addslashes($userName) . "'"
    

    同样,参数化查询使得无法通过转换特殊字符来执行SQL注入。这意味着当用户名作为参数传递时,它将被视为数据,而不是查询的一部分。由于您在将值作为参数传递之前添加了斜杠,这意味着您实际存储了反斜杠,从而破坏了数据。

  2. 为什么要在MySQL中存储10 MB文件?一般的解决方案是将文件存储在磁盘上,而不是存储在RDBMS中。

    • 接收文件就像将文件从临时目录复制/移动到特定目录一样简单。

    • 使用fpassthru等功能将文件发送给用户也很容易。

  3. 为什么要将文件大小存储为select * from User where UserName = '\'; drop table User; \'' ,而它是一个整数?

答案 1 :(得分:0)

  

我无法仅仅因为其他用户无权访问磁盘而存储在磁盘中。如果人们出于某种原因猜到了他们可以下载的文件的名称(如图像),那就是为什么我要存储在数据库中以避免这种情况。

这只是一种妄想。当然,文件系统可以提供相同级别的安全性。没有人强迫您在Web根目录中存储文件。您可以将它们存储在上面,无法直接访问。就这么简单。