博客列表 >细说文件上传原理与实战

细说文件上传原理与实战

溪边小树
溪边小树原创
2020年05月28日 16:59:031335浏览

文件上传

1. 文件上传配置

文件上传项目项在php.ini中设置,常用的配置项有:

序号 配置项 默认值 描述
1 file_uploads On 使 PHP 支持文件上传
2 upload_tmp_dir /tmp 指示应该临时把上传的文件存储在什么位置
3 max_file_uploads 20 单次请求时允许上传的最大文件数量
4 max_execution_time 30 设置 PHP 最长执行时间(秒)
5 max_input_time 60 设置 PHP 通过 POST/GET/PUT 接收数据的时长(秒)
6 memory_limit 128M 系统分配给当前脚本执行可用的最大内存容量
7 post_max_size 8M 允许的 POST 数据的总大小(以字节为单位)
8 upload_max_filesize 32M 允许的尽可能最大的文件上传(以字节为单位)

$_FILES

  • 上传文件的描述信息,全部保存在系统全局变量$_FILES
  • $_FILES以二维数组形式保存: $_FILES['form_file_name']['key']
  • 'form_file_name': 对应着表单中<input type="file" name="my_pic">name属性值
  • 'key': 共有 5 个键名, 描述如下:
序号 键名 描述
1 name 文件在客户端的原始文件名(即存在用户电脑上的文件名)
2 type 文件的 MIME 类型, 由浏览器提供, PHP 并不检查它
3 tmp_name 文件被上传到服务器上之后,在临时目录中临时文件名
4 error 和该文件上传相关的错误代码
5 size 已上传文件的大小(单位为字节)
  • 文件上传错误信息描述
序号 常量 描述
1 UPLOAD_ERR_OK 0 没有错误发生,文件上传成功
2 UPLOAD_ERR_INI_SIZE 1 文件超过php.iniupload_max_filesize
3 UPLOAD_ERR_FORM_SIZE 2 文件大小超过表单中MAX_FILE_SIZE指定的值
4 UPLOAD_ERR_PARTIAL 3 文件只有部分被上传
5 UPLOAD_ERR_NO_FILE 4 没有文件被上传
6 UPLOAD_ERR_NO_TMP_DIR 6 找不到临时文件夹
7 UPLOAD_ERR_CANT_WRITE 7 文件写入失败
  • 支持文件上传的前端表单设置
序号 属性设置 描述
1 <form method="POST"> 请求类型必须是POST
2 <form enctype="multipart/form-data"> 设置表单提交数据的编码类型
3 <input type="file" name="uploads"> 设置表单控件类型与名称以支持上传
4 <input type="hidden" name="MAX_FILE_SIZE" value="..."> 设置隐藏域限制上传文件大小(可选)
  • [扩展] enctype属性说明

enctype: 设置表单数据,在发送到服务器之前的编码规则

序号 属性值 描述
1 application/x-www-form-urlencoded 默认值, 发送前进行编码,空格转+,非空字符转 16 进制
2 multipart/form-data 不对字符编码,以二进制发送,适合文件上传
3 text/plain 纯文本发送,仅对空格编码(转为+)

4. MIME 类型

4.1 概述

  • MIME: (Multipurpose Internet Mail Extensions)的简写,中文意思”多功能因特网邮件扩展”
  • MIME: 最初用来表示 Email 附件格式的字符串, 后来演变成为网络文档或应用程序的文档格式规范
  • MIME: 由一个媒体类型一个子类组成, 中间用斜线/分隔,例如text/css

4.2 类型

