|
|
|
| В книге "PHP 5 на примерах" в листинге 6.15 приведен код разбивки файла на части (код ниже). В следующем листинге 6.16 приведен код для склейки этих частей в новый файл.
При исполнении кода из второго листинга проявляется ошибка, грешашая на присутствие нулевых файлов.
Как оказалось, эти нулевые файлы и создаются кодом из первого листинга (что логично, ибо вытекает из математики).
У меня возникли два вопроса:
1. Почему все же возникают файлы с нулевым размером при разрезке файла и как от них избавится?
2. Для чего нужна строка из кода:
if((float)(filesize($fn)/$piece) - $count != 0) $count++;
|
Сам код из листинга:
//Имя файла
$fn = "clip2net082b.zip"; // размер этого файла: 1.511.371
//Задаем размер части для разбивки файла - 100.000 байт
$piece = 100000;
//Открываем файл в режиме чтения
$fp = fopen($fn, "r");
//Читаем содержимое файла в буфер
$bufer = fread($fp, filesize($fn));
//Закрываем файл
fclose($fp);
//Подсчитываем число частей, на которые должен быть разбит файл
$count = (int)filesize($fn)/$piece;
if((float)(filesize($fn)/$piece) - $count != 0) $count++;
//В цикле разбиваем содержимое файла в буфере на части
for($i=0; $i<$count; ++$i)
{
$part = substr($bufer, $i*$piece, $piece);
//Сохраняем текущую часть в отдельном файле
$fp = fopen("site.tm".$i, "w");
fwrite($fp, $part);
fclose($fp);
}
|
| |
|
|
|
|
|
|
|
для: baston
(02.03.2010 в 15:08)
| | Собственно, можно избавится от ошибки, если в коде второго листинга одновременно с проверкой на существование файла (части) проверять и на его размер:
if((file_exists($filename)) && (filesize($filename) != 0))
|
Но меня по-прежнему интересуют вопросы, изложенные в первом сообщении.
Да и еще смутило, что листинги 6.15 и 6.16 в результате приводят к ошибке... Вроде учебник, должен быть выверен.
Спасибо. | |
|
|
|
|
|
|
|
для: baston
(02.03.2010 в 15:08)
| | Очень странное поведение: перестал работать скрипт загрузки (код в первом сообшении). Пишет, что нет такого файла:
>Цитата
>Warning: fopen(clip2net082b.zip): failed to open stream: No such file or directory in Z:\home\test.ru
>\www\zagruzka_file.php on line 8 Warning: filesize(): stat failed for clip2net082b.zip in Z:\home\test.ru
>\www\zagruzka_file.php on line 10
При том, что вчера (впрочем, как и сегодня) этот файл лежал в той же папке, что и выполняемый скрипт...
Кто-то может пояснить, в чем может быть причина такого поведения?
P.S. Сервер (то есть Денвер) перезагружал. | |
|
|
|
|
|
|
|
для: baston
(02.03.2010 в 15:08)
| | 2. Для чего нужна строка из кода:
if((float)(filesize($fn)/$piece) - $count != 0) $count++;
- что бы не потерялся остаток файла.
Получиться, допустим, файл разбит на 10 частей по 100 килобайт и плюс еще одна часть - 20 кб в остатке. В итоге файл разбит на 11 частей | |
|
|
|
|
|
|
|
для: ДобрыйУхх
(03.03.2010 в 19:48)
| | Да, спасибо. Интуитивно я это сообразил, но хотелось уточнения.
Меня еще смущает, что создаются файлы с нулевым размером. Теоретически все верно, если файл "весит" полтора мегабайта, то деление на 10.000 приведет к 15 частям. Однако при реальном разделении 10 частей имеют размер, а 5 - имеют 0-й размер. И второй код из-за этого выдает ошибку (как исправить, я показал выше).
Впрочем, как учебный пример, код сойдет. Но не как реальный. Жаль... | |
|
|
|
|
|
|
|
для: baston
(02.03.2010 в 15:08)
| | >Как оказалось, эти нулевые файлы и создаются кодом из первого листинга (что логично, ибо вытекает из математики).
Такое вытекает лишь из незнания даже не математики, а арифметики.
Учитывая арифметику, можно добиться не только отсутствия томов с нулевым размером, но и сделать так, чтоб размер любого тома отличался от размера любого другого тома не более чем на 1 байт. | |
|
|
|
|
|
|
|
для: Trianon
(03.03.2010 в 22:37)
| | Это, наверное, камень в огород авторов данного листинга.
Если нет, то можно конкретно подсказать направление в арифметике для исправления кода?
Спасибо. | |
|
|
|
|
|
|
|
для: baston
(04.03.2010 в 07:50)
| | там нечего править.
Код нужно переписывать.
И с арифметикой это уже не связано.
Массовые операции выполнять следует так, чтобы не требовалось загружать данные большого объема в оперативную память.
Книга "php5 на примерах", насколько я помню, авторами лишь организована.
Сами примеры взяты из ответов этого форума.
Так что камень здесь в огороде чисто номинальный. | |
|
|
|
|
|
|
|
для: Trianon
(04.03.2010 в 08:40)
| | Понял, спасибо. | |
|
|
|
|
|
|
|
для: Trianon
(04.03.2010 в 08:40)
| | У меня получилось, как корректно переписать этот код.
Я заношу в память небольшие куски файла (а не весь файл).
Посмотрите пожалуйста и, если есть что поправить, сообщите. Спасибо.
//Сохраняем в переменную название файла
$fn = "clip2net082b.zip";
//Проверяем существование файла в текущем каталоге (местонахождение скрипта)
if (!is_file($fn)) { die("Файл \"$fn\" не существует"); }
//Определяем размер частей: не больше 102.400 байт (100 кб)
$piece = 102400;
//Получаем размер файла в байтах
$size = filesize($fn);
//Расчитываем количество частей, на которые необходимо разбить файл
$count = (int)$size/$piece;
//Открываем файл в режиме чтения (с проверкой на его существование)
if($fd = fopen($fn, "rb"))
{
//Пока не достигнем конца файла
while(!feof($fd))
{
for($i=0; $i<$count; $i++)
{
//Считываем в память определенное количество байт из основного файла
$buffer = fread($fd, $piece);
//Создаем файл-часть
$part = fopen("part.tm".$i, "w");
//Записываем в файл-часть данные из буфера
fwrite($part, $buffer);
//Закрываем файл-часть
fclose($part);
}
}
//Закрываем основной файл
fclose($fd);
}
|
P.S. Возможно проверок существования файла много... | |
|
|
|
|
|
|
|
для: baston
(04.03.2010 в 16:57)
| | сразу бросается в глаза ошибка в логике
Цикл у Вас один. А операторов цикла - два
while(!feof($fd))
for($i=0; $i<$count; $i++)
|
Один лишний. Результаты могут быть печальные.
Навскидку - затирание первого тома данными из хвостика последнего.
Кроме того Ваш код все равно читает в оперативную память объем целого тома.
Так что внутренний цикл таки нужен. Но с другой логикой и другим условием повтора.
Но вот за то, что Вы стали работать над алгоритмом - респект. | |
|
|
|
|
|
|
|
для: Trianon
(04.03.2010 в 22:06)
| | Я думал над тем, что по окончании работы скрипта в памяти остается все, что мы занесли туда.
Может быть, стоит очищать память после каждой итерации?
Жаль, мало знаний для соображения о том, какую иную логику здесь можно применить....
За критику отдельное большое спасибо. | |
|
|
|
|
|
|
|
для: baston
(05.03.2010 в 07:58)
| | просто Вы кладете данные в том за один раз.
Представьте себе карьер с песком (это Ваш большой файл), кучу самосвалов, которыми этот песок нужно вывезти (это Ваши тома) и экскаватор (это Ваш скрипт).
У автора скрипта из книги экскаватор имел ковш размером с весь карьер.
Один раз зачерпнув всё, он отсыпал каждому самосвалу в кузов.
У Вас экскаватор имеет ковш размером с кузов самосвала.
Зачерпывает из карьера, насыпает в самосвал, зачерпывает - насыпает в следующий, и т.д.
Обычно же экскаватор насыпает самосвалы в несколько шагов.
Это позволяет ему иметь относительнокомпактный (по сравнению с размерами самосвала) ковш.
Размер ковша в этой аналогии - в чистом виде объем оперативной памяти, потребляемой скриптом. | |
|
|
|
|
|
|
|
для: Trianon
(05.03.2010 в 10:42)
| | Честно говоря, бьюсь уже второй день над этой задачей и не продвинулся ни на йоту.
Если я правильно понял вашу мысль, вы предлагаете читать из большого файла по маленькому кусочку (скажем, 10 кб) и записывать этот кусочек в том, пока размер тома не станет равным, например, 100 кб.
Я перепробовал разные варианты цикла, но все стопорится зацикливанием и созданием одного большого файла.
Уже отчаяние берет и мозги кипят, как говорится...
Может быть, еще какие наводки дадите? Например, какими функциями ограничиться, какими циклами...
Мне сложно выйти за рамки, так как практики-то маловато.
Можно было бы воспользоваться функцией file_get_contents(), но она сродни функции file и в память получается тоже заносится много данных... Или я не прав? | |
|
|
|
|
|
|
|
для: baston
(07.03.2010 в 15:35)
| | Вот так сейчас неожиданно получилось (добавил функцию). Поправьте, если опять есть неоптимальности или ошибки. Спасибо.
//Сохраняем в переменную название файла
$fn = "clip2net082b.zip";
//Проверяем существование файла в текущем каталоге (местонахождение скрипта)
if (!is_file($fn)) { die("Файл \"$fn\" не существует"); }
//Получаем размер файла в байтах
$size = filesize($fn);
//Задаем размер считываемых данных за раз (не более 10 кб)
$read = 10240;
//Задаем максимальный размер тома (100 кб)
$tom = 102400;
//Определяем количество томов
$count = (int)$size/$tom; //~14 томов с хвостиком
//Увеличиваем количество томов, если есть хвостик
if((float)($size/$tom) - $count != 0) $count++;
//Определяем размер каждого тома
$sizetom = (int)$size/$count;
if((float)($size/$count) - $sizetom != 0) $sizetom++;
//Открываем файл в режиме чтения
$fd = fopen($fn, "rb");
//Функция чтения данных по 10 кб и записи их в конец файла
function readwrite($bigfile, $tomus, $maxsize)
{
for($i=0; $i<102400; $i+=10240)
{
$buf = fread($bigfile, 10240);
fwrite($tomus, $buf);
ob_clean();
}
}
for($i=0; $i<$count; $i++)
{
$part = fopen("part.tm".$i, "ab");
readwrite($fd, $part, $sizetom);
fclose($part);
}
//Закрываем основной файл
fclose($fd);
|
| |
|
|
|
|
|
|
|
для: baston
(07.03.2010 в 19:33)
| | подход верный, но обилие констант прямо в записи цикла показывает, что вот эти строки использоваться не будут.
//Задаем размер считываемых данных за раз (не более 10 кб)
$read = 10240;
//Задаем максимальный размер тома (100 кб)
$tom = 102400;
|
а стоит сделать, чтобы применялись в конечном итоге именно значения этих переменных.
Еще полезно знать, что округление от деления a на b вверх быстро считается так:
$ n = (int)(($a+$b-1)/$b); | |
|
|
|
|
|
|
|
для: Trianon
(08.03.2010 в 17:53)
| | Спасибо за подсказки.
Я, наконец, собрался с духом и вернулся к этому скрипту. Решил сделать всё простой функцией + использование вложенной функции. Не уверен, что это оптимально...
Однако столкнулся с тем, что у меня повторяются данные из первой функции во второй. Как это обойти - не соображу. Если можете, подскажите пожалуйста. Спасибо. Код ниже:
//Функция разбивки файла на части
function splitfile($fn)
{
//Проверяем существование файла
if(!is_file($fn)) {die("Файл не найден. Проверьте его существование.");}
//Определяем размер файла
$fs = filesize($fn);
//Задаем размер считываемых данных за раз (не более 10 кб)
$read = 10240;
//Задаем максимальный размер тома (100 кб)
$tom = 102400;
//Определяем количество томов и увеличиваем, если есть хвостик
$count = (int)(($fs+$tom-1)/$tom);
//Определяем размер каждого тома и увеличиваем, если есть хвостик
$sizetom = (int)(($fs+$count-1)/$count);
//Открываем файл для чтения
$fd = @fopen($fn, "rb");
//Вложенная функция чтения данных по 10 кб и записи их в конец файла
function readwrite($bigfile, $tomus, $maxsize, $tom = 102400, $read = 10240)
{
for($i=0; $i<$tom; $i+=$read)
{
$buf = fread($bigfile, $read);
fwrite($tomus, $buf);
ob_clean();
}
}
//Проходим циклом и создаем части
for($i=0; $i<$count; $i++)
{
$part = fopen("part.tm".$i, "ab");
readwrite($fd, $part, $sizetom);
fclose($part);
}
//Закрываем основной файл
fclose($fd);
}
splitfile("clip2net082b.zip");
|
| |
|
|
|
|
|
|
|
для: baston
(16.03.2010 в 12:46)
| | надо просто вызов записать полной формой
readwrite($fd, $part, $sizetomб $tom, $read); | |
|
|
|
|
|
|
|
для: Trianon
(16.03.2010 в 13:04)
| | Благодарю!
Таким образом, можно ли считать, что решение данной задачи с помощью этой функции является оптимальным решением и может использоваться в реальной ситуации? | |
|
|
|
|
|
|
|
для: baston
(16.03.2010 в 13:23)
| | Сравните.
Сделайте выводы. | |
|
|
|
|
|
|
|
для: Trianon
(05.03.2010 в 10:42)
| | Простите, что опять напоминаю о себе :)
Можете вы оценить финальное решение данной задачи (код выше в предпоследнем сообщении)?
А то варюсь в собственном соку...
Буду очень благодарен за критику и указание на ошибки.
С уважением. | |
|
|
|