Странности через CGI аплоад с браузерами

kode

never knows best
Странности через CGI аплоад с браузерами

Мучал тут загрузку файлов, код:

index.html:
PHP:
<form target="upload_frame" enctype="multipart/form-data" action="./upload.cgi" method="post">
    <input id="image_file" type="file" class="file" name="image_file" >
    <input type="submit" class="submit" value="Upload">
</form>

<iframe id="upload_frame" width="50%" height="400" name="upload_frame"></iframe>
upload.cgi:
PHP:
#!/usr/local/bin/php
<?php
include("functions.php");

echo ("Content-type: text/plain\n\n");

set_time_limit(0);

$length = $_SERVER['CONTENT_LENGTH'];
$in = fopen("php://stdin","r");
$first = fread($in,1024);

list($headers,$data) = explode("\r\n\r\n",$first,2);
$length -= strlen($headers)+4; //Вычитаем заголовки
$headers = parseHeader($headers); //Парсим заголовки
$boundary_str = "\r\n".$headers['boundary']."--\r\n"; //Получаем "пограничную" строку
$length -= strlen($fucken_str); //Вычитаем её длину, в итого получаем чистый размер файла

$pointer = 0;

//Открываем файл для записи
if(!$file = fopen("uploads/".$headers['Content-Disposition']['filename'],"w+")){
	die("Cant open file");
}

$data = str_replace($boundary_str,'',$data); //это на тот случай если файл < 1024 байт
if(!$pointer = fwrite($file,$data)){
	die("fuck: ".$data);
}



while (!feof($in)) {

	if($pointer+1024 < $length){
		$data = fread($in,1024); //Спокойно читаем
	}else{ //Видимо последнее чтение
		echo "Final reading\n";
		$data = str_replace($boundary_str,'',fread($in,1024)); //Есть мегамалая вероятность (1 из 10000000) что можем удалить нето
	}
	
	$pointer += strlen($data);
	fwrite($file,$data);
	echo "Pointer: ",$pointer,"\n";
	flush();
}

fclose($file);
fclose($in);

$filelength = filesize("uploads/".$headers['Content-Disposition']['filename']);

echo "Lengths ",$filelength,"/",$length;

?>
При разгрузке файла больше ~5 Мб - браузер пишет что отсылает данные, но бесконечно - те просто подвисает на загрузку (те сам не глючит, но бесконечно загружает). Грешу на скрипт, потому-что если использовать перловые аналоги всё ок.

ЗЫ. Написано аляповато - но это просто чтобы поигратся.

-~{}~ 29.12.07 05:31:

Мазилла пишет "The connection was reset"

-~{}~ 29.12.07 05:31:

Мб размер буфера увеличить?

-~{}~ 29.12.07 20:13:

ап. странно что никто с таким не сталкивался
 

kode

never knows best
Автор оригинала: WP
max_input_time?
Нет, об этом я тоже позаботился и придусмотрел (тем более PHP работает через cli )

+ http://bugs.php.net/bug.php?id=37306

Причём загрузка обрывается ровно на 4 Мб

-~{}~ 05.01.08 02:28:

Это что-то в самом upload.cgi

-~{}~ 05.01.08 02:38:

Окей:
Если заменить
PHP:
while (!feof($in)) {

	if($pointer+1024 < $length){
		$data = fread($in,1024); //Спокойно читаем
	}else{ //Видимо последнее чтение
		echo "Final reading\n";
		$data = str_replace($boundary_str,'',fread($in,1024)); //Есть мегамалая вероятность (1 из 10000000) что можем удалить нето
	}
	
	//die($pointer);
	
	$pointer += strlen($data);
	fwrite($file,$data);
	echo "Pointer: ",$pointer,"\n";
}
на

PHP:
stream_copy_to_stream($in,$file,$length-$pointer);
То всё чики-пуки, но - мы не можем контролировать процесс загрузки :(

-~{}~ 05.01.08 03:12:

Решением оказалось:

PHP:
$remain = $length-$pointer;
$copy_chunk = 1024;

while ($remain) {
	if($remain<$copy_chunk){
		$copy_chunk = $remain;
	}
	
	$copied = stream_copy_to_stream($in,$file,$copy_chunk);
	$remain -= $copied;
	$pointer += $copied;	
	
}
-~{}~ 05.01.08 05:04:

Если интересно могу выложить все скрипты: реализована загрузка больших файлов + через AJAX показывает процесс загрузки, чтобы новичкам в пример :)

-~{}~ 05.01.08 06:09:

http://code.airnet.ru/sandbox/uploadtest/

-~{}~ 05.01.08 07:55:

Исходники открыл - грязновато ИМХО :) ибо quick & dirty
 

Beavis

Banned
че то не работает твой пример!! постоянно пишет .C not allowed, хотя я заливаю допустимые файлы
 

kode

never knows best
Как имя файла который заливаешь?

PHP:
$extension = array_pop(explode('.',$filename));
 

Beavis

Banned
да на всё что заливаю пишет State: Error: '.c' not allowed! или зависает на State: Upload...Starting
бред какой то
 

kode

never knows best
Дык имя файла какое? Из текста ошибки получается что ты пытаешся залить *.c файл. И что за браузер?
 

Beavis

Banned
да любое имя файла...
например
111111 или
msicuu2.exe или
vienna_wallpaper_COX.kz.rar или
%E2%EE%EF%F0%EE%F1%FB%20%EF%EE%20%D2%D1%CF.jpg

я х.з. откуда он берет расширение .С , но факт налицо...
браузер IE7
 

kode

never knows best
http://code.airnet.ru/sandbox/uploadtest/?debug

Попробуй залить и кинь сюда вывод из ифрейма.

ГЫ. Проверил через Konqueror - вобще жесть :) А вот через нормальные браузеры (Opera, Mozilla) всё работает нормально. IE - это ненормальный браузер :) У меня подозрение что IE опять как не все, это в традициях Некрософта положить на стандарты, вот и на MIME тут наверное тоже "положили". Хотя других вариантов не исключаю.
 

Beavis

Banned
-----------------------------7d82903220274
Content-Disposition: form-data; name="upload_file"; filename="C:\Documents and Settings\&#65408;&#33990;&#31455;\&#65424;&#29678;&#8568;&#38876; &#57709;&#40473;\msicuu2.exe"
Content-Type: application/octet-stream
Error: '.c' not allowed!
 

kode

never knows best
Автор оригинала: Beavis
-----------------------------7d82903220274
Content-Disposition: form-data; name="upload_file"; filename="C:\Documents and Settings\&#65408;&#33990;&#31455;\&#65424;&#29678;&#8568;&#38876; &#57709;&#40473;\msicuu2.exe"
Content-Type: application/octet-stream
Error: '.c' not allowed!
Всё пофиксил. Дело оказалось в том что IE посылает файл с полным путём у поль-ля.
 

Beavis

Banned
первый файл залил нормально, только в конце пишет finished и никуда не редиректит... а после заливки второго файла вообще написало Upload...Starting, но файл залился
 

dimagolov

Новичок
Автор оригинала: kode
Всё пофиксил. Дело оказалось в том что IE посылает файл с полным путём у поль-ля.
известная фишка. обычно плачат по тому, что все остальные это не делают, вернее что в JS нельзя это узнать при обработке формы.
 
Сверху