序号 类型 描述 示例
1 text 文本 text/plain,text/html,text/css,text/javascript
2 image 图像 1 image/jpeg,image/gif,image/png,image/bmp,image/ webp,
2 image 图像 2 image/x-icon, image/vnd.microsoft.icon
3 audio 音频 audio/midi, audio/mpeg, audio/webm, audio/ogg, audio/wav
4 video 视频 video/mp4,video/mpeg,video/webm, video/ogg,video/x-msvideo
5 application 二进制 1 application/octet-stream, application/javascript, application/ecmascript
5 application 二进制 2 application/json,application/pkcs12, application/vnd.mspowerpoint
5 application 二进制 3 application/xhtml+xml, application/xml, application/pdf,application/x-gzip
  • 表中,以x-为前缀的是还没有成为国际标准的格式
  • 如果某个文档不在该列表中,会识别为: applicaton/octet-stream, 如.md文档, 尽管它是纯文本

示例演示

前期准备基本代码:

  1. <?php
  2. // 查看与文件上传相关的配置项
  3. phpinfo();
  4. //了解各配置项的基本信息
  5. -------------
  6. <?php
  7. // $_FILES
  8. printf('<pre>%s</pre>', print_r($_FILES, true));
  9. // 二维数组: 只有一个元素
  10. ?>
  11. <!DOCTYPE html>
  12. <html lang="en">
  13. <head>
  14. <meta charset="UTF-8">
  15. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  16. <title>文件上传变量$_FILES</title>
  17. </head>
  18. <body>
  19. <form action="" method="POST" enctype="multipart/form-data">
  20. <fieldset>
  21. <legend>单文件上传</legend>
  22. <input type="hidden" name="MAX_FILE_SIZE" value="500000">
  23. <input type="file" name="my_pic">
  24. <button>上传</button>
  25. </fieldset>
  26. </form>
  27. </body>
  28. </html>
  29. ---------------
  30. <?php
  31. // $_FILES
  32. printf('<pre>%s</pre>', print_r($_FILES, true));
  33. $errorCode = $_FILES['my_pic']['error'];
  34. if (UPLOAD_ERR_NO_FILE === $errorCode) echo '<p>没有文件被上传</p>';
  35. // if (empty($_FILES['my_pic']['name'])) echo '<p>没有文件被上传</p>'
  36. ?>
  37. <!DOCTYPE html>
  38. <html lang="en">
  39. <head>
  40. <meta charset="UTF-8">
  41. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  42. <title>文件上传变量$_FILES</title>
  43. </head>
  44. <body>
  45. <form action="" method="POST" enctype="multipart/form-data">
  46. <fieldset>
  47. <legend>单文件上传: 检查是否有文件被上传</legend>
  48. <input type="hidden" name="MAX_FILE_SIZE" value="500000">
  49. <input type="file" name="my_pic">
  50. <button>上传</button>
  51. </fieldset>
  52. </form>
  53. </body>
  54. </html>
  55. -----------
  56. <?php
  57. // $_FILES
  58. printf('<pre>%s</pre>', print_r($_FILES, true));
  59. // 检测当前文件是否是通过 HTTP POST 方式上传的
  60. $tmpFileName = $_FILES['my_pic']['tmp_name'];
  61. $originalFileName = $_FILES['my_pic']['name'];
  62. if (is_uploaded_file($tmpFileName)) echo "{$orginalFileName}: 上传方式合法";
  63. ?>
  64. <!DOCTYPE html>
  65. <html lang="en">
  66. <head>
  67. <meta charset="UTF-8">
  68. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  69. <title>文件上传变量$_FILES</title>
  70. </head>
  71. <body>
  72. <form action="" method="POST" enctype="multipart/form-data">
  73. <fieldset>
  74. <legend>单文件上传: 检查是否是通过POST上传?</legend>
  75. <input type="hidden" name="MAX_FILE_SIZE" value="500000">
  76. <input type="file" name="my_pic">
  77. <button>上传</button>
  78. </fieldset>
  79. </form>
  80. </body>
  81. </html>
  82. ----------------------
  83. <?php
  84. // $_FILES
  85. printf('<pre>%s</pre>', print_r($_FILES, true));
  86. // 获取文件类型
  87. $fileType = $_FILES['my_pic']['type'];
  88. // echo $fileType, '<br>';
  89. // print_r(explode('/',$fileType)[0]);
  90. // echo strstr($fileType, '/', true);
  91. // $allow = ['jpg', 'png', 'gif'];
  92. // $extension = pathinfo($_FILES['my_pic']['name'])['extension'];
  93. // echo $extension;
  94. // if (!in_array($extension, $allow)) echo '<p>文件类型错误</p>';
  95. if (strstr($fileType, '/', true) !== 'image') echo '<p>文件类型错误</p>';
  96. ?>
  97. <!DOCTYPE html>
  98. <html lang="en">
  99. <head>
  100. <meta charset="UTF-8">
  101. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  102. <title>文件上传变量$_FILES</title>
  103. </head>
  104. <body>
  105. <hr>
  106. <form action="" method="POST" enctype="multipart/form-data">
  107. <fieldset>
  108. <legend>单文件上传: 检测文件类型</legend>
  109. <input type="hidden" name="MAX_FILE_SIZE" value="500000">
  110. <input type="file" name="my_pic">
  111. <button>上传</button>
  112. </fieldset>
  113. </form>
  114. </body>
  115. </html>
  116. ------------
  117. <?php
  118. // $_FILES
  119. printf('<pre>%s</pre>', print_r($_FILES, true));
  120. // php.ini
  121. echo ini_get('upload_max_filesize');
  122. ini_set('upload_max_filesize', '10M');
  123. $errorCode = $_FILES['my_pic']['error'] ?? 'null';
  124. switch ($errorCode) {
  125. case 1:
  126. echo '文件超过`php.ini`中`upload_max_filesize`值';
  127. break;
  128. case 2:
  129. echo '文件大小超过表单隐藏域中`MAX_FILE_SIZE`指定的值 ';
  130. }
  131. ?>
  132. <!DOCTYPE html>
  133. <html lang="en">
  134. <head>
  135. <meta charset="UTF-8">
  136. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  137. <title>文件上传变量$_FILES</title>
  138. </head>
  139. <body>
  140. <hr>
  141. <form action="" method="POST" enctype="multipart/form-data">
  142. >
  143. <fieldset>
  144. <legend>单文件上传: 检测文件大小</legend>
  145. <input type="hidden" name="MAX_FILE_SIZE" value="3000000">
  146. <input type="file" name="my_pic">
  147. <button>上传</button>
  148. </fieldset>
  149. </form>
  150. </body>
  151. </html>

