Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

> [php] pobieranie pliku w tle + buforowanie, Proszę o radę
Sztef89
post
Post #1





Grupa: Zarejestrowani
Postów: 60
Pomógł: 0
Dołączył: 6.12.2010

Ostrzeżenie: (0%)
-----


Chciałbym zrobić pobieranie plików z innych serwerów, mam kilka wątpliwości:
- Mój obecny serwer ma mało pamięci dlatego chce użyć buforowanego zapisu (kod poniżej), czy takie rozwiązanie jest wydajne, może jest jakieś lepsze ?
- Gdy plik się pobiera wisi wątek w przeglądarce i dopiero po pobraniu pliku skrypt się odwiesza. Zauważyłem, że jak wyłączę przeglądarkę to plik się dalej pobiera. Czy takie działanie skryptu podczas pobierania dużej ilości plików (używając tego samego skryptu) będzie wydajne ? kiedyś czytałem, że lepiej żeby skrypt działał w tle ale nie wiem jak to się robi,
- Chciałbym poniższy skrypt rozbudować o możliwość wznawiania pobierania gdy wystąpi błąd podczas pobierania (max 3 próby). Wiem, że trzeba użyć fseek ale nie za bardzo wiem gdzie i jak w tym kodzie go umieścić.

  1. <?php
  2.  
  3. $url = 'http://strona.pl/plik.zip';
  4. getUrlContents($url);
  5.  
  6. function getUrlContents($url)
  7. {
  8. $url_parsed = parse_url($url);
  9.  
  10. $host = $url_parsed["host"];
  11. if ($url == '' || $host == '') {
  12. return false;
  13. }
  14. $port = 80;
  15. $path = (empty($url_parsed["path"]) ? '/' : $url_parsed["path"]);
  16. $path.= (!empty($url_parsed["query"]) ? '?'.$url_parsed["query"] : '');
  17. $out = "GET $path HTTP/1.0\r\nHost: $host\r\nConnection: Close\r\n\r\n";
  18. $fp = fsockopen($host, $port, $errno, $errstr, 30);
  19. fwrite($fp, $out);
  20. $headers = '';
  21. $content = '';
  22. $buf = '';
  23. $isBody = false;
  24. while (!feof($fp) and !$isBody) {
  25. $buf = fgets($fp, 1024);
  26. if ($buf == "\r\n" ) {$isBody = true;}
  27. else{$headers .= $buf;}
  28. }
  29. $file1 = fopen(basename($url_parsed["path"]), 'w');
  30. $bytes=stream_copy_to_stream($fp,$file1);
  31. fclose($fp);
  32. return $bytes;
  33. }
  34.  
  35. ?>
Go to the top of the page
+Quote Post
 
Start new topic
Odpowiedzi (1 - 6)
Sephirus
post
Post #2





Grupa: Zarejestrowani
Postów: 1 527
Pomógł: 438
Dołączył: 28.06.2011
Skąd: Warszawa

Ostrzeżenie: (0%)
-----


ad 1. Co do wydajności to cięższa sprawa - wszystko zależy od ustawień nie tylko PHP ale i serwera, wielkości buforów i prędkości połączenie - im szybsze tym gorzej (szybciej docierają dane i zapychają pamięć) - może kod zamienić na CURL z CURLOPT_WRITEFUNCTION i spróbować z tego - to duże ułatwienie i łatwo można kontrolować pobieranie i zapisywanie pliku.

ad 2. Zapoznaj się z register_shutdown_function oraz ignore_user_abort ich odpowiednia kombinacja powinna pozwolić Ci na odpalenie skryptu w przeglądarce, zwrócenie przez niego wyniku i dalszej pracy. Pamiętaj tylko o zamykaniu sesji - aby sesja nie wisiała.

ad 3. Jeśli użyłbyś CURL'a to przy pomocy WRITE_FUNCTION i CURL_GETINFO (z tego co pamiętam) łatwo można wykryć czy plik pobrał się dobrze i zadziałać odpowiednio (IMG:style_emoticons/default/wink.gif)
Go to the top of the page
+Quote Post
Sztef89
post
Post #3





Grupa: Zarejestrowani
Postów: 60
Pomógł: 0
Dołączył: 6.12.2010

Ostrzeżenie: (0%)
-----


Cytat(Sephirus @ 28.02.2012, 14:33:25 ) *
ad 1. Co do wydajności to cięższa sprawa - wszystko zależy od ustawień nie tylko PHP ale i serwera, wielkości buforów i prędkości połączenie - im szybsze tym gorzej (szybciej docierają dane i zapychają pamięć) - może kod zamienić na CURL z CURLOPT_WRITEFUNCTION i spróbować z tego - to duże ułatwienie i łatwo można kontrolować pobieranie i zapisywanie pliku.

