Yii Framework Forum: Usuwanie plików i download plików - Yii Framework Forum

Jump to content

Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic

Usuwanie plików i download plików Rate Topic: -----

#1 User is offline   robikon 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 32
  • Joined: 17-October 11

Posted 04 April 2012 - 06:34 AM

Witam.
Mam 2 pytania bo nijak nie mogę sobie poradzić, przejrzałem dużo postów ale nie znalazłem odpowiedniego rozwiązania.

1. W jaki sposób usunąć fizyczne plik z dysku wraz z usunięciem rekordu z bazy wykorzystując sposób jaki jest zawarty w standardowej procedurze usuwania rekordów w kontrolerze. W jednym polu tabeli mam nazwę tego pliku, np.: plik.zip

2. W jaki sposób mogę wykonać download plików tak aby adres pobieranego pliku nie był widoczny wprost. Tak jak powyżej nazwa pliku jest także w polu tabeli.
0

#2 User is offline   Mariusz W. 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 24
  • Joined: 15-December 11
  • Location:Poland/Warsaw

Posted 04 April 2012 - 06:54 AM

Witaj,

1) Klasa CActiveRecord ma metodę afterDelete(). Musisz w swoim modelu dodać tą metodę i w niej napisać to co ma się dziać po usunięciu rekordu z bazy.

2) To jest chyba proste :-)
Masz tabelę "files". W rekordzie o Id=1 masz pole z nazwą pliku "plik.zip".
Tworzysz np. akcję "download" w kontrolerze "FilesController", która przyjmuje parametr $id.
Wtedy sprawdzasz czy w bazie jest taki rekord, odczytujesz nazwę pliku i przesyłasz jego zawartość do przeglądarki (musisz dać też odpowiedni header, aby otworzyło się okno do zapisu pliku - sprawdź przykład na tej stronie:
http://php.net/manua...on.readfile.php)

Pozdrawiam Mariusz
Posted Image
Posted Image
0

#3 User is offline   aquasite.pl 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 18
  • Joined: 20-May 11

Posted 04 April 2012 - 10:55 AM

Co do odpowiedzi na drugie pytanie: readfile() to raczej zły pomysł. Może się sprawdzić dla jakichś małych plików (zdjęcia, pliki tekstowe) jednak przy większych plikach mogą być problemy z wydajnością i duże zużycie ramu.
Lepiej pomyśleć o wykorzystaniu modułu do Apache - X-Sendfile. Minimalnie obciąża serwer a w folderze z plikami można dodać .htaccess z "deny from all". Problem tylko jest taki, że trzeba ten moduł zainstalować na serwerze czyli trzeba mieć dostęp do serwera (np. serwer dedykowany). Na serwerach wirtualnych raczej X-sendfile się nie znajdzie.

Tutaj jest ładnie opisane:
http://www.yiiframew...b-applications/
Posted Image
Agencja interaktywna Itzen.pl
Informatyczne rozwiązania dla firm.
Programowanie, Pozycjonowanie, Testy A/B, Reklama.
0

#4 User is offline   robikon 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 32
  • Joined: 17-October 11

Posted 04 April 2012 - 02:28 PM

Problem nr 2 rozwiązałem w ten sposób, że:

- w kontrolerze dodałem akcję:

        public function actionDownload($id)
	{
	  $model=$this->loadModel($id);
	  
	  $file = Yii::app()->basePath . '/../images/' . $model->image;
	  
	  if (file_exists($file))
		{
			$fileDir=Yii::app()->basePath . '/../images/';
			Yii::app()->request->sendFile(
				$model->image,
				file_get_contents($fileDir . $model->image),
				$model->image
			);  
		}
		else
		{
			throw new CHttpException(404,'Żądany plik nie istnieje.');
			return $model;
		}
	}


+ uprawnienia do wykonania tej akcji w "public function accessRules()"

oraz w widoku częściowym dałem

<?php 
    echo CHtml::link("Pobierz plik", array('download', 'id'=>$data->id));
?>


Działa i chyba tak może zostać.

Pozostał jeszcze problem nr 1.
Mariusz W. pisze:

Quote

Klasa CActiveRecord ma metodę afterDelete(). Musisz w swoim modelu dodać tą metodę i w niej napisać to co ma się dziać po usunięciu rekordu z bazy.


ale czy nie potrzeba beforeDelete(), żeby chociaż do zmiennej przypisać jaki plik ma usunąć?
0

#5 User is offline   Mariusz W. 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 24
  • Joined: 15-December 11
  • Location:Poland/Warsaw

Posted 05 April 2012 - 02:37 AM

Witaj,
Nie trzeba, ponieważ metoda afterDelete jest wykonywana na konkretnej instancji modelu, który reprezentuje konkretny rekord. Wygląda to mniej więcej tak:
1) Tworzony jest model i do atrybutów pobierane są wartości z odpowiednich kolumn.
2) Wywoływana jest akcja usuwania rekordu
3) Po usunięciu rekordu w metodzie afterDelete masz dostęp do wartości atrybutów (np. $this->fileName)

Pozdrawiam Mariusz
Posted Image
Posted Image
0

#6 User is offline   robikon 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 32
  • Joined: 17-October 11

Posted 05 April 2012 - 05:40 AM

View PostMariusz W., on 05 April 2012 - 02:37 AM, said:

Witaj,
Nie trzeba, ponieważ metoda afterDelete jest wykonywana na konkretnej instancji modelu, który reprezentuje konkretny rekord. Wygląda to mniej więcej tak:
1) Tworzony jest model i do atrybutów pobierane są wartości z odpowiednich kolumn.
2) Wywoływana jest akcja usuwania rekordu
3) Po usunięciu rekordu w metodzie afterDelete masz dostęp do wartości atrybutów (np. $this->fileName)