单文件上传示例:

文件上传正常:

文件上传异常:

  1. <?php
  2. // $_FILES
  3. printf('<pre>%s</pre>', print_r($_FILES, true));
  4. // 自定义上传异常类
  5. class UploadException extends Exception
  6. {
  7. // 在异常子类中,可以访问并重写Exception中的四个属性,通过__toString()格式化异常输出信息
  8. public function __toString()
  9. {
  10. return <<< UPLOAD
  11. <style>
  12. table {border-collapse: collapse;border:1px solid black;text-align: center;}
  13. td {border:1px solid black;padding: 5px;}
  14. tr:first-of-type {background-color:#eee;}
  15. tr:last-of-type td {color: coral;}
  16. </style>
  17. <table>
  18. <tr><td>代码</td><td>信息</td><td>文件</td><td>行号</td></tr>
  19. <tr><td>$this->code</td><td>$this->message</td><td>$this->file</td><td>$this->line</td></tr>
  20. </table>
  21. UPLOAD;
  22. }
  23. }
  24. ///////////////////////////////////////////////////////////////////////
  25. try {
  26. // 上传出错的代码
  27. $errorCode = $_FILES['my_pic']['error'];
  28. if ($errorCode > UPLOAD_ERR_OK) {
  29. switch ($errorCode) {
  30. case UPLOAD_ERR_INI_SIZE:
  31. throw new UploadException('上传的文件超过了 php.ini 中 upload_max_filesize 选项限制的值', 1);
  32. break;
  33. case UPLOAD_ERR_FORM_SIZE:
  34. throw new UploadException('上传文件的大小超过了 HTML 表单中 MAX_FILE_SIZE 选项指定的值', 2);
  35. break;
  36. case UPLOAD_ERR_PARTIAL:
  37. throw new UploadException('文件只有部分被上传', 3);
  38. break;
  39. case UPLOAD_ERR_NO_FILE:
  40. throw new UploadException('没有文件被上传', 4);
  41. break;
  42. case UPLOAD_ERR_NO_TMP_DIR:
  43. throw new UploadException('找不到临时文件夹', 6);
  44. break;
  45. case UPLOAD_ERR_CANT_WRITE:
  46. throw new UploadException('文件写入失败', 7);
  47. break;
  48. default:
  49. // 测试时建议关掉default: 避免误报影响
  50. throw new UploadException('未知类型错误', 8);
  51. }
  52. }
  53. // 判断文件类型
  54. $fileType = $_FILES['my_pic']['type'];
  55. if (strstr($fileType, '/', true) !== 'image') throw new UploadException('文件类型错误',9);
  56. // 将文件从临时目录 移动到用户自定义的目标目录中
  57. // 临时文件名
  58. $tempFileName = $_FILES['my_pic']['tmp_name'];
  59. if (is_uploaded_file($tempFileName)) {
  60. // 原始文件名
  61. $originalFileName = $_FILES['my_pic']['name'];
  62. // 目录文件名
  63. $destFileName = 'uploads/'.md5(time().mt_rand(1,1000)).strstr($originalFileName, '.');
  64. // 移动文件到目标目录使用的函数
  65. if (move_uploaded_file($tempFileName, $destFileName)) {
  66. echo "<p>$originalFileName: 上传成功~~</p>";
  67. // 预览
  68. echo "<img src='{$destFileName}' width='200'>";
  69. }
  70. }
  71. } catch (UploadException $e) {
  72. echo $e;
  73. }
  74. ?>
  75. <!DOCTYPE html>
  76. <html lang="en">
  77. <head>
  78. <meta charset="UTF-8">
  79. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  80. <title>文件上传变量$_FILES</title>
  81. </head>
  82. <body>
  83. <hr>
  84. <form action="" method="POST" enctype="multipart/form-data">
  85. <fieldset>
  86. <legend>单文件上传: 异常处理</legend>
  87. <input type="hidden" name="MAX_FILE_SIZE" value="3000000">
  88. <input type="file" name="my_pic">
  89. <button>上传</button>
  90. </fieldset>
  91. </form>
  92. </body>
  93. </html>

多文件上传示例一:

  1. <?php
  2. // $_FILES
  3. printf('<pre>%s</pre>', print_r($_FILES, true));
  4. // 将异常处理先省略,只演示关键代码
  5. foreach ($_FILES as $file) {
  6. // 只要判断 error === 0
  7. if ($file['error'] === 0) {
  8. $destFile = 'uploads/' . $file['name'];
  9. move_uploaded_file($file['tmp_name'], $destFile);
  10. echo "<img src='{$destFile}' width='200'>";
  11. }
  12. }
  13. ?>
  14. <!DOCTYPE html>
  15. <html lang="en">
  16. <head>
  17. <meta charset="UTF-8">
  18. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  19. <title>文件上传变量$_FILES</title>
  20. </head>
  21. <body>
  22. <hr>
  23. <form action="" method="POST" enctype="multipart/form-data">
  24. <fieldset>
  25. <legend>多文件上传: 逐个上传(一)</legend>
  26. <input type="hidden" name="MAX_FILE_SIZE" value="3000000">
  27. <input type="file" name="my_pic1">
  28. <input type="file" name="my_pic2">
  29. <input type="file" name="my_pic3">
  30. <button>上传</button>
  31. </fieldset>
  32. </form>
  33. </body>
  34. </html>

多文件上传示例二:

  1. <?php
  2. // $_FILES
  3. printf('<pre>%s</pre>', print_r($_FILES, true));
  4. // 将异常处理先省略,只演示关键代码
  5. if ($_FILES['my_pic'])
  6. foreach ($_FILES['my_pic']['error'] as $key => $error) {
  7. // 只要判断 error === 0
  8. if ($error === UPLOAD_ERR_OK) {
  9. // 临时文件名
  10. $tmpFileName = $_FILES['my_pic']['tmp_name'][$key];
  11. // 原始文件名
  12. $originalFileName = $_FILES['my_pic']['name'][$key];
  13. // 目标文件名
  14. $destFileName = 'uploads/'. $originalFileName;
  15. // 移动文件
  16. move_uploaded_file($tmpFileName, $destFileName);
  17. // 预览
  18. echo "<img src='{$destFileName}' width='200'>";
  19. }
  20. }
  21. ?>
  22. <!DOCTYPE html>
  23. <html lang="en">
  24. <head>
  25. <meta charset="UTF-8">
  26. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  27. <title>文件上传变量$_FILES</title>
  28. </head>
  29. <body>
  30. <hr>
  31. <form action="" method="POST" enctype="multipart/form-data">
  32. <fieldset>
  33. <legend>多文件上传: 逐个上传(二)</legend>
  34. <input type="hidden" name="MAX_FILE_SIZE" value="3000000">
  35. <!-- 将name属性值能数组的形式提供 -->
  36. <input type="file" name="my_pic[]">
  37. <input type="file" name="my_pic[]">
  38. <input type="file" name="my_pic[]">
  39. <button>上传</button>
  40. </fieldset>
  41. </form>
  42. </body>
  43. </html>

批量文件上传示例:

  1. <?php
  2. // $_FILES
  3. printf('<pre>%s</pre>', print_r($_FILES, true));
  4. // 将异常处理先省略,只演示关键代码
  5. if ($_FILES['my_pic'])
  6. foreach ($_FILES['my_pic']['error'] as $key => $error) {
  7. // 只要判断 error === 0
  8. if ($error === UPLOAD_ERR_OK) {
  9. // 临时文件名
  10. $tmpFileName = $_FILES['my_pic']['tmp_name'][$key];
  11. // 原始文件名
  12. $originalFileName = $_FILES['my_pic']['name'][$key];
  13. // 目标文件名
  14. $destFileName = 'uploads/'. $originalFileName;
  15. // 移动文件
  16. move_uploaded_file($tmpFileName, $destFileName);
  17. // 预览
  18. echo "<img src='{$destFileName}' width='200'>";
  19. }
  20. }
  21. ?>
  22. <!DOCTYPE html>
  23. <html lang="en">
  24. <head>
  25. <meta charset="UTF-8">
  26. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  27. <title>文件上传变量$_FILES</title>
  28. </head>
  29. <body>
  30. <hr>
  31. <form action="" method="POST" enctype="multipart/form-data">
  32. <fieldset>
  33. <legend>多文件上传: 批量上传</legend>
  34. <input type="hidden" name="MAX_FILE_SIZE" value="3000000">
  35. <!-- 将name属性值能数组的形式提供 -->
  36. <input type="file" name="my_pic[]" multiple>
  37. <button>上传</button>
  38. </fieldset>
  39. </form>
  40. </body>
  41. </html>

课程学习小结

本次课程老师讲解非常细致,在老师的直播讲解之后,又通过回看视频进行理解消化,在案例实操方面,老师由浅入深,从基本必备知识开始演示,通过单个文件上传、多个文件上传(列举了两种不同的实现方式)到批量文件上传,涵盖了几乎所有日常可能使用到的情况,并且融入了文件类型检查,异常处理等实现方式,使得代码更加健壮,通过对各个示例代码的认真理解及部分修改实践,加深对本次课程内容的理解。在实际应用系统中,文件上传是非常重要的功能,必须熟练掌握。

声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议