PHP / Javascript chunked upload: поврежденный файл IE9, если размер файла больше, чем upload_max_filesize или post_max_size

я использую Plupupload загружать файлы. если я попытаюсь загрузить exe с IE9 и размером файла все кончено upload_max_filesize или же post_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.zip также содержит поврежденный файл загрузки в plupload-2.1.2/examples/upload/file_54c4c1d05c2ef папка и файл, чтобы попытаться загрузить plupload-2.1.2/examples/TryMe.exe

Готовься к тесту (пользуюсь XAMPP версия 1.8.3):

  1. расстегнуть молнию plupload-2.1.2.zip в вашем htdocs
  2. задавать php.ini upload_max_filesize=22M post_max_size=22M (меньше, чем TryMe.exe размер файла 23 МБ), перезапустите 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.ini upload_max_filesize=24M post_max_size=24M (до размера файла TryMe.exe 23 МБ), перезапустите Apache
  8. Выберите файл в %YourHtdocs%/plupload-2.1.2/examples/TryMe.exe и загрузить
  9. иди в %YourHtdocs%/plupload-2.1.2/examples/upload/ и найти загруженный файл
  10. загруженный файл в порядке.

8

Решение

Что мы знаем, так это то, что неповрежденный файл разбивается на куски, каждый из которых имеет префикс HTTP multipart/form-data заголовок и Content-Disposition заголовок. Первый всегда разделен правильно, второй нет.
Это оставляет нам 3 возможности:

  1. По крайней мере один из заголовков поврежден при отправке файла.
  2. По крайней мере, один из заголовков поврежден после того, как он был отправлен браузером, но до того, как он был проанализирован PHP.
  3. Что-то идет не так, пока PHP обрабатывает запрос.

Причиной любого из вышеперечисленного может быть деструктивная фильтрация брандмауэром, антивирусом или любой другой службой, которая по какой-то причине чувствует необходимость перебрать сетевой трафик или активность ОЗУ / файловой системы. За 1. это также может быть ошибка в браузере / JavaScript / Flash / Silverlight / PlUpload. За 2. теоретически это может быть Apache, который что-то испортит, но это крайне маловероятно, поскольку он передает данные 1: 1 в PHP. Теперь для 3. мы не можем исключить ошибку в PHP, но это слишком маловероятно, так как здесь PHP является константой, и результаты могут отличаться в зависимости от разных браузеров. Но я могу себе представить, что PHP получает файл, сохраняет его вместе со вторым заголовком, затем файл блокируется, потому что какой-то сервис его фильтрует, фильтрация занимает много времени, потому что файл ненадежен и большой, PHP пытается удалить второй заголовок, но ему запрещен доступ. потому что фильтрация все еще продолжается, и в конце вы получаете файл с заголовком. Разные результаты в разных браузерах можно объяснить разными размерами блоков или просто производительностью браузера.

К сожалению, это всего лишь домыслы. Теперь, когда Microsoft сделала все возможное, чтобы максимально упростить версию 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 и загрузите TryMe.exe с IE9. Рядом с файлом upload.php должен находиться файл out.txt, содержащий все необходимые данные о запросах на загрузку файлов. Пожалуйста, загрузите этот файл куда-нибудь и дайте нам ссылку на него.

3

Другие решения

По умолчанию максимальный размер загружаемого файла PHP составляет 2 МБ.

Попробуйте обновить настройки php (php.ini):

upload_max_filesize = 20M
post_max_size = 22M

Больше информации: http://php.net/manual/en/ini.core.php#ini.upload-max-filesize а также http://php.net/manual/en/ini.core.php#ini.post-max-size

2