ad 2. Zapoznaj się z register_shutdown_function oraz ignore_user_abort ich odpowiednia kombinacja powinna pozwolić Ci na odpalenie skryptu w przeglądarce, zwrócenie przez niego wyniku i dalszej pracy. Pamiętaj tylko o zamykaniu sesji - aby sesja nie wisiała.

ad 3. Jeśli użyłbyś CURL'a to przy pomocy WRITE_FUNCTION i CURL_GETINFO (z tego co pamiętam) łatwo można wykryć czy plik pobrał się dobrze i zadziałać odpowiednio (IMG:style_emoticons/default/wink.gif)


1. Coś czułem ze ta funkcja nie jest najlepsza (IMG:style_emoticons/default/smile.gif) Ale testowałem i działa bardzo dobrze, pobrałem nią paręnaście GB i nie było żadnych problemów tylko niepotrzebnie skrypt wisi w przeglądarce w trakcie pobierania

2. Ok zapoznam się - mam nadzieje że dam rade, dzięki ! (IMG:style_emoticons/default/smile.gif)

3. Czyli najlepszym rozwiązaniem byłoby użycie cURLa do pobierania pliku ? Robię w cURL logowanie aby pobrać plik więc by było niemal 2 w jednym: logowanie i pobieranie przez cURL. Czyli dzięki tym dwóm funkcjom WRITE_FUNCTION i CURL_GETINFO można sprawdzić czy dobrze się pobrał plik, a jeśli nie to wznowić pobieranie - od tego miejsca co się zdążyło pobrać ?

A jeszcze jedno: czy curl ładuje pobierany plik do pamięci czy na dysk serwera ? To jest bardzo dla mnie ważne bo pamięci serwer mój ma bardzo mało, a transfer ok 5MB/s na pobieranie

Ten post edytował Sztef89 28.02.2012, 14:53:15
Go to the top of the page
+Quote Post
Sephirus
post
Post #4





Grupa: Zarejestrowani
Postów: 1 527
Pomógł: 438
Dołączył: 28.06.2011
Skąd: Warszawa

Ostrzeżenie: (0%)
-----


Jeszcze raz (IMG:style_emoticons/default/tongue.gif)

ad 1. Tu mi chodzilo o to że możesz to sobie uprościć tylko (IMG:style_emoticons/default/wink.gif)

ad 2. (IMG:style_emoticons/default/wink.gif)

ad 3. W praktyce jest tak jak mówisz o ile serwer z którego pobierasz obsługuje pobieranie "kawałków" (CONTENT-RANGE - Http). Jeśli tak to po pobraniu sprawdzasz czy pobrane bajty równają się założonym (pobranym z nagłówka Content-Type poprzez Curl_getinfo) - jeśli się okażę że tak nie jest możesz spróbować pobierać od nowa lub wywołać plik od miejsca w którym się skończyło - właśnie poprzez odpowiednie ustawienie content-range w nagłówkach - jest o tym sporo w Internecie.

Co do dodatkowego pytania to nie jestem pewny więc musiałbyś to sprawdzić. Dodatkowo też zamiast CURL i FSOCKOPEN możesz pop orstu użyć FOPEN (IMG:style_emoticons/default/wink.gif) co może w niektórych aspektach być wygodniejsze a jest podobne do fsockopen (IMG:style_emoticons/default/wink.gif) Tam możesz dać odpowiedni nagłówek/nagłówki poprzez tzw context jako ostatni argument fopen (IMG:style_emoticons/default/wink.gif) Tu masz manual: stream_context_create.

Ze względu na to że masz mało pamieci - przetestowałbym na twoim miejscu wszystkie 3 opcje i wybrał najbardziej wydajną - na pewno curl sam w sobie dorzuca coś od siebie ale lepiej to sprawdzić (IMG:style_emoticons/default/smile.gif)

Zawsze też możesz ewentualnie posłużyć się komendami linuxa - wget itd... to powinno też nieźle działać (IMG:style_emoticons/default/wink.gif)
Go to the top of the page
+Quote Post
Sztef89
post
Post #5





Grupa: Zarejestrowani
Postów: 60
Pomógł: 0
Dołączył: 6.12.2010

Ostrzeżenie: (0%)
-----


OK, poradziłem sobie z logowaniem + pobieraniem pliku + praca skryptu w tle + wykrywaniem błędów podczas pobierania. Została jedna rzecz - wznawianie pobierania.

Specjalnie pogorszyłem sygnał sieci aby wystąpiły błędy podczas pobierania i w rezultacie pobiera się więcej niż plik powinien zajmować (dałem warunek w while że kończy pracę dopiero gdy plik zajmuje tyle samo co wielkośc pliku pobrana z serwera). Natomiast bez pętli while pobiera się do pewnego miejsca i kończy pracę. Przy silnym sygnale sieci plik pobiera się w całości - bez i z pętlą while, więc jest ok.