Pozdrawiam Mariusz


Thx, za wyjaśnienie. Pomogło.
Dodałem w modelu i działa ładnie. Może komus sie przyda.

protected function afterDelete()
    {
		parent::afterDelete();
		
		$file = $this->logo;
		unlink(Yii::app()->basePath . '/../images/programs/' . $file);
    }

0

#7 User is offline   sidewinder 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 218
  • Joined: 08-July 09
  • Location:Poland

Posted 06 April 2012 - 02:09 AM

Witam,
z tym afterDelete, to nie jest taka pewna sprawa.
Problem polega na tym, że z bazy danych - jeżeli użytkownik w bazie ma odpowiednie uprawnienia i nie ma constraint'ów - rekord można zawsze usunąć bo nawet jeżeli odbywa się odczyt lub zapis to są to operacje atomowe. W przypadku usuwaniu pliku z dysku sprawa nie jest już taka pewna. Najprostszy przykład jest taki, że plik może być otwarty w innym skrypcie i usunięcie nie powiedzie się z powodu braku dostępu. Co wtedy? Rekord z bazy usunięty, a plik zostaje.
Lepszą opcją jest próba usunięcia pliku w beforeDelete. W przypadku niepowodzenia wyświetlasz błąd, w przypadku sukcesu usuwasz rekord z bazy danych. To czy użytkownik który łączy się z bazą ma uprawnienia i to czy gdzieś nie ma blokad zależy tylko i wyłącznie od projektu bazy danych i testów wykonanych przed usunięciem (chociażby w beforeDelete :) ).
---------------------------------------------------------------------
"Never memorize what you can look up in books."
Albert Einstein
0

#8 User is offline   Mariusz W. 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 24
  • Joined: 15-December 11
  • Location:Poland/Warsaw

Posted 06 April 2012 - 02:28 AM

Też zastanawiałem się nad tym, czy lepiej jest usuwać plik w beforeDelete czy afterDelete?

1) Przypadek pesymistyczny w beforeDelete:
usuwany jest plik, natomiast jest problem podczas usuwania rekordu z bazy.
Wtedy gdzieś w systemie nadal pojawia się odniesienie do tego pliku (bo przecież informacja nadal jest w bazie danych), ale nie można nic zrobić, ponieważ plik nie istnieje.

2) Przypadek pesymistyczny w afterDelete
usuwany jest rekord, natomiast wystąpił podczas usuwania pliku (np. tak jak pisałeś, inny skrypt działa na tym pliku). Nie ma wtedy problemu, że plik pokazuje się gdzieś tam w systemie. Natomiast mamy "porzucony" plik.
W tym przypadku można zapisać, że wystąpił taki problem do jakiegoś pliku, a pracownik monitorujący błędy, które występują w aplikacji wyłapie problem :-)

3) Przypadek eliminujący problem
Zapisywać pliki w bazie :-) Łatwiesza synchronizacja. Nie mam w tym zakresie odpowiedniego doświadczenia i wiedzy, ale pewnie baza jest wtedy bardziej obciążona. Jednak na pewno istnieją rozwiązania, które eliminują i ten problem (w końcu całe bazy są przechowywane także w plikach).
Posted Image
Posted Image
0

#9 User is offline   sidewinder 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 218
  • Joined: 08-July 09
  • Location:Poland

Posted 06 April 2012 - 05:02 AM

Przypadek 1: można wyeliminować poprzez odpowiedni test przed usunięciem pliku (na przykład sprawdzając czy istnieją rekordy które mogłyby zablokować usunięcie).
Przypadek 2: należy upewnić się, że plik będzie usuwany tylko w jednym miejscu. Operacje rename, unlink mogą wywalić błędy jeżeli np. pierwszy skrypt otworzy plik, drugi skrypt wywoła rename, pierwszy skrypt zamknie plik i wywoła rename albo unlink. Nawet wywołanie file_exist() tuż przed unlink/rename nic nie da, gdyż dalej będą to 2 wywołania nie stanowiące operacji atomowej i prowadzące do sytuacji wyścigu.
Przypadek 3: ok dla mało obciążonych serwisów. Oprócz konieczności przechowywanie wszystkich meta-danych (czas utworzenia, uprawnienia, właściciel itp) jest sprawa wydajności. Pliki z dysku nieraz są (w zależności od serwera) przesyłane asynchronicznie z dysku bezpośrednio do interfejsu sieciowego za pomocą sendfile(). W przypadku db, dochodzi mnóstwo operacji dyskowych.
No i sprawa pieniędzy. Porównaj ceny 1 GB powierzchni dyskowej i ceny 1GB w bazie danych.
Przeczytaj to: http://perspectives....nsOfPhotos.aspx
---------------------------------------------------------------------
"Never memorize what you can look up in books."
Albert Einstein
0

#10 User is offline   robikon 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 32
  • Joined: 17-October 11

Posted 06 April 2012 - 03:45 PM

O, niechcący wywołałem dyskusję.

Pkt. 3 - przechowywanie plików bezpośrednio w bazie wypróbowałem i jest OK w przypadku małych plików a w przypadku dużych obciąża bazę więc przyjąłem obecną metodę.
Pkt 1 lub 2 - chyba afterDelete jest bardziej bezpieczne niż beforeDelete, mimo iż może wystapić błąd że zostanie usunięty rekord z bazy a plik zostanie (w jednym momencie na rekordzie będzie wykonywana jedna operacja więc chyba nie grozi odwołanie do wykonania innej operacji w tym samym czasie).
0

Share this topic:


Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic

1 User(s) are reading this topic
0 members, 1 guests, 0 anonymous users