我想将大文件上传到我的服务器。我在上传之前拼接这些文件。 1MB块。如果上传了一个块,则该块将附加到该文件。 在我的本地服务器上一切正常,但如果我在我的Web服务器上测试此脚本(由Strato -.-托管),则每次服务器上的附加文件大到64MB时,进程都会退出内部服务器错误。 我认为它(当然?)是由于Strato的限制造成的。也许有记忆的东西,但我无法向自己解释为什么会发生这种情况。 这是脚本(PHP 5.6版):
$file = $_FILES['chunk'];
$server_chunk = fopen($uploadDir.$_POST['file_id'], "ab");
$new_chunk = fopen($file['tmp_name'], "rb");
while(!feof($new_chunk)) //while-loop is optional
{
fwrite($server_chunk, fread($new_chunk, 1024));
};
fclose($new_chunk);
fclose($server_chunk);
在我看来,这段代码中没有一行将文件加载到内存中导致此错误,或者是否有不同的行导致此错误?
我检查了服务器日志,但是如果发生此错误则没有条目。
我可以创建多个63MB文件。只有当文件超过64MB时,服务器才会中止。
更新
我编写了以下脚本来连接服务器上的filechunks和cat。
但我总是得到一个8192B文件。
这个脚本有问题吗?
$ command类似于:
/ bin / cat ../files/8_0 ../files/8_1 ../files/8_2 ../ files / 8_3
$command = '/bin/cat';
foreach($file_array AS $file_info)
{
$command = $command.' ../files/'.$file_info['file_id'].'_'.$file_info['server_chunkNumber'];
}
$handle1 = popen($command, "r");
$read = fread($handle1, $_GET['size']);
echo $read;
我检查了结果。 8192B文件中的字节与原始文件的开头完全相同。所以似乎有些东西......
更新: 我找到了this。
更新
$handle1 = popen($command, "r");
while(!feof($handle1))
{
$read = fread($handle1, 1024);
echo $read;
}
这样可行,我可以从句柄中逐段读取。但当然这种方式我会遇到超时限制。我怎样才能将文件传递给客户端?如果这个问题得到解答,我的所有问题都消失了;)
答案 0 :(得分:2)
(请参阅底部的更新)
内存限制错误看起来会有所不同(但您可以编写一个脚本,通过向不断增长的数组添加大对象来连续分配内存,并查看会发生什么)。此外,内存限制与PHP核心,脚本,结构,以及任何文件内容有关;即使附加到的文件被加载或计入内存限制(可能通过mmap
,即使它看起来很奇怪),也不太可能最多限制为64兆字节。
所以这看起来像对单个文件大小的文件系统限制。有几个云文件系统有这样的限制;但我知道在本地磁盘存储上没有这样的。我要求提供托管技术支持的线索。
我做了一些尝试:
仔细检查$uploadDir
,除非是您亲自指定的。
尝试使用与$uploadDir
不同的路径创建文件,除非肯定在同一个文件系统上
尝试检查错误:
if (!fwrite($server_chunk, fread($new_chunk, 1024))) {
die("Error writing: ");
}
对phpinfo()进行偏执检查以确保没有一些真正奇怪的奇怪,例如function overriding。您可以enumerating the functions进行调查并查看(可欺骗,是的,但不太可能)。
它看起来像文件大小限制,与PHP无关。同一主机的早期用户报告了32 Mb。请参阅here和here。那些人无法运行mysqldump
或tar
备份。它与PHP无直接关系。
您可以解决此问题,方法是将文件存储在块中,然后分几个部分下载,或者将Apache管道传递给cat
程序(如果可用)。
您将存储file123.0001,file123.0002,...,然后在下载时检查所有片段,发送相应的Content-Length
,为/bin/cat
构建命令行(如果可访问... )并将流连接到服务器。你可能仍然遇到时间限制,但值得一试。
示例:
<?php
$pattern = "test/zot.*"; # BEWARE OF SHELL METACHARACTERS
$files = glob($pattern);
natsort($files);
$size = array_sum(array_map('filesize', $files));
ob_end_clean();
Header("Content-Disposition: attachment;filename=\"test.bin\";");
Header("Content-Type: application/octet-stream");
Header("Content-Length: {$size}");
passthru("/bin/cat {$pattern}");
我测试了上面的内容,它从一堆10-Mb块中下载了一个120Mb文件,命令如zot.1,zot.2,...,zot.10,zot.11,zot.12( 是的,我最初没有使用natsort
)。如果我能找到时间,我将在具有限制网络的VM中再次运行它,这样脚本有10秒的时间限制,下载需要20秒。 可能 PHP 终止脚本直到passthru返回,因为我发现PHP的计时不是很直观。
以下代码的运行时间限制为三秒。它运行一个 4 秒的命令,然后将输出发送到浏览器,一直运行直到时间耗尽。
<pre>
<?php
print "It is now " . date("H:i:s") . "\n";
passthru("sleep 4; echo 'Qapla!'");
print "It is now " . date("H:i:s") . "\n";
$x = 0;
for ($i = 0; $i < 5; $i++) {
$t = microtime(true);
while (microtime(true) < $t + 1.0) {
$x++;
}
echo "OK so far. It is now " . date("H:i:s") . "\n";
}
结果是:
It is now 20:52:56
Qapla!
It is now 20:53:00
OK so far. It is now 20:53:01
OK so far. It is now 20:53:02
OK so far. It is now 20:53:03
( ! ) Fatal error: Maximum execution time of 3 seconds exceeded in /srv/www/rumenta/htdocs/test.php on line 9
Call Stack
# Time Memory Function Location
1 0.0002 235384 {main}( ) ../test.php:0
2 7.0191 236152 microtime ( ) ../test.php:9
当然,Strato可能会对脚本的运行时间进行更强有力的检查。另外,我安装了PHP作为模块;可能对于作为独立流程运行的CGI,适用不同的规则。