Php 当文件存储在webroot之外时,是否上载和下载文件?(菲律宾)
我正在构建一个web应用程序,它本质上是一个工作板——用户创建“工作/项目”,然后将其显示为一个全球“待办事项”列表,供公司其他人查看 我尝试实现的功能之一是“附件”功能,允许用户上传文件作为每个项目数据的一部分。 其想法是允许用户安全地上传文件,然后允许其他用户下载附件 例如,如果我们正在为客户创建产品包装,那么最好能够将客户的徽标(a.pdf或其他格式)作为项目数据的一部分,以便使用我们的工作板查看项目的任何设计师都可以下载该文件Php 当文件存储在webroot之外时,是否上载和下载文件?(菲律宾),php,html,mysql,file-upload,download,Php,Html,Mysql,File Upload,Download,我正在构建一个web应用程序,它本质上是一个工作板——用户创建“工作/项目”,然后将其显示为一个全球“待办事项”列表,供公司其他人查看 我尝试实现的功能之一是“附件”功能,允许用户上传文件作为每个项目数据的一部分。 其想法是允许用户安全地上传文件,然后允许其他用户下载附件 例如,如果我们正在为客户创建产品包装,那么最好能够将客户的徽标(a.pdf或其他格式)作为项目数据的一部分,以便使用我们的工作板查看项目的任何设计师都可以下载该文件 结合使用常见的上传技术并参考一本PHP书籍(Larry U
结合使用常见的上传技术并参考一本PHP书籍(Larry Ullman编写的动态网站的PHP和MySQL),我构建了以下PHP脚本:
[..]
// check if the uploads form has been submitted:
if($_SERVER['REQUEST_METHOD'] == 'POST') {
//check if the $_FILES global has been set:
if (isset($_FILES['upload'])) {
//create a function to rewrite the $_FILES global (for readability):
function reArrayFiles($file) {
$file_ary = array();
$file_count = count(array_filter($file['name']));
$file_keys = array_keys($file);
for ($i=0; $i<$file_count; $i++) {
foreach ($file_keys as $key) {
$file_ary[$i][$key] = $file[$key][$i];
}
}
return $file_ary;
}
//create a variable to contain the returned data & call the function
//**Quick note: I thought simply stating 'reArrayFiles($_FILES['upload']);' would be enough, but I guess not
$file_ary = reArrayFiles($_FILES['upload']);
//establish an array of allowed MIME file types for the uploads:
$allowed = array(
'image/pjpeg', //.jpeg
'image/jpeg',
'image/JPG',
'image/X-PNG', //.png
'image/PNG',
'image/png',
'image/x-png',
'image/gif', //.gif
'application/pdf', //.pdf
'application/msword', //.doc
'application/vnd.openxmlformats-officedocument.wordprocessingml.document', //.docx
'application/vnd.ms-excel', //.xls
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', //.xlsx
'text/csv', //.csv
'text/plain', //.txt
'text/rtf', //.rtf
);
//these are two arrays for containing statements and errors that occured for each individual file upload
//so I can choose exactly where these "errors" print on page, rather than printing where the script as a whole is called
$statement = array();
$upload_error = array();
//multi-file upload, so perform checks and actions on EACH file upload individually:
foreach ($file_ary as $upload) {
//validate the uploaded file's MIME type using finfo:
$fileinfo = finfo_open(FILEINFO_MIME_TYPE); //open handle
//read the file's MIME type (using magic btyes), then check if it is w/i the allowed file types array
if ( in_array((finfo_file($fileinfo, $upload['tmp_name'])), $allowed) ) {
//check the file's MIME type AGAIN, but this time using the rewritten $_FILES['type'] global
//it may be redundant to check the file type twice, but I felt this was necessary because some files made it past the first conditional
if ( in_array($upload['type'], $allowed) && ($upload['size'] < 26214400) ) {
//set desired file structure to store files:
//the tmp directory is one level outside my webroot
//the '$job_data[0]' variable/value is the unique job_id of each project
//here, it is used to create a folder for each project's uploads -- in order to keep them organized
$structure = "../tmp/uploads/job_" . $job_data[0] . "/";
//check if the folder exists:
if (file_exists($structure) && is_dir($structure)) {
//if directory already exists, get file count: (files only - no directories or subdirectories)
$i = 0;
if (($handle = opendir($structure))) {
while (($file = readdir($handle)) !== false) {
if (!in_array($file, array('.','..')) && !is_dir($structure.$file))
$i++;
}
closedir($handle);
$file_count = $i;
}
} else {
//directory does not exist, so create it
//files are NOT counted b/c new directories shouldn't have any files) -- '$file_count == 0'
mkdir($structure);
}
//if file count is less than 10, allow file upload:
//this limits the project so it can only have a maximum of 10 attachments
if ($file_count < 10) {
if (move_uploaded_file($upload['tmp_name'], "$structure{$upload['name']}")) {
$statement[] = '<p>The file has been uploaded!</p>';
} else {
$statement[] = '<p class="error">The file could not be transfered from its temporary location -- Possible file upload attack!</p>';
}
} else if ($file_count >= 10) {
//if there are already 10 or more attachments, DO NOT upload files, return statement/error
$statement[] = '<p class="error">Only 10 attachments are allowed per Project.</p>';
}
//ELSE FOR 2ND FILE TYPE CHECK:
} else {
$statement[] = '<p class="error">Invalid basic file type.</p>';
}
//set an error msg to $upload_error array if rewritten $_FILES['error'] global is not 0
//this section of code omitted; literally every upload script does this
if ($upload['error'] > 0) {
switch ($upload['error']) {
[...]
}
}
//remove the temp file if it still exists after the move/upload
if ( file_exists($upload['tmp_name']) && is_file($upload['tmp_name']) ) {
unlink ($upload['tmp_name']);
}
//ELSE FOR 1ST FILE TYPE CHECK
} else {
$statement[] = '<p class="error">Invalid MIME file type.</p>';
}
//close the finfo module
finfo_close($fileinfo);
} //END OF FOREACH
} //END OF isset($_FILES['upload']) conditional
}//END OF $_SERVER['REQUEST_METHOD'] == 'POST' conditional
[…]
//检查上传表单是否已提交:
如果($\u服务器['REQUEST\u METHOD']=='POST'){
//检查是否已设置$\u全局文件:
如果(isset($_文件['upload'])){
//创建一个函数来重写$\u全局文件(为了可读性):
函数reArrayFiles($file){
$file_ary=array();
$file_count=count(数组_过滤器($file['name']);
$file\u keys=数组\u keys($file);
对于($i=0;$i相关(1):
理论是:您将文件存储在webroot之外的某个位置,以防止直接访问和在服务器端执行。当用户提供正确的参数时,您必须能够找到它。(但是,您应该小心,文件是如何呈现给用户的?在这里,数据库可能会很有用)如果出于安全考虑,您必须确保用户无法访问他本不应该访问的文件(例如,当他拥有正确的下载链接但没有权限时,因为到目前为止,您的下载链接似乎不太神秘)
您在问题中提到的脚本非常好,尽管我可能只使用fpassthru
而不是feof-fread-echo循环。这里的想法基本上是找出mime类型,将其添加到标题中,然后将内容转储到输出流中
关于第(2)款:
使用数据库,尤其是与准备好的语句一起使用,是非常安全的,并且提供了一些额外的可能性(例如向附件添加一些注释、时间戳、文件大小、重新排序等等)
您没有检查上传名称
,这很可能是。/../your webroot/index.php
或类似的内容。我的建议是将上传的文件存储为非想象的“文件ID”并将该id与原始文件名一起存储在数据库中。您可能还应该删除任何前导的多个点、斜杠(“目录”)和类似内容
加载栏…我想这就是我的口味。为了清晰起见,我删除了我的评论,并将其放在了原始帖子中…尽管下载链接是基本的,但我还是要添加登录验证/权限,这样我们就清楚了(但感谢提醒)…我明白你关于数据库使用的意思,并且在检查$upload时有很好的收获['name']--我会解决的。
<form enctype="multipart/form-data" action="edit-job.php" method="post">
<input type="hidden" name="MAX_FILE_SIZE" value="26214400"/>
<fieldset>
<legend>Upload Project Files</legend>
<input type="file" name="upload[]"/>
<input type="file" name="upload[]"/>
<input type="file" name="upload[]"/>
<input type="file" name="upload[]"/>
<input type="file" name="upload[]"/>
<p>Max Upload Size = 25MB</p>
<p><b>Supported file types:</b> .jpeg, .png, .gif, .pdf, .doc, .docx, .xls, .xlsx, .csv, .txt, .rtf</p>
</fieldset>
<input type="submit" name="submit" value="Edit Job"/>
</form>