Powiedźcie mi czy jak chcę wznowić pobieranie to to w opcji CURLOPT_RANGE podaję bajty liczone od 0 czy od 1 ? trzeba zawsze obie wartości podawać, czyli X-Y czy wartość X wystarczy (czyli od którego bajta ma zacząć pobierać). Czytałem manuala ale nic o tym nie napisali :/ Dałem teraz wartości liczone od zera i pliki są pobierane prawidłowo ale chciałbym być pewien na 100%.

pod koniec whila, zaraz przed klamra zamykającą przypisuje nową wielkość pliku $this->file_new_size = filesize($this->file);, a jak widać po działaniu funkcji wygląda jakby przypisało tylko raz:
Próba 0, zakres: 0 - 6004979, w:3800095
Próba 1, zakres: 3800094 - 6004979, w:3800095
Próba 2, zakres: 3800094 - 6004979, w:3800095
Próba 3, zakres: 3800094 - 6004979, w:3800095
Ilość wszystkich prób: 4
Prędkość: 202643
Rozmiar pobieranego pliku: 6004980
Zapisano: 3800095 - powinno być 12442440

Te pogrubione wartości to ilość pobranych danych po wykonaniu zapytania cURL. Wartość "Zapisano" jest obliczana po wyjściu z while'a i jest znowu źle obliczana :/ Dlaczego funkcja filesize() źle oblicza wielkość pliku ?

Ten post edytował Sztef89 9.03.2012, 17:46:05
Go to the top of the page
+Quote Post
daniofantasy
post
Post #6





Grupa: Zarejestrowani
Postów: 30
Pomógł: 1
Dołączył: 14.06.2007
Skąd: Chesterfield UK

Ostrzeżenie: (0%)
-----


a myslales moze o odpaleniu calosci pod cronem?

aaa - moment - to jest w js - file_get_contents() w php i calosc sparsuj jsem - moze pod ajaxem?

Ten post edytował daniofantasy 9.03.2012, 18:07:37
Go to the top of the page
+Quote Post
Sztef89
post
Post #7





Grupa: Zarejestrowani
Postów: 60
Pomógł: 0
Dołączył: 6.12.2010

Ostrzeżenie: (0%)
-----


to jest PHP a nie js itd (IMG:style_emoticons/default/biggrin.gif)
CRON służy do automatycznego odpalania co pewien okres to mi nic nie da... zresztą błąd jest gdzie indziej jeden już znalazłem:
funkcja filesize() cachuje dane więc kolejne je odpalenia dają ten sam wynik i należało ten cache czyścic po każdym jej wywołaniu funkcją clearstatcache();

Teraz jak skrypt odpalam to rozmiar pliku jest zawsze prawidłowo odczytany ale cały czas pobiera się za dużo tak jakby te przedziały które ustawiam opcją CURLOPT_RANGE nie działały:

Próba 0, zakres: 0 - 6004979, zapisano:3518315
Próba 1, zakres: 3518314 - 6004979, zapisano:7242490
Ilość wszystkich prób: 2
Prędkość: 180443
Rozmiar pliku: 6004980
Zapisano: 7242490

Oto kawałek kodu skryptu, klasy FileWriteHandler nie zamieszczam bo tam błędu nie ma, plik się dobrze zapisuje - funkcja sprawdzona. Błąd musi być w kodzie poniżej i czuje, że w parametrach cURL'a - CURLOPT_RANGE?? hymm

//EDIT
Już sobie poradziłem, odpowiedź znalazłem na zagranicznych forach. cURL'owi należy podać tylko wielkość pliku w bajtach i to mu wystarczy aby plik dokończyć. Czyli nalezy zrobic taki warunek:


  1. #Zapis pliku
  2. while($this->file_new_size != $this->file_size){
  3. //>>>>tutaj ustawienia curla<<<<
  4.  
  5. if(file_exists($this->file)){
  6. curl_setopt($c, CURLOPT_RANGE, $this->file_new_size."-");
  7. }
  8. curl_exec($c);
  9. $this->file_new_size = filesize($this->file);
  10.  
  11. $i++;
  12.  
  13. //Warunek kiedy ma wyjść z while'a
  14. if(($this->file_new_size > $this->file_size)||$i>3)break;
  15. }
  16.  
  17. echo "<br>Ilość wszystkich prób: ".$i;
  18. echo "<br>Prędkość: ".curl_getinfo($c,CURLINFO_SPEED_DOWNLOAD);
  19. echo "<br>Rozmiar: ".$this->file_size;
  20. echo "<br>Zapisano: ".filesize($this->file);
  21. echo "<br>Ścieżka: ".$this->file;


Ten post edytował Sztef89 9.03.2012, 21:10:51
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
2 Użytkowników czyta ten temat (2 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Aktualny czas: 24.08.2025 - 07:31