jQuery File Upload – asynchroniczna, masowa wysyłka plików

Ostatnio, w jednym z projektów miałem potrzebę wysłania na serwer wielu plików na raz oraz wysyłania dużych plików (np. o objętości 50 MB). Standardowe rozwiązania takie, jak synchroniczne wysyłanie plików nie zdają egzaminu. W przypadku PHP, standardowy czas wykonywania skryptu, to zazwyczaj 30 sekund, więc gdy wyślemy sporo danych w tradycyjny sposób, nastąpi zerwanie połączenia. Możemy wprawdzie skorzystać z takich rozwiązań, jak applet w języku Java, ale wtedy zmuszamy użytkownika do instalacji i uruchamiania maszyny wirtualnej Javy. Kolejnym dostępnym rozwiązaniem jest SWF File Upload, ale jesteśmy wtedy zmuszani do używania technologii Flash, która już właściwie wychodzi z obiegu w świecie aplikacji internetowych. Ponadto, implementacja tego rozwiązania może być nieco problematyczna.

Z pomocą przychodzi nam jQuery File Upload. Jest to open-sourcowy projekt oparty na jQuery, który umożliwia asychnroniczne wysyłanie plików na serwer bez konieczności używania Flasha. Wszystko jest w pełni oparte o HTML5 i JavaScript.

Mamy tutaj możliwość wysłania wielu plików na raz i śledzenia postępu wysyłania na paskach postępu. Dodatkowo, jest dostępna bardzo dobra dokumentacja i gotowe przykłady użycia dla PHP, Pythona, Ruby on Rails, Javy i nawet Node.js. W dokumentacji znajdziemy też implementacje pluginu w różnych frameworkach przygotowane przez użytkowników.

Implementacja jQuery File Upload we własnych projektach nie jest trudna. Czasem warto trochę dokładniej przejrzeć dokumentację oraz API, a także pobawić się tym trochę, aby zrozumieć niektóre mechanizmy.

Przykład z demo jest dość rozbudowany, ale tak naprawdę, aby wywołać aplikację JavaScript, poza zawarciem niezbędnych plików oraz aplikacji server-side, wystarczy podać następujące polecenie:

Gdybyśmy chcieli wywołać jakąś operację, gdy wszystkie pliki zostaną wysłane na serwer (np. wyświetlić komunikat lub przekierować na inną stronę), możemy to zrobić w następujący sposób:

Jeśli chcemy odwoływać się do innych zdarzeń lub napisać bardziej skomplikowaną aplikację, warto, abyśmy zajrzeli do dokumentacji.

Gdy chcemy wysyłać duże pliki, w przypadku serwera Apache i języka PHP, należy ustawić odpowiednie parametry w pliku .htaccess dla naszej aplikacji.

php_value upload_max_filesize 50M
php_value post_max_size 50M

Możemy też odpowiednio zmodyfikować plik php.ini lub pominąć ten krok, gdy serwer jest już odpowiednio skonfigurowany.

Moim zdaniem, jQuery File Upload, to obecnie najlepsze i najlepiej dopracowane rozwiązanie do asynchronicznego i masowego wysyłania wielu (także dużych) plików na serwer.

Posted in JavaScript, jQuery, Technicznie | Leave a comment

Opóźnione ładowanie

Wzorzec opóźnionego ładowania jest bardzo przydatnym wzorcem, który możemy wykorzystać w sytuacji, gdy chcemy załadować jakieś dane posiadając niepełne informacje lub w sytuacji, gdy chcemy konkretne informacje załadować na żądanie. Ten wzorzec posiada różne wariacje takie, jak chociażby opóźniona inicjalizacja (lazy initialization), wirtualny pośrednik (virtual proxy), uchwyt wartości (value holder), czy widmo (ghost). Zdarzyło mi się wykorzystać ten wzorzec podczas tworzenia aplikacji internetowych.

Autouzupełnianie

Dobrym przykładem wykorzystania tego wzorca jest tzw. autouzupełnianie w formularzach wyszukiwania. Mamy z tym do czynienia na co dzień chociażby w wyszukiwarce Google, gdzie podczas wprowadzania frazy, Google wyświetla nam podpowiedzi. Można tę metodę wykorzystać też w mniejszych projektach, gdzie mamy do dyspozycji sporą bazę danych (tabele w bazie danych), a nie możemy jej załadować w całości w trakcie jednego żądania, gdyż ogranicza nas pamięć operacyjna komputera i prędkość łącza. Poza tym, ten proces trwałby bardzo długo. Autouzupełnianie zostało zaimplementowane w jQuery UI. Przykład jego wykorzystania z użyciem zewnętrznego źródła danych w formacie JSON, można zobaczyć na stronie: http://jqueryui.com/demos/autocomplete/#remote-jsonp . Warto się z nim zapoznać. Wykorzystałem tę metodę podczas podpowiadania nazw portów lotniczych w wyszukiwarce lotów.

Diagram sekwencji opisujący tę operację wygląda następująco:

Opóźnione ładowanie map Google

Napotkałem sytuację, w której musiałem załadować na jednej stronie kilkanaście map Google. Podczas normalnego, synchronicznego ładowania strony, zwykła przeglądarka internetowa w takiej sytuacji po prostu się zawiesi. Warto więc uaktywniać poszczególne mapy w razie potrzeby, tj. na żądanie, aby nie ładować zbyt wielu danych na raz. W praktyce dla każdej z map musimy oddzielnie załadować skrypt API z serwerów Google. Bardzo dobry wpis na ten temat (w języku angielskim) znajduje się na stronie: http://blog.liip.ch/archive/2008/04/22/lazy-load-google-maps.html. Dla własnych potrzeb, stworzyłem modyfikację metody On demand lazy load. W swoim projekcie chciałem załadować listę lotów wygenerowanych przez wyszukiwarkę połączeń lotniczych. Przy każdym locie stworzyłem możliwość wyświetlenia mapy, na której jest zaprezentowany poglądowy plan lotu. Fragment kodu, który stworzyłem w języku JavaScript jest zaprezentowany poniżej (część danych jest generowana w PHP) . Kod jest wyrwany z kontekstu, ale widać w nim ideę, o którą mi chodzi.

<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript">
	<?php foreach ($loty->route as $travel):?>

		<?php if( ($loty->query->from == $travel->from) && $travel->f_id == 1 ):?>
			function loadMapScript_wylot_<?php echo $travel->id;?>() {
				var script = document.createElement("script");
				script.type = "text/javascript";
				script.src = "http://maps.google.com/maps/api/js?sensor=false&async=2&callback=loadMap_wylot_<?php echo $travel->id;?>";
				document.body.appendChild(script);
			}

			function loadMap_wylot_<?php echo $travel->id;?>() {
				var myLatlng = new google.maps.LatLng(30.0, 0.0);
				var myOptions = {
				  zoom: 2,
				  center: myLatlng,
				  mapTypeId: google.maps.MapTypeId.ROADMAP
				}
				var map = new google.maps.Map(document.getElementById("Wylot_<?php echo $travel->id;?>"), myOptions);

				<?php if($podrozWylot[$travel->id]):?>
					<?php foreach ($podrozWylot[$travel->id] as $value):?>
						new google.maps.Marker({
						position: new google.maps.LatLng(<?php echo $value->location;?>),
						map: map,
						icon: '<?php echo url::base();?>resources/images/main/google-maps-icon_airport.png',
						title: '<?php echo $value->airport_name.', '.$value->city.', '.$value->country;?>'
						});
					<?php endforeach;?>
				<?php endif;?>

				<?php if($podrozWylot[$travel->id]):?>
					var flightPlanCoordinates = [
												<?php foreach ($podrozWylot[$travel->id] as $value):?>
												 new google.maps.LatLng(<?php echo $value->location;?>),
												 <?php endforeach;?>
												];

					var flightPath = new google.maps.Polyline({
										 path: flightPlanCoordinates,
										 strokeColor: "#f70",
										 strokeOpacity: 1.0,
										 strokeWeight: 2,
										 geodesic: true
									 });

					flightPath.setMap(map);
				<?php endif;?>
			}

			$(document).ready(function() {
				$('#MapClick<?php echo $travel->id;?>_wylot').click(function() {
					if($('#Wylot_<?php echo $travel->id;?>').is(':hidden'))
					{
						$('#Wylot_<?php echo $travel->id;?>').slideDown('fast', function() {
							loadMapScript_wylot_<?php echo $travel->id;?>();
							$('#MapClick<?php echo $travel->id;?>_wylot').text("<?php echo Kohana::lang('includes/body/content/data/wyswietl/loty.schowajmape')?>");
						});
					}
					else
					{
						$('#Wylot_<?php echo $travel->id;?>').slideUp('fast', function() {
							$('#MapClick<?php echo $travel->id;?>_wylot').text("<?php echo Kohana::lang('includes/body/content/data/wyswietl/loty.wyswietlmape')?>");
						});
					}
				});
			});
		<?php endif;?>

	<?php endforeach;?>
</script>

Następnie w kodzie HTML, w pętli mam następujący kod:

<?php
echo '<div id="MapClick'.$travel->id.'_wylot" class="MapClick">wyświetl mapę</div>';
echo '<div id="Wylot_'.$travel->id.'" class="MapArea"></div>';
?>

Po kliknięciu w element z odpowiednim identyfikatorem, we właściwej warstwie, aktywna mapa jest wyświetlana na żądanie.

Posted in Google Maps, JavaScript, PHP, Technicznie, Wzorce projektowe | Leave a comment

Dodawanie odwróconych liczb

Wprowadzenie

W tym wpisie przedstawię moją propozycję algorytmu dodawania odwróconych liczb.

Problem jest opisany w języku angielskim na stronie: http://www.spoj.pl/problems/ADDREV/ i pojawił się on podczas Europejskiego Konkursu Programistycznego w Pradze w 1998 roku (ACM Central European Programming Contest, Prague 1998).

Ogólny opis problemu (w języku angielskim)

Input

The input consists of N cases (equal to about 10000). The first line of the input contains only positive integer N. Then follow the cases. Each case consists of exactly one line with two positive integers separated by space. These are the reversed numbers you are to add.

Output

For each case, print exactly one line containing only one integer – the reversed sum of two reversed numbers. Omit any leading zeros in the output.

Example

Sample input:

3
24 1
4358 754
305 794

Sample output:

34
1998
1

W skrócie program powinien działać tak:

  1. W pierwszej linii na wejściu podajemy informację o tym, ile linii będziemy przetwarzać.
  2. W kolejnych liniach pojawiają się liczby rozdzielone odstępem (spacją), w których należy odwrócić kolejność cyfr, a następnie je do siebie dodać.
  3. W kolejnym kroku należy odwrócić kolejność cyfr w wygenerowanym wyniku i wypisać go na wyjście w kolejnych liniach.

Propozycja rozwiązania problemu w języku Ruby

def addrev(n)
	b = 0
	n.split(' ').each do |a|
		b+= a.to_s.reverse.to_i
	end
	return b.to_s.reverse.to_i
end

a = true
i = j = 0
out = []

$stdin.each_line do |line|
	if(a) then
		i = line.to_i
		a = false
	else
		if(j != i)
			out.push(addrev(line.to_s))
			j += 1
		else
			break
		end
	end
end

out.each do |b|
	$stdout << b << "\n"
end

 

Posted in Algorytmy, Ruby, Technicznie | Leave a comment

Odwrotna Notacja Polska

Wprowadzenie

Za Wikipedią:

Odwrotna notacja polska (ONP, ang. Reverse Polish Notation, RPN) – jest sposobem zapisu wyrażeń arytmetycznych, w którym znak wykonywanej operacji umieszczony jest po operandach (zapis postfiksowy), a nie pomiędzy nimi jak w konwencjonalnym zapisie algebraicznym (zapis infiksowy) lub przed operandami jak w zwykłej notacji polskiej (zapis prefiksowy). Zapis ten pozwala na całkowitą rezygnację z użycia nawiasów w wyrażeniach, jako że jednoznacznie określa kolejność wykonywanych działań.

Mówiąc prościej, jest to po prostu zapis działań arytmetycznych, w których chcemy określić własną kolejność wykonywania działań, ale bez użycia nawiasów.

Przykładowo wyrażenie:

(2+3)*5

W Odwrotnej Notacji Polskiej będzie zapisane następująco:

2 3 + 5 *

Ktoś mógłby się zapytać: Po co nam to?

Obecnie w wielu intepreterach (np. Python lub Ruby), programach inżynierskich takich, jak np. Matlab lub zaawansowanych kalkulatorach naukowych mamy możliwość wykorzystywania nawiasów. Po co więc korzystać z ONP, skoro nie musimy? Otóż kalkulatory naukowe takich marek jak Hewlett-Packard, National Semiconductor, czy Sinclar Scientific wykorzystują Odwrotną Notację Polską do wykonywania obliczeń. Część kompilatorów również korzysta z tego algorytmu podobnie, jak programy inżynierskie. Można zatem stwierdzić, że jeśli ktoś będzie chciał wykonywać jakieś obliczenia na niskim poziomie abstrakcji, zachowując własną kolejność wykonywania działań, będzie musiał skorzystać z ONP.

Algorytmy

Kalkulatory i programy wykorzystujące ONP muszą wykonać 2 główne kroki:

  1. Konwersja z notacji infiksowej na ONP
  2. Obliczenie wartości wyrażenia ONP

Notacja infiksowa, to po prostu klasyczny zapis działania matematycznego z wykorzystaniem nawiasów.

Algorytm wymieniony w punkcie pierwszym można przeanalizować na Wikipedii.

Opis algorytmu wymienionego w punkcie drugim również jest dostępny na Wikipedii.

W przypadku drugiego algorytmu, przedstawię jego implementację napisaną przeze mnie w języku Ruby. Aktualnie staram się poznać lepiej ten język programowania, dlatego wybrałem go do realizacji tego zadania.

Obliczanie wartości wyrażenia ONP w języku Ruby

stack = []
operators = ['+','-','*','/']
2.upto(ARGV.to_s.length - 3) do |i|
	if( !operators.include?(ARGV.to_s[i].to_s) )
		stack.push(ARGV.to_s[i].to_i)
	else
		new_number = eval(stack.slice(-2,2).join(ARGV.to_s[i].to_s).to_s)
		2.times { stack.pop }
		stack.push(new_number.to_i)
	end
end
puts stack

Teraz wystarczy, że zapiszemy nasz skrypt pod nazwą np. onp.rb i wywołamy go z konsoli.

Przykładowe wywołanie może wyglądać następująco:

ruby onp.rb 12+34+*

Powinniśmy otrzymać wynik:

21

Napisany przeze mnie skrypt akceptuje tylko operatory dodawania (+), odejmowania (-), mnożenia (*) i dzielenia (/).

Podsumowanie

Jak widać, większości programistów ten algorytm nieszczególnie się przyda, ale warto wiedzieć, w jaki sposób działają kompilatory, kalkulatory i programy obliczeniowe. Ponadto, gdy będziemy musieli wykonywać niskopoziomowe operacje arytmetyczne, będziemy wiedzieli, z czego skorzystać.

Posted in Algorytmy, Ruby, Technicznie | Tagged | Leave a comment

Początek


Już jakiś czas temu planowałem założyć bloga, ale brakowało mi do tego czasu i mobilizacji. Tym razem postanowiłem wziąć się w garść i stworzyć coś w rodzaju swojego wirtualnego notatnika. Mam w planach gromadzenie tutaj swoich pomysłów, eksperymentów i inspiracji związanych głównie z szeroko pojętymi technologiami IT. Nie jestem jeszcze pewien, jakie konkretnie treści się tutaj pojawią, choć mam kilka pomysłów. Zobaczę, jak wszystko wyjdzie w praktyce i czy będzie jakieś zainteresowanie związane z poruszanymi przeze mnie tematami. Wychodzę z założenia, że ucząc innych uczę się sam, dlatego też postaram się zamieszczać tu pomysły na rozwiązywanie różnych problemów i opisy ciekawych zagadnień. Niewykluczone, że część wpisów zamieszczanych tu przeze mnie będzie miała charakter nietechniczny. Zobaczymy. Bloga będę prowadził w dwóch wersjach językowych: polskiej i angielskiej. Jeśli znajdziesz, jakieś błędy językowe w angielskiej wersji, napisz, proszę, o tym w komentarzach lub wyślij mi e-mail.

Technikalia

Jeśli chodzi o kwestie techniczne związane z tym blogiem, to wykorzystałem tutaj Zasadę Pareto i oparłem serwis na darmowym oprogramowaniu WordPress. Zainstalowałem plugin do obsługi tzw. responsive web design dla obecnego szablonu, dzięki czemu można stronę oglądać na różnych urządzeniach (netbooki, telefony komórkowe z systemami Android i iOS, tablety), plugin do obsługi kilku wersji językowych, obsługi Latex-a i kolorowania składni kodu. Mógłbym oczywiście stracić mnóstwo czasu na pisanie własnego oprogramowania do obsługi tego bloga i pewnie byłoby ono szybsze, ale osiągnięcie tak złożonej funkcjonalności, jak w WordPressie zajęłoby mi pół roku, albo i dłużej, a końcowy odbiorca i tak nie zauważyłby znacznej różnicy. Ze względu na to, że główną wartością tego bloga ma być treść, ten wybór uważam za uzasadniony.

Posted in Bez kategorii | Leave a comment