文件的下载很简单,在前端HTML标记语言中就能实现。但是,文件的上传却不是那么容易,需要进行服务器端的简单编程。而且,还有很多细节方面的注意事项,官方文件中都没有说明,这也是很多人对文件上传这个问题很苦恼的原因。其实只需要注意几个细节,就能轻松搞定文件上传啦!
图文 / 丁建雄
小白是单纯为兴趣而写作的独立创作人,如果您喜欢小白的文章,欢迎关注、交流、分享(引用请链接到本文)。
PHP简介
PHP(外文名:PHP: Hypertext Preprocessor,中文名:“超文本预处理器”)是一种通用开源脚本语言。语法吸收了C语言、Java和Perl的特点,利于学习,使用广泛,主要适用于Web开发领域。PHP 独特的语法混合了C、Java、Perl以及PHP自创的语法。它可以比CGI或者Perl更快速地执行动态网页。用PHP做出的动态页面与其他的编程语言相比,PHP是将程序嵌入到HTML(标准通用标记语言下的一个应用)文档中去执行,执行效率比完全生成HTML标记的CGI要高许多;PHP还可以执行编译后代码,编译可以达到加密和优化代码运行,使代码运行更快。
这个是百度百科的解释,其实简单来讲,PHP就是一门在服务器端通用的编程语言,功能十分强大!
HTML是一种标记语言,而对于一些需要复杂动态运算的算法设计,HTML就无法实现了。这个时候,在服务器端,就需要类似PHP,JavaScript这种脚本语言来实现。小白的背景是光电专业,当时学的是C++,所以对PHP这种通用型脚本语言情有独钟。
PHP是一种比较松散的语言,这也就使得他更接近于人类自然语言,没有很多的限制,但是你需要注意更多的细节,才能游刃有余,小白强烈推荐!
文件上传
这里所指的文件上传不同于服务器端的FTP上传,是指在网页客户端,在访问页面进行实时的文件上传操作。
具体而言,实现方法有很多种,比如:基于Java、xml、HTML的方法;在HTML页面直接嵌入式简单JavaScript的方法;使用Apache fileupload库的方法;基于HTML、PHP脚本的方法等。
小白基本这些方法都尝试过,个人觉得HTML+PHP的方法最稳定、简单、实用。
HTML表单
HTML表单是一个强大的网页信息交互系统,尤其是一些少量信息的交互,表单处理将变得十分高效。
大家可以这么理解,我们浏览网页大部分时候只是为了得到信息。但是有些时候,我们需要向网页反馈我们的想法、信息,这个时候就需要表单出场了。
至于表单的细节性问题,这里先不细聊,在我的前端开发文章集 [ 网页设计 ] 篇章会有文章专门讨论表单,这里就直截了当上代码。
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Hello, world</title>
<!-- Bootstrap -->
<link href="css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<h1>你好,世界!</h1>
<!--put your contents here-->
<form action="upload.php" method="post" enctype="multipart/form-data">
<label for="file">Filename:</label>
<br/>
<br/>
<input type="file" name="file" id="file"/>
<br />
<input type="submit" name="submit" value="提交上传" />
</form>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="http://cdn.bootcss.com/jquery/1.11.1/jquery.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="js/bootstrap.min.js"></script>
</body>
</html>
这里代码还是沿用的之前Hello,World的版本,注意中间 <form></form>
部分是新增的代码。
稍微解释一下各个部分的内容:
action="upload.php"
是页面指向,也就是这个表单提交的页面URL是 upload.php
,这个页面其实是我们下面要编写的一个处理脚本程序。程序是需要变量的,而下面的 input
选项就是存放的变量,包含了变量的类型 type
和变量的名称 name
。
我们是制作一个上传文件的表单,那么表单的方法就是上传,即 method="post"
。我们还要指定文件的编码方式,默认地,表单数据会编码为 application/x-www-form-urlencoded
。就是说,在发送到服务器之前,所有字符都会进行编码,这是单纯的文字的编码方式。但是,对于文件就不行了,得使用 multipart/form-data
方法。
那个 <label>
是等同于输入文件按钮的一个新的标签,也就是说你可以在同一个页面的不同处设立上传按钮,上传到同一个空间,而只需要重写标签,不需要重写上传控件。
<input type="file" name="file" id="file"/>
定义了上传控件文件的类型、名称、和ID(为了重定义标签使用的名字)。
<input type="submit" name="submit" value="提交上传" />
这个是提交按钮,只要点了这个按钮,就会转向脚本文件,并把文件上传到服务器上。
其实,表单属性如果要细聊会很复杂。当然,也正是由于他的重要性,多样性,才有了多姿多彩的网页设计规划。这是一个非常有趣的话题,小白将在前端的文章中跟大家好好讨论一下。
PHP脚本
PHP语言的一般结构
<?php
echo "Hello,World!";
?>
是不是很简单,这个就是PHP版本的Hello,world。
其实,PHP还可以跟HTML语言混合使用,基本上HTML能做的事情,PHP都能做到,就是可能在繁琐程度上会有不同。而且,类似上面的Hello,World在PHP编写好之后,是可以直接发布到HTML上显示的,只需要额外套用个HTML框架即可,比如
<!DOCTYPE html>
<html>
<body>
<?php
echo "Hello,World!";
?>
</body>
</html>
就跟之前的网页版Hello,world的显示效果几乎一模一样(这里为了大家能看清楚,没有套用之前Bootstrap的框架)。
代码部分,我这边是参考的w3school的 PHP教程,自己稍微做了一些修改
<?php
if ((($_FILES["file"]["type"] == "image/gif")
|| ($_FILES["file"]["type"] == "image/jpeg")
|| ($_FILES["file"]["type"] == "image/pjpeg"))
&& ($_FILES["file"]["size"] < 2000000))
{
if ($_FILES["file"]["error"] > 0)
{
echo "Return Code: " . $_FILES["file"]["error"] . "<br />";
}
else
{
echo "Upload: " . $_FILES["file"]["name"] . "<br />";
echo "Type: " . $_FILES["file"]["type"] . "<br />";
echo "Size: " . ($_FILES["file"]["size"] / 1024) . " Kb<br />";
echo "Temp file: " . $_FILES["file"]["tmp_name"] . "<br />";
if (file_exists("upload/" . $_FILES["file"]["name"]))
{
echo $_FILES["file"]["name"] . " already exists. ";
}
else
{
move_uploaded_file($_FILES["file"]["tmp_name"],
"upload/" . $_FILES["file"]["name"]);
echo "Stored in: " . "upload/" . $_FILES["file"]["name"];
}
}
}
else
{
echo "Invalid file";
}
?>
这个就是表单中提到的 upload.php
文件了。
这边一些基本的条件语句相信大家有点编程基础的都能看懂吧,这边就不赘述了。
PHP变量命名规则是以 $
开始的,这里的变量其实就一个 $_FILES
。注意,PHP变量是对大小写敏感的。其余的变量属性 file name type size
等都是从表单中获得的,也就是说,这个脚本的实例化是依赖于表单的输入文件的。这个PHP文件只是作为一种处理方法,而跟HTML标记不一样,不具有实际存在的形态,必须依赖于外部输入才能执行。
这里,前面三个或 和一个与 条件构成了上传文件的约束 。在这里,规定只能上传 gif、jpeg 、和 pjpeg 格式的小于2000000bit大小的图片。当然,这个只是个约束范例,如果大家要传输各类文件,只需要加上其他约束就行了。如果要任何文件都传输,那么就直接删掉约束条件就行了。但是,不建议这么做,当网站面向大众的时候,这么做很容易成为黑客入侵的快速入口,对服务器来讲是致命的。
内部,关键性的上传代码是
if (file_exists("upload/" . $_FILES["file"]["name"]))
{
echo $_FILES["file"]["name"] . " already exists. ";
}
else
{
move_uploaded_file($_FILES["file"]["tmp_name"],
"upload/" . $_FILES["file"]["name"]);
echo "Stored in: " . "upload/" . $_FILES["file"]["name"];
}
这条语句的意思是,先看目录下有没有同名称文件,如果有,就提示已经存在相同文件,并提示,且不会继续上传;如果没有相同文件,则使用 move_uploaded_file
方法将文件上传到指定目录中,并且输出存储目录。
这里,最关键的上传步骤就存在于上传方法那个封装好的函数之中,这块是标准化的库函数,大家直接调用就可以了,在安装了PHP的服务器上,这个函数是存在于默认的库中的,不需要额外添加。
分享一个自己的小故事
关键词:权限、刷新——服务器文件默认权限对文件上传的影响。
一般来讲,服务器为了安全性考虑,一般是默认只读权限。小白使用的CentOS系统,安装的是Apache服务器。
这里还有个小故事,小白初次接触文件上传的时候,在网上找了各路博客大神的帖子,基本把相关的热帖都看遍了,还有官方说明文档。但是,很不巧,文件上传总是不成功(虽然文件提示都已经上传了,也就是说程序都跑通了,但是服务器上就是没有文件)。
咋办呢?小白开始怀疑了,难道官方文档也有错?
后来,在折腾了3个小时之后,在一个论坛回复里面看到有人稍微说了一下权限 问题,我也没抱太大希望,因为没多少人讨论过权限。
于是,小白就抱着试试看的心态,修改了目标文件夹的权限。没想到,奇迹出现了—— 提示有变化了,之前都是简单提示已经 存储完毕,这次是 文件已存在 。
有点小欣喜,但是,目标文件夹还是没有文件啊!
咋办呢?这次至少知道文件已经存储在服务器上了,但是为什么还是看不到呢?难道是延迟?
小白先登出服务器,再进入,还是看不到!到底是个咋回事?当时都已经凌晨3点了,小白真的要崩溃了,但是不搞出来也睡不着啊,算了,撸起袖子继续找,一定要搞出来,毕竟有眉目了!
小白在自己本地的电脑上是习惯于不断刷新(虽然感觉并没有什么用),由于服务器端小白是FTP登录,窗口比较小,没注意过刷新。但是,就在这个时候,小白不小心点了一下刷新!
奇迹出现了,上传的所有文件都出现了!瞬间如释重负、欣喜若狂啊!
哎呀妈呀!原来是这个问题啊,这下我终于懂得刷新的重要性了,瞬间对刷新有了全新的理解!
故事还没结束。
第二天早上,虽然四点多才睡觉的,但是小白8点就醒了,想想昨晚的成果,瞬间又有了动力,继续干,再摸索摸索,看看有没有新的发现。
我把之前的文件夹删除了,新建了一个文件夹,看看上传效果。不搞不知道,一搞吓一跳,原封不动的代码,又不行了!
不过,不要急,这个bug处理过,不就是权限 问题嘛!我把权限修改好,又做了一次,这下好了!这里小白要着重说明一个事情:
很多人都知道设定一个文件夹及其子文件夹的权限之后,原有文件夹的属性肯定是不变的。但是,如果在文件夹下面新建一个子文件夹,那这个子文件夹的权限会继承 父文件夹的权限吗?
答案是,不会!新建的子文件夹权限还是默认的权限,不会因为父辈的权限而继承,若要继承,得再设定一次权限,才能改变新建文件夹的权限属性。
这个问题是最重要的了,小白为这个问题付出了4个多小时的努力,问题虽小,却很关键!
由此,小白感觉现在的各路博客大神 写文章真是有点水了。都喜欢各种转载、抄袭,而不去真正实践,实践才能找到问题,也才能写出真正对求知者有用的文章。否则,泥沙俱下,人云亦云,垃圾太多的结果就是浪费了大家太多的时间。
这也是小白最初想写一点自己的心得的原因。小白每一次的写作都是自己亲自尝试过的,有任何的细小的问题,小白都会在文章中跟大家详细讨论。无惧简单,避免含糊、装高大上。写出最浅显易懂的文章,阐释最基本的技术原理,这是小白的追求。
结语
好啦,又到了本期问题思考环节了!
本期的问题是这样的
问题1:
仔细看的小伙伴一定已经发现了,这个上传重复文件的判断语句似乎是不合理的!
我们正常的文件上传情况都是如果有重复文件,会跳出一个处理对话框,选择是否覆盖掉原始文件,然而这里却没有。你能不能设计一个简单的方案,来弥补这个小缺陷,达到正常的处理效果呢?
问题2:
在服务器端,如果某个文件夹有写 权限,会对服务器安全造成影响,具体影响可能有哪些?你有没有什么好的解决办法呢?
技术问题没有高级与低级之分,无论多么细小的问题,你提出来都会给别人或多或少提供帮助与参考。
所以,大家有任何疑问或者建议都可以留言或者私信小白哦~
欢迎各位查阅相关资料,在留言中积极参加讨论。