如何中止超过 php 的上传最大值的文件上传?
How can I abort a file upload that exceeds php's upload maximum?
当上传超过 PHP 设置允许的最大大小的文件时,通常,在我能够处理来自服务器端的响应之前,整个文件都会被上传。
在 php.ini 中,我将最大 post 大小和最大上传大小设置为 2 MB。当我 select 上传表单中的文件超过 2 MB(又名 1 GB 文件)时,服务器仍会接受该文件,但我需要等到整个文件上传完毕才能处理 /处理上传事件。
现在,有趣的部分是上传进度会话 returns 100%,允许我 return 控制上传表单(隐藏进度条,显示上传表单并再次提交按钮),但是由于必须等待上传实际完成,我无法 return 向用户返回响应。
对于我的项目,有 3 个部分:
- 上传表单 - 这包含 post 上传到 'upload.php'(隐藏 iframe)的代码,并在 'upload_progress.php' (XHR) 上监控进度。
- upload.php - 这是负责接受文件的文件处理程序,然后根据文件类型、错误、成功等将响应提升回上传表单(跨框架脚本锁定到当前域和协议)。
- upload_progress.php - 这只是读取 php 的上传进度模块提供的上传进度会话数据。响应是一个整数。
问题是如果上传太大,#3 显示为完整,但是#2 继续接受数据,直到整个文件上传完毕。
我想知道的是,有什么方法可以让我在发生这种情况时立即检测到这种情况,而不是 100% 依赖于 JavaScript 在上传前检查文件大小,因为这可以被绕过。我知道我可以在发送文件之前检查客户端,但是我想在文件太大时在 javascript 中实现客户端中止之前实现适当的服务器端上传中止。意思是,我不想依赖 JavaScript 来确保服务器安全(文件类型、大小等)。
希望我解释得足够清楚了。我已经在 PHP 中看到这个问题很多年了,令我惊讶的是,当接收到的数据阈值超过 php.ini[=14 中设置的限制时,文件上传不会在服务器端自动中止=]
以防万一,这是我的 PHP 版本详细信息
PHP 5.6.17-0+deb8u1 (cli) (built: Jan 13 2016 09:10:12)
Copyright (c) 1997-2015 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2015 Zend Technologies
with Zend OPcache v7.0.6-dev, Copyright (c) 1999-2015, by Zend Technologies
PHP FPM(在我的 apache 配置中服务 php)
PHP 5.6.17-0+deb8u1 (fpm-fcgi) (built: Jan 13 2016 09:10:13)
Copyright (c) 1997-2015 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2015 Zend Technologies
with the ionCube PHP Loader (enabled) + Intrusion Protection from ioncube24.com (unconfigured) v5.0.23, Copyright (c) 2002-2016, by ionCube Ltd.
with Xdebug v2.3.3, Copyright (c) 2002-2015, by Derick Rethans
with Zend OPcache v7.0.6-dev, Copyright (c) 1999-2015, by Zend Technologies
和 Apache(根据要求)
Server version: Apache/2.4.10 (Debian)
Server built: Nov 28 2015 14:05:48
Server's Module Magic Number: 20120211:37
Server loaded: APR 1.5.1, APR-UTIL 1.5.4
Compiled using: APR 1.5.1, APR-UTIL 1.5.4
Architecture: 64-bit
Server MPM: prefork
threaded: no
forked: yes (variable process count)
Server compiled with....
-D APR_HAS_SENDFILE
-D APR_HAS_MMAP
-D APR_HAVE_IPV6 (IPv4-mapped addresses enabled)
-D APR_USE_SYSVSEM_SERIALIZE
-D APR_USE_PTHREAD_SERIALIZE
-D SINGLE_LISTEN_UNSERIALIZED_ACCEPT
-D APR_HAS_OTHER_CHILD
-D AP_HAVE_RELIABLE_PIPED_LOGS
-D DYNAMIC_MODULE_LIMIT=256
-D HTTPD_ROOT="/etc/apache2"
-D SUEXEC_BIN="/usr/lib/apache2/suexec"
-D DEFAULT_PIDLOG="/var/run/apache2.pid"
-D DEFAULT_SCOREBOARD="logs/apache_runtime_status"
-D DEFAULT_ERRORLOG="logs/error_log"
-D AP_TYPES_CONFIG_FILE="mime.types"
-D SERVER_CONFIG_FILE="apache2.conf"
使用 Javascript 并在上传前检查文件大小,尽管这不是关于如何从服务器端中止上传的示例。
这是一个现场示例:http://codesheet.org/codesheet/E8ZwGzoV
<form method="post" target="submit" enctype="multipart/form-data" action="/post.php">
<input type="file" name="file" id="file" />
<input type="submit" name="submit" value="Submit" />
</form>
<div id="message"></div>
<iframe name="submit" width="100%" height="400px"></iframe>
JS在这里:
document.forms[0].addEventListener('submit', function( evt ) {
var file = document.getElementById('file').files[0];
// 1 MB = 1048576 this size is in bytes
if(file && file.size < 2097152) { // 2MB = 2097152
//Submit form
document.getElementById('message').innerHTML = 'The File '+bytesToSize(file.size)+' is OK!';
}
else
{
//Prevent default and display error
document.getElementById('message').innerHTML = 'The File '+bytesToSize(file.size)+' is to big!';
evt.preventDefault();
}
}, false);
function bytesToSize(bytes) {
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
if (bytes == 0) return '0 Bytes';
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i];
}
当上传超过 PHP 设置允许的最大大小的文件时,通常,在我能够处理来自服务器端的响应之前,整个文件都会被上传。
在 php.ini 中,我将最大 post 大小和最大上传大小设置为 2 MB。当我 select 上传表单中的文件超过 2 MB(又名 1 GB 文件)时,服务器仍会接受该文件,但我需要等到整个文件上传完毕才能处理 /处理上传事件。
现在,有趣的部分是上传进度会话 returns 100%,允许我 return 控制上传表单(隐藏进度条,显示上传表单并再次提交按钮),但是由于必须等待上传实际完成,我无法 return 向用户返回响应。
对于我的项目,有 3 个部分:
- 上传表单 - 这包含 post 上传到 'upload.php'(隐藏 iframe)的代码,并在 'upload_progress.php' (XHR) 上监控进度。
- upload.php - 这是负责接受文件的文件处理程序,然后根据文件类型、错误、成功等将响应提升回上传表单(跨框架脚本锁定到当前域和协议)。
- upload_progress.php - 这只是读取 php 的上传进度模块提供的上传进度会话数据。响应是一个整数。
问题是如果上传太大,#3 显示为完整,但是#2 继续接受数据,直到整个文件上传完毕。
我想知道的是,有什么方法可以让我在发生这种情况时立即检测到这种情况,而不是 100% 依赖于 JavaScript 在上传前检查文件大小,因为这可以被绕过。我知道我可以在发送文件之前检查客户端,但是我想在文件太大时在 javascript 中实现客户端中止之前实现适当的服务器端上传中止。意思是,我不想依赖 JavaScript 来确保服务器安全(文件类型、大小等)。
希望我解释得足够清楚了。我已经在 PHP 中看到这个问题很多年了,令我惊讶的是,当接收到的数据阈值超过 php.ini[=14 中设置的限制时,文件上传不会在服务器端自动中止=]
以防万一,这是我的 PHP 版本详细信息
PHP 5.6.17-0+deb8u1 (cli) (built: Jan 13 2016 09:10:12)
Copyright (c) 1997-2015 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2015 Zend Technologies
with Zend OPcache v7.0.6-dev, Copyright (c) 1999-2015, by Zend Technologies
PHP FPM(在我的 apache 配置中服务 php)
PHP 5.6.17-0+deb8u1 (fpm-fcgi) (built: Jan 13 2016 09:10:13)
Copyright (c) 1997-2015 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2015 Zend Technologies
with the ionCube PHP Loader (enabled) + Intrusion Protection from ioncube24.com (unconfigured) v5.0.23, Copyright (c) 2002-2016, by ionCube Ltd.
with Xdebug v2.3.3, Copyright (c) 2002-2015, by Derick Rethans
with Zend OPcache v7.0.6-dev, Copyright (c) 1999-2015, by Zend Technologies
和 Apache(根据要求)
Server version: Apache/2.4.10 (Debian)
Server built: Nov 28 2015 14:05:48
Server's Module Magic Number: 20120211:37
Server loaded: APR 1.5.1, APR-UTIL 1.5.4
Compiled using: APR 1.5.1, APR-UTIL 1.5.4
Architecture: 64-bit
Server MPM: prefork
threaded: no
forked: yes (variable process count)
Server compiled with....
-D APR_HAS_SENDFILE
-D APR_HAS_MMAP
-D APR_HAVE_IPV6 (IPv4-mapped addresses enabled)
-D APR_USE_SYSVSEM_SERIALIZE
-D APR_USE_PTHREAD_SERIALIZE
-D SINGLE_LISTEN_UNSERIALIZED_ACCEPT
-D APR_HAS_OTHER_CHILD
-D AP_HAVE_RELIABLE_PIPED_LOGS
-D DYNAMIC_MODULE_LIMIT=256
-D HTTPD_ROOT="/etc/apache2"
-D SUEXEC_BIN="/usr/lib/apache2/suexec"
-D DEFAULT_PIDLOG="/var/run/apache2.pid"
-D DEFAULT_SCOREBOARD="logs/apache_runtime_status"
-D DEFAULT_ERRORLOG="logs/error_log"
-D AP_TYPES_CONFIG_FILE="mime.types"
-D SERVER_CONFIG_FILE="apache2.conf"
使用 Javascript 并在上传前检查文件大小,尽管这不是关于如何从服务器端中止上传的示例。
这是一个现场示例:http://codesheet.org/codesheet/E8ZwGzoV
<form method="post" target="submit" enctype="multipart/form-data" action="/post.php">
<input type="file" name="file" id="file" />
<input type="submit" name="submit" value="Submit" />
</form>
<div id="message"></div>
<iframe name="submit" width="100%" height="400px"></iframe>
JS在这里:
document.forms[0].addEventListener('submit', function( evt ) {
var file = document.getElementById('file').files[0];
// 1 MB = 1048576 this size is in bytes
if(file && file.size < 2097152) { // 2MB = 2097152
//Submit form
document.getElementById('message').innerHTML = 'The File '+bytesToSize(file.size)+' is OK!';
}
else
{
//Prevent default and display error
document.getElementById('message').innerHTML = 'The File '+bytesToSize(file.size)+' is to big!';
evt.preventDefault();
}
}, false);
function bytesToSize(bytes) {
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
if (bytes == 0) return '0 Bytes';
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i];
}