Gdzieś w sieci spotkałem pytanie o treści: jak stworzyć plik o określonej wielkości (gdzie zawartość nie ma znaczenia) w PHP?. Pytanie proste, aczkolwiek znalazłem aż trzy możliwe sposoby na jego rozwiązanie.
1. dd
Systemy Uniksowe mają takie zgrabne narzędzie o nazwie dd. Wielokrotnie przydawało mi się, pomyślałem, że tym razem również z niego skorzystam.
Skrypt, generujący 500 MiB mieści się w jednej linijce i wygląda tak:
`dd if=/dev/urandom of=/tmp/sample.txt bs=1024 count=512000`;
Niestety to rozwiązanie jest najwolniejsze. Czas wykonania skryptu na mojej maszynie wirtualnej wyniósł: 67s
Nie jest to rozwiązanie w pełni PHPowe. Uznałem jednak, że można je tutaj dopisać, jako że w części wypadków ma się dostęp do powłoki systemowej i można korzystać z takich narzędzi.
2. Wypełnienie pliku
Zdaje się, że jest to najbardziej oczywisty sposób. Generujemy ciąg znaków odpowiedniej długości i zapisujemy to do pliku.
Najprostsza forma tego rozwiązania wygląda tak:
$size = 1024 * 1024 * 500;
file_put_contents('/tmp/sample.txt', str_repeat(' ', $size));
Czas wykonania: 10s
Całkiem nieźle, aczkolwiek jest jeden problem. Przedstawione rozwiązanie potrzebuje tyle wolnej pamięci ile chcemy danych ulokować w pliku (w tym przypadku było to 500MiB). Raczej kiepsko, aby skrypt potrzebował do wykonania tyle pamięci.
Rozwiązanie tego problemu jest proste – nie przechowywać w pamięci takiej ilości danych tylko od razu wrzucać je do pliku:
$size = 1024 * 1024;
$file = new SplFileObject('/tmp/sample.txt', 'w');
while($size--) {
$file->fwrite(str_repeat(' ', 500));
}
Czas wykonania: 7s
Zużyta pamięć: 9 MiB
3. fseek
Jakiś czas temu pisałem o podstawowych operacjach na plikach. Wspominałem tam o funkcji fseek, oraz jej specyficznym zachowaniu.
W tym przypadku fseek’a, można użyć do tego aby przesunąć wskaźnik pliku na określoną pozycję, zapisać do niej jeden znak, a resztę załatwi FS (puste miejsca zostaną wypełnione „śmieciami”).
Przykładowy skrypt:
<?php
$size = 1024 * 1024 * 500; // 500 MiB
$file = new SplFileObject('/tmp/sample.txt', 'w');
$file->fseek($size - 1);
$file->fwrite(' ');
Czas wykonania: 0.025s
Widać różnicę prawda? ;)