我正在尝试创建一个用户可以在数据库中存储文件的平台。我认为问题在于我没有正确插入数据库中的文件,因为无论何时下载它我都无法打开。
我的数据库结构包括以下内容:
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);
答案 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();
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注入。这意味着当用户名作为参数传递时,它将被视为数据,而不是查询的一部分。由于您在将值作为参数传递之前添加了斜杠,这意味着您实际存储了反斜杠,从而破坏了数据。
为什么要在MySQL中存储10 MB文件?一般的解决方案是将文件存储在磁盘上,而不是存储在RDBMS中。
接收文件就像将文件从临时目录复制/移动到特定目录一样简单。
使用fpassthru
等功能将文件发送给用户也很容易。
为什么要将文件大小存储为select * from User where UserName = '\'; drop table User; \''
,而它是一个整数?
答案 1 :(得分:0)
我无法仅仅因为其他用户无权访问磁盘而存储在磁盘中。如果人们出于某种原因猜到了他们可以下载的文件的名称(如图像),那就是为什么我要存储在数据库中以避免这种情况。
这只是一种妄想。当然,文件系统可以提供相同级别的安全性。没有人强迫您在Web根目录中存储文件。您可以将它们存储在上面,无法直接访问。就这么简单。