【问题标题】:PHP/Javascript chunked upload: IE9 corrupt file if filesize is over upload_max_filesize or post_max_sizePHP/Javascript 分块上传:如果文件大小超过 upload_max_filesize 或 post_max_size,则 IE9 损坏文件
【发布时间】:2015-01-17 11:12:44
【问题描述】:

我正在使用Plupupload 上传文件。如果我尝试使用 IE9 加载 exe 并且文件大小超过 upload_max_filesizepost_max_size 设置,则上传的文件已损坏。

这是我正在使用的 PHP 脚本:

<?php
/**
 * upload.php
 *
 * Copyright 2013, Moxiecode Systems AB
 * Released under GPL License.
 *
 * License: http://www.plupload.com/license
 * Contributing: http://www.plupload.com/contributing
 */

// Make sure file is not cached (as it happens for example on iOS devices)
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");

// 5 minutes execution time
@set_time_limit(5 * 60);

// Settings
$targetDir  = __DIR__ . DIRECTORY_SEPARATOR . "upload";

// Create target dir
if (!file_exists($targetDir)) {
    @mkdir($targetDir);
}

// Get a file name
if (isset($_REQUEST["name"])) {
    $fileName = $_REQUEST["name"];
} elseif (!empty($_FILES)) {
    $fileName = $_FILES["file"]["name"];
} else {
    $fileName = uniqid("file_");
}

$filePath = $targetDir . DIRECTORY_SEPARATOR . $fileName;

// Chunking might be enabled
$chunk  = isset($_REQUEST["chunk"])  ? intval($_REQUEST["chunk"])  : 0;
$chunks = isset($_REQUEST["chunks"]) ? intval($_REQUEST["chunks"]) : 0;


// Open temp file
if (!$out = @fopen("{$filePath}.part", $chunks ? "ab" : "wb")) {
    die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}');
}

if (!empty($_FILES)) {
    if ($_FILES["file"]["error"] || !is_uploaded_file($_FILES["file"]["tmp_name"])) {
        die('{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file."}, "id" : "id"}');
    }

    // Read binary input stream and append it to temp file
    if (!$in = @fopen($_FILES["file"]["tmp_name"], "rb")) {
        die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');
    }
} else {    
    if (!$in = @fopen("php://input", "rb")) {
        die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');
    }
}

while ($buff = fread($in, 4096)) {
    fwrite($out, $buff);
}

@fclose($out);
@fclose($in);

// Check if file has been uploaded
if (!$chunks || $chunk == $chunks - 1) {
    // Strip the temp .part suffix off 
    rename("{$filePath}.part", $filePath);
}

// Return Success JSON-RPC response
die('{"jsonrpc" : "2.0", "result" : null, "id" : "id"}');

通过html页面上传:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>

<title>Plupload - Custom example</title>

<!-- production -->
<script type="text/javascript" src="../js/plupload.full.min.js"></script>

</head>
<body style="font: 13px Verdana; background: #eee; color: #333">

<h1>Custom example</h1>

<p>Shows you how to use the core plupload API.</p>

<div id="filelist">Your browser doesn't have Flash, Silverlight or HTML5 support.</div>
<br />

<div id="container">
    <a id="pickfiles" href="javascript:;">[Select files]</a> 
    <a id="uploadfiles" href="javascript:;">[Upload files]</a>
</div>

<br />
<pre id="console"></pre>


<script type="text/javascript">
// Custom example logic

var uploader = new plupload.Uploader({
    runtimes : 'html5,flash,silverlight,html4',
    browse_button : 'pickfiles', // you can pass in id...
    container: document.getElementById('container'), // ... or DOM Element itself
    url : 'upload.php',
    flash_swf_url : '../js/Moxie.swf',
    silverlight_xap_url : '../js/Moxie.xap',
    chunk_size : '2mb',

    filters : {
        max_file_size : '100mb',
        mime_types: [
            {title : "Image files", extensions : "jpg,gif,png"},
            {title : "Zip files", extensions : "zip"},
            {title : "Exe files", extensions : "exe"}
        ]
    },

    init: {
        PostInit: function() {
            document.getElementById('filelist').innerHTML = '';

            document.getElementById('uploadfiles').onclick = function() {
                uploader.start();
                return false;
            };
        },

        FilesAdded: function(up, files) {
            plupload.each(files, function(file) {
                document.getElementById('filelist').innerHTML += '<div id="' + file.id + '">' + file.name + ' (' + plupload.formatSize(file.size) + ') <b></b></div>';
            });
        },

        UploadProgress: function(up, file) {
            document.getElementById(file.id).getElementsByTagName('b')[0].innerHTML = '<span>' + file.percent + "%</span>";
        },

        Error: function(up, err) {
            document.getElementById('console').innerHTML += "\nError #" + err.code + ": " + err.message;
        }
    }
});

uploader.init();

</script>
</body>
</html>

exe 损坏时,如果我尝试用notepad++ 打开它们,我会发现:

我的设置:

PHP Version 5.5.9
System          Windows NT PC-XXX 6.0 build 6002 (Windows Vista Service Pack 2) i586 
Compiler        MSVC11 (Visual C++ 2012) 
Architecture    x86 
Server API      Apache 2.0 Handler 

php.ini

max_execution_time=30
max_input_time=60
memory_limit=128M
max_file_uploads=20

附加信息

  1. 所有 Plupupload 方法(html5、flash、silverlight、html4)都有问题
  2. 已禁用防病毒软件
  3. UAC 已禁用

尝试自己发行

我已经为任何想尝试的人创建了一个包。

下载包:http://www.sndesign.it/shared/stackoverflow/plupload-2.1.2.zip

我的plupload-2.1.2.zipplupload-2.1.2/examples/upload/file_54c4c1d05c2ef 文件夹中还包含一个损坏的上传文件,以及要尝试上传plupload-2.1.2/examples/TryMe.exe 的文件

准备测试(我使用XAMPP Version 1.8.3):

  1. 在你的htdocs中解压plupload-2.1.2.zip
  2. 设置php.iniupload_max_filesize=22Mpost_max_size=22M(小于TryMe.exe文件大小23MB),重启Apache
  3. 打开 IE9(IE9 总是失败),然后转到:http://localhost/plupload-2.1.2/examples/custom.html
  4. 选择%YourHtdocs%/plupload-2.1.2/examples/TryMe.exe中的文件并上传
  5. 进入%YourHtdocs%/plupload-2.1.2/examples/upload/,找到上传的文件
  6. 上传的文件已损坏。
  7. 设置php.iniupload_max_filesize=24Mpost_max_size=24M(最大TryMe.exe文件大小23MB),重启Apache
  8. 选择%YourHtdocs%/plupload-2.1.2/examples/TryMe.exe中的文件并上传
  9. 进入%YourHtdocs%/plupload-2.1.2/examples/upload/,找到上传的文件
  10. 上传的文件没问题。

【问题讨论】:

  • @maytham 用于收集由开发人员上传的独立软件的服务。
  • @maytham 您遇到了什么问题?我用图片和 zip 试了一下,效果很好
  • @maytham 我什至在 github plupupload project 上也报告了这个问题。敬请期待,也许有人找到解决方案...
  • @maytham 我开始赏金了。如果你对这个问题感兴趣,你可以添加赏金或投票给这个问题以提高知名度。
  • 每次使用一种上传方式(html5、flash等)定位您的问题

标签: javascript php plupload chunked-encoding


【解决方案1】:

我们所知道的是,一个完整的文件被分割成多个块,每个块都以 HTTP multipart/form-data 标头和 Content-Disposition 标头作为前缀。第一个总是正确剥离,第二个不是。
这给我们留下了 3 种可能性:

  1. 发送文件时至少有一个标头损坏。
  2. 至少有一个标头在被浏览器发送之后但在被 PHP 解析之前已损坏。
  3. PHP 解析请求时出现问题。

上述任何情况的原因都可能是防火墙、防病毒软件或任何其他服务出于某种原因认为需要检查您的网络流量或 RAM/文件系统活动的破坏性过滤。对于1.,它也可能是浏览器/JavaScript/Flash/Silverlight/PlUpload 引擎中的错误。对于 2.,理论上可能是 Apache 搞砸了,但这极不可能,因为它将数据 1:1 传递给 PHP。现在对于 3.,我们不能排除 PHP 中的错误,但这种可能性极小,因为 PHP 在这里是一个常量,并且结果因不同的浏览器而异。但我可以想象 PHP 收到文件,将其与第二个标头一起保存,然后文件锁定,因为某些服务正在过滤它,过滤需要很长时间,因为文件不受信任且很大,PHP 尝试删除第二个标头但被拒绝访问因为过滤仍在进行,最后你会得到一个带有标题的文件。不同浏览器的不同结果可以由不同的块大小或浏览器性能来解释。

很遗憾,这一切都只是猜测。现在微软尽最大努力让IE降级变得尽可能困难,我目前无法用IE9测试它,我只能给你一些调试说明:

在你的 php.ini 中,设置

enable_post_data_reading = Off

这将完全中断该服务器上的所有 POST 请求,但它允许您读取和转储文件上传请求。

在您的 upload.php 中,在任何其他代码之前添加这两行:

file_put_contents('out.txt', print_r(getallheaders(), true).PHP_EOL.substr(file_get_contents('php://input'), 0, 1000), FILE_APPEND);
exit;

启动 Apache 并使用 IE9 上传 TryMe.exe。现在,您的 upload.php 旁边应该是一个 out.txt 文件,其中包含有关文件上传请求的所有相关数据。请将该文件上传到某个地方并给我们一个链接。

【讨论】:

  • 感谢您的帮助,您可以下载输出文件sndesign.it/shared/stackoverflow/out.txt
  • 嗯,this 是 IE11 的结果,请注意 chunkchunks 部分是如何丢失的?上传适用于 IE9 的文件时,您能给我一个转储吗?
  • 有多小?小于2MB?还是 8KB?
  • 如果文件大小超过upload_max_filesizepost_max_size则失败
  • 但它会发生在已签名的 exe、未签名的 exe 和任何其他文件类型上?
【解决方案2】:

默认情况下,PHP 最大上传文件大小设置为 2MB。

尝试更新您的 php 设置 (php.ini):

upload_max_filesize = 20M
post_max_size = 22M

更多信息:http://php.net/manual/en/ini.core.php#ini.upload-max-filesizehttp://php.net/manual/en/ini.core.php#ini.post-max-size

【讨论】:

  • 注意,设置生效前需要重启apache服务器(如果还没有)
  • 啊,我在您的帖子中也注意到您在实例化 plupload 时没有定义“chunk_size”。
  • 您是否尝试过使用不同的大型 exe 文件(可能来自 Microsoft 的安全文件)?您是否还知道您是否有一些可能会影响此的后台进程(防病毒、作业等)?我只是想把这些排除在外......
  • 我的意思是尝试一个大的 Microsoft exe 文件,我认为可以安全地假设除了大于 2MB 的 exe 文件之外的其他文件类型也可以工作
  • 确实,如果将值增加超过要上传的文件大小,它就可以工作。但是,它不应该那样工作。我删除了我的其他 cmets,因为它们是错误的。直到现在我才确切地发现了问题。
猜你喜欢
  • 1970-01-01
  • 2023-04-08
  • 1970-01-01
  • 1970-01-01
  • 2017-08-29
  • 2020-06-18
  • 1970-01-01
  • 1970-01-01
  • 2021-07-19
相关资源
最近更新 更多