PHP文件上传源码分析(RFC1867)
而基于HTTP的上传,相对来说易用性和安全性上就比FTP要增强了很多. 可以应用的上传方式有PUT, WEBDAV, 和RFC1867三种, 本文将分析在PHP中,是如何基于RFC1867实现文件上传的.
RFC1867
RCF1867是Form-based File Upload in HTML标准协议, RFC1867标准对HTML做出了两处修改:
1 为input元素的type属性增加了一个file选项。
2 input标记可以具有accept属性,该属性能够指定可被上传的文件类型或文件格式列表。
另外,本标准还定义了一种新的mime类型:multipart/form-data,以及当处理一个带有enctype=”multipart/form-data” 并且/或含有<input type=”file”>的标记的表单时所应该采取的行为。
举例来说,当HTML想让用户能够上传一个或更多的文件时,他可以这么写:
代码如下 | 复制代码 |
<form enctype="multipart/form-data" action="upload.php" method=post> |
这个表单, 大家一定不陌生, 而对于PHP来说, 它自己另外定义了一个默认表单元素MAX_FILE_SIZE, 用户可以通过这个隐藏的表单元素来建议PHP最多只容许上传文件的大小, 比如对于上面的例子, 我们希望用户上传的文件不能大于5000(5k)字节, 那么可以如下写:
代码如下 | 复制代码 |
<form enctype="multipart/form-data" action="upload.php" method=post> |
姑且不说, 这个MAX_FILE_SIZE是多么的不可靠(所以基于浏览器的控制,都是不可靠的), 单纯从实现来讲, 我会慢慢介绍这个MAX_FILE_SIZE是如何起作用的.
当用户选择了一个文件(laruence.txt), 并填写好文件描述(”laruence的个人介绍”), 点击上传后, 发生了什么呢?
表单提交
在用户确定提交以后, 浏览器会发送如下类似格式的数据包到form中action属性指定的页面(在本例中是upload.php):
代码如下 | 复制代码 |
//请求头
|
接下来, 就是服务器, 是如何处理这些数据了.
接受上传
当Web服务器, 此处假设为Apache(另外假设PHP是以module方式安装在Apache上的), 接受到用户的数据时, 首先它根据HTTP请求头, 通过确定MIME TYPE为PHP类型, 然后经过一些过程以后(这部分,可以参看我之前的PHP Life Cycle ppt), 最终会把控制权交给PHP模块.
这个时候, PHP会调用sapi_activate来初始化一个请求, 在这个过程中, 首先判断请求类型, 此时是POST, 从而去调用sapi_read_post_data, 通过Content-type, 找到rfc1867的处理函数rfc1867_post_handler, 从而调用这个handler, 来分析POST来的数据.
关于rfc1867_post_handler这部分的源代码, 可以在mian/rfc1867.c找到, 另外也可以参看我之前的深入理解PHP之文件上传, 其中也列出的源代码.
然后, PHP通过boundary, 对于每一个分段, 都通过检查, 是否同时定义了:
name和filename属性(有名文件上传)
没有定义name定义了filename(无名上传)
定义了name没有定义filename(普通数据),
从而进行不同的处理.
代码如下 | 复制代码 |
if ((cd = php_mime_get_hdr_value(header, "Content-Disposition"))) { while (isspace(*cd)) { while (*cd && (pair = php_ap_getword(&cd, ';'))) while (isspace(*cd)) { if (strchr(pair, '=')) { if (!strcasecmp(key, "name")) {
|
在这个过程中, PHP会去检查普通数据中,是否有MAX_FILE_SIZE.
代码如下 | 复制代码 |
/* Normal form variable, safe to read all data into memory */ if (!strcasecmp(param, "MAX_FILE_SIZE")) { efree(param);
|
有的话, 就会按照它的值来检查文件大小是否超出.
代码如下 | 复制代码 |
if (PG(upload_max_filesize) > 0 && total_bytes > PG(upload_max_filesize)) {
|
通过上面的代码,我们也可以看到, 判断分为俩部, 第一部分是检查PHP默认的上传上限. 第二部分才是检查用户自定义的MAX_FILE_SIZE, 所以表单中定义的MAX_FILE_SIZE并不能超过PHP中设置的最大上传文件大小.
通过对name和filename的判断, 如果是文件上传, 会根据php的设置, 在文件上传目录中创建一个随机名字的临时文件:
代码如下 | 复制代码 |
if (!skip_upload) {
|
返回文件句柄, 和临时随机文件名.
之后, 还会有一些验证,比如文件名合法, name合法等.
如果这些验证都通过, 那么就把内容读入, 写入到这个临时文件中.
.....
代码如下 | 复制代码 |
else if (blen > 0) {
|
当循环读入完成后, 关闭临时文件句柄. 记录临时变量名:
代码如下 | 复制代码 |
zend_hash_add(SG(rfc1867_uploaded_files), temp_filename,
|
并且生成FILE变量, 这个时候, 如果是有名上传, 那么就会设置:
代码如下 | 复制代码 |
$_FILES['userfile'] //name="userfile" |
如果是无名上传, 则会使用tmp_name来设置:
代码如下 | 复制代码 |
$_FILES['tmp_name'] //无名上传 |
最终交给用户编写的upload.php处理.
这时在upload.php中, 用户就可以通过move_uploaded_file来操作刚才生成的文件了
本文章详细的介绍了关于投票系统实现原理与实现代码,有需要的朋友可参考一下。
数据库的设计
设计三张表:投票结果统计表(count_voting),投票人记录表(ip_votes),用户表(user)
投票结果统计表用于统计最后的投票记录,我给它弄了4个字段:被投票项的名称(SelectName),被投票项标签名(LabelName)(起到分类的作用),票数(CountVotes)。
投票人记录表用于登记投票人的ip(IP),地理位置(Location),投票时间(VoteTime),被投票项名称(SelectName)。然后我还给它加一个ID。
用户表主要用于给管理员用的,包含用户名(name)和密码(passwd)。
生成表的sql脚本如下:
代码如下 | 复制代码 |
DROP TABLE IF EXISTS `count_voting`; -- -------------------------------------------------------- -- DROP TABLE IF EXISTS `ip_votes`; -- -- -------------------------------------------------------- -- DROP TABLE IF EXISTS `user`; -- INSERT INTO `user` (`name`, `passwd`) VALUES -- -- |
从脚本中可以看出,我创建了一个触发器,当往ip_votes表中插入数据的时候就给count_voting表中的CountVotes字段加1。还能后出最后一句是设置外部关联字。
框架设计
OperatorDB类用于操作数据库,OperatorVotingDB类用于该系统特定的操作集合。
使用PDO操作数据库,我它简单的封装一下:
代码如下 | 复制代码 |
public function __construct() |
把连接数据库的信息封装进去方便后续的操作。
代码如下 | 复制代码 |
public function __construct() /** /** /** /** /** /** |
Zend_Auth和Zend_Acl进行登录认证及根据用户角色进行权限控制 有需要的朋友可参考一下。
Zend_Auth_Adapter_Interface中提供了一个接口,我们需要自己去实现
代码如下:
代码如下 | 复制代码 |
<?php
|
解释:在authenticate方法的实现代码中,return一个Zend_Auth_Result对象实例,而查看Zend_Auth_Result的源代码,知道实例化的时候需要传入三个参数:
@param int $code 身份认证的结果(如:Zend_Auth_Result::SUCCESS)
@param mixed $identity 用于身份认证的标示符(如:登录名(张三))
@param array $messages 认证失败的原因数组
而一旦认证成功,则将信息存储到session变量中。
要实现异步上传图片方法有常用的有二种,一种是利用iframe实现,另一种是借助于ajax来实现一般用第三方插件了。
上传图片form提交target到一个隐藏的iframe里,
代码如下 | 复制代码 |
form action="upload.php" id="form1" name="form1" enctype="multipart/form-data" method="post" target="uploadIframe"> |
然后后台处理完上传图片逻辑后返回给前台,利用ajax修改当前页面DOM对象实现无刷新上传图片的友好功能。
实例
代码如下 | 复制代码 |
a.html <form enctype="multipart/form-data" action="a.php" target="ifram_sign" method="POST"> <iframe name="ifram_sign" src="" frameborder="0" height="0" width="0" marginheight="0" marginwidth="0"></iframe> |
PHP代码:
代码如下 | 复制代码 |
<?php if ($_FILES["test_file"]["error"] > 0) { echo "Error: " . $_FILES["test_file"]["error"] . "<br />"; } else { //这里的判断图片属性的方法就不写了。自己扩展一下。 $filetype=strrchr($_FILES["test_file"]["name"],"."); $filetype=substr($filetype,1,strlen($filetype)); $filename="img/".time("YmdHis").".".$filetype; move_uploaded_file($_FILES["test_file"]["tmp_name"],$filename); echo '<script >alert(1)</script>'; $return="parent.document.getElementByIdx_x('mpic".$pageset_id."').innerHTML='".$dataimgpath."'"; echo "<script >alert('上传成功')</script>"; echo "<script>{$return}</script>"; } ?> |
其实jquery ajax图片异步上传
HTML:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en_US" xml:lang="en_US">
<head>
<title>图片异步上传</title>
</head>
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/index.js"></script>
<link type="text/css" rel="stylesheet" href="css/index.css">
<body>
<div class="frm">
<form name="uploadFrom" id="uploadFrom" action="upload.php" method="post" target="tarframe" enctype="multipart/form-data">
<input type="file" id="upload_file" name="upfile">
</form>
<iframe src="" width="0" height="0" style="display:none;" name="tarframe"></iframe>
</div>
<div id="msg">
</div>
</body>
</html>
index.js
$(function(){
$("#upload_file").change(function(){
$("#uploadFrom").submit();
});
});
function stopSend(str){
var im="<img src='upload/images/"+str+"'>";
$("#msg").append(im);
}
upload.php
<?php
$file=$_FILES['upfile'];
$name=rand(0,500000).dechex(rand(0,10000)).".jpg";
move_uploaded_file($file['tmp_name'],"upload/images/".$name);
//调用iframe父窗口的js 函数
echo "<script>parent.stopSend('$name')</script>";
?>
介绍一个
使用方法
$
代码如下 | 复制代码 |
passwdHandler = new Htpasswd('/home/myuser/.htpasswd'); // Add a user with name 'user1' and password 'I prefer to use passphrase rather than password.' if it doesn't exist in .htpasswd. $passwdHandler -> addUser('user1', 'I prefer to use passphrase rather than password.'); // Delete the user 'user1' if it exists in .htpasswd. $passwdHandler -> deleteUser('user1'); // Check if user 'user1' exists in .htpasswd. if ($passwdHandler -> doesUserExist('user1')) { // User 'user1' exists. } |
htpasswd类
代码如下 | 复制代码 |
class Htpasswd { |
相关文章
- 下面小编来给大家演示几个php操作zip文件的实例,我们可以读取zip包中指定文件与删除zip包中指定文件,下面来给大这介绍一下。 从zip压缩文件中提取文件 代...2016-11-25
Jupyter Notebook读取csv文件出现的问题及解决
这篇文章主要介绍了JupyterNotebook读取csv文件出现的问题及解决,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2023-01-06- 有时我们接受或下载到的PSD文件打开是空白的,那么我们要如何来解决这个 问题了,下面一聚教程小伙伴就为各位介绍Photoshop打开PSD文件空白解决办法。 1、如我们打开...2016-09-14
- 这篇文章主要介绍了解决python 使用openpyxl读写大文件的坑,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-03-13
- 这篇文章主要介绍了C#实现HTTP下载文件的方法,包括了HTTP通信的创建、本地文件的写入等,非常具有实用价值,需要的朋友可以参考下...2020-06-25
- 这篇文章主要为大家详细介绍了SpringBoot实现excel文件生成和下载,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-02-09
- C#使用System.IO中的文件操作方法在Windows系统中处理本地文件相当顺手,这里我们还总结了在Oracle中保存文件的方法,嗯,接下来就来看看整理的C#操作本地文件及保存文件到数据库的基本方法总结...2020-06-25
php无刷新利用iframe实现页面无刷新上传文件(1/2)
利用form表单的target属性和iframe 一、上传文件的一个php教程方法。 该方法接受一个$file参数,该参数为从客户端获取的$_files变量,返回重新命名后的文件名,如果上传失...2016-11-25- 本文章来人大家介绍一个php文件上传类的使用方法,期望此实例对各位php入门者会有不小帮助哦。 简介 Class.upload.php是用于管理上传文件的php文件上传类, 它可以帮...2016-11-25
- 要替换字符串中的内容我们只要利用php相关函数,如strstr,str_replace,正则表达式了,那么我们要替换目录所有文件的内容就需要先遍历目录再打开文件再利用上面讲的函数替...2016-11-25
- 又码了一个周末的代码,这次在做一些关于文件上传的东西。(PHP UPLOAD)小有收获项目是一个BT种子列表,用户有权限上传自己的种子,然后配合BT TRACK服务器把种子的信息写出来...2016-11-25
- 本文实例讲述了jQuery实现文件上传进度条效果的代码。分享给大家供大家参考。具体如下: 运行效果截图如下:具体代码如下:<!DOCTYPE html><html><head><meta charset="utf-8"><title>upload</title><link rel="stylesheet...2015-11-24
- 今天小编在这里就来给photoshop的这一款软件的使用者们来说下AI源文件转photoshop图像变模糊问题的解决教程,各位想知道具体解决方法的使用者们,那么下面就快来跟着小编...2016-09-14
- 这篇文章主要介绍了C++万能库头文件在vs中的安装步骤(图文),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-23
- 步骤:Window -> PHP -> Editor -> Templates,这里可以设置(增、删、改、导入等)管理你的模板。新建文件注释、函数注释、代码块等模板的实例新建模板,分别输入Name、Description、Patterna)文件注释Name: 3cfileDescriptio...2013-10-04
- 本篇文章主要说明的是与php文件上传的相关配置的知识点。PHP文件上传功能配置主要涉及php.ini配置文件中的upload_tmp_dir、upload_max_filesize、post_max_size等选项,下面一一说明。打开php.ini配置文件找到File Upl...2015-10-21
- 这篇文章主要介绍了C#使用StreamWriter写入文件的方法,涉及C#中StreamWriter类操作文件的相关技巧,需要的朋友可以参考下...2020-06-25
ant design中upload组件上传大文件,显示进度条进度的实例
这篇文章主要介绍了ant design中upload组件上传大文件,显示进度条进度的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-10-29- 举一个案例:复制代码 代码如下:<?phpclass Downfile { function downserver($file_name){$file_path = "./img/".$file_name;//转码,文件名转为gb2312解决中文乱码$file_name = iconv("utf-8","gb2312",$file_name...2014-06-07
- 这篇文章主要为大家详细介绍了js实现上传图片及时预览的相关资料,具有一定的参考价值,感兴趣的朋友可以参考一下...2016-05-09