Java Servlets – servlet chaining a grafika
V jednom z minulých článkov som spomenul možnosť reťazenia servletov (servlet chaining) na úrovni predávania si textových informácií. Na druhej strane, servlety si môžu navzájom posielať nie len text, ale aj obrázky. Samozrejme nie len tak samoúčelne, ale často z dôvodu filtrovania či aplikovania rôznych grafických efektov a podobne. V tomto článku si ukážeme dva varianty, ako môžu servlety spolupracovať v oblasti spracovania grafiky.
Zatiaľ sme o reťazení servletov v súvislosti s grafikou ešte nehovorili. Ak si spomeniete, tak servlet v reťazi najprv získa požadovaný obsah na svojom vstupe, vykoná filtrovanie a následne pošle prefiltrovaný obsah ako svoj výstup. Väčšinou sa táto technika využíva na filtrovanie textových údajov, ako som už spomenul, ale nič vám nebráni využiť ju aj v súvislosti s obrázkami.
Aplikovanie špeciálneho efektu na obrázok sa vykoná v podstate rovnakým spôsobom bez ohľadu na to, či ide o servlet filter alebo štandardný servlet. Jediný rozdiel je v tom, že namiesto načítania obrázku zo súboru, alebo generovania vlastnej grafiky, sa dáta predstavujúce obrázok získajú ako zakódovaný prúd bajtov. Nasledujúci príklad predstavuje servlet zapojený do reťaze. Jeho úlohou je zmenšiť rozmery obrázku na polovicu.
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import Acme.JPM.Encoders.*;
public class ShrinkFilterServlet extends HttpServlet {
public void doGet
(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
ServletOutputStream out = res.getOutputStream();
String contType = req.getContentType();
if (contType == null || !contType.startsWith(„image“)) {
throw new ServletException(„Chýba content type, musí byť \“image/*\““);
}
// získame dáta obsahujúce obrázok
DataInputStream in = new DataInputStream( new BufferedInputStream( req.getInputStream()));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[4 * 1024]; // 4K buffer
int len;
while ((len = in.read(buf, 0, buf.length)) != -1) {
baos.write(buf, 0, len);
}
// vytvoríme objekt image
Image image = Toolkit.getDefaultToolkit() .createImage(baos.toByteArray());
// vytvoríme ale nezobrazíme frame
Frame frame = new Frame();
// natiahneme obrázok a získame jeho výšku a šírku
MediaTracker mt = new MediaTracker(frame);
mt.addImage(image, 0);
try {
mt.waitForAll();
}
catch (InterruptedException e) {
getServletContext().log(„Chyba pri načítaní obrázka.“, e);
throw new ServletException(e.getMessage());
}
/* Zmenšíme rozmery obrázku na polovicu.
Vylepšená verzia servletu by mohla získať
požadovaný pomer strán z inicializačných parametrov.
Ako alternatívu by sme mohli využiť triedu
java.awt.image.AreaAveragingScaleFilter, alebo triedu
java.awt.image.ReplicateScaleFilter. */
Image shrunk = image.getScaledInstance (image.getWidth(frame) / 2, image.getHeight(frame) / 2, image.SCALE_DEFAULT);
// zmenšený obrázok zakódujeme a pošleme
res.setContentType(„image/gif“);
GifEncoder encoder = new GifEncoder(shrunk, out);
encoder.encode();
}
}
Metóda createImage(byte[])
triedy Toolkit
vytvorí objekt Image
z dát uložených v poli bajtov. Táto metóda neakceptuje vstupný prúd bajtov, z toho dôvodu bolo nutné najprv vytvoriť objekt ByteArrayOutputStream
. Metóda ďalej automaticky rozoznáva jeden z podporovaných formátov (GIF, JPEG, XBM). Po získaní obrázku je nutné zistiť jeho skutočné rozmery. Následne servlet použitím getScaledInstance()
, vytvorí prefiltrovanú verziu obrázku, ktorá je o polovicu užšia a o polovicu nižšia. Nakoniec je objekt zakódovaný a poslaný klientovi.
Možno sa pýtate, načo vlastne použiť servlet filter na aplikovanie grafického efektu, namiesto štandardného servletu. Hlavným dôvodom je vyššia flexibilita. Napríklad môže byť povedané, že každý obrázok bude vrátený v zmenšenej podobe, ak požadované URI bude začínať napríklad na „/lite“. Alebo sa môže povedať, že všetky obrázky typu „image/xbm“, musia najprv prejsť špeciálnym filtrom na konverziu XBM na GIF.
Ak vás napadla možnosť, že by bolo vhodné na predávanie si obrázkov využiť serializáciu, tak chcem podotknúť, že obrázky, respektíve objekt Image
, nie sú serializovateľné. Existuje iná pomerne efektívna metóda na prenos grafiky medzi servletmi, má však jeden háčik – servlet, ktorý túto techniku použije, musí mať istotu, že nasledujúci článok v reťazi je servlet a nie už klient. Konkrétne ide o využitie systémových Properties
, ktoré môžu dramaticky zvýšiť výkonnosť popisovaných operácií.
Vyššie uvedená technika môže byť pomerne náročná na výkon hlavne pri väčších obrázkoch a vyššom zaťažení. Druhá alternatíva využívajúca Properties
spočíva v tom, že prvý servlet uloží obrázok ako objekt Image
do systémových Properties
a následne pošle druhému servletu malý unikátny kľúč, odkazujúci na tento obrázok. Prostredníctvom tohto kľúča potom druhý servlet môže daný obrázok identifikovať. Nasledujúci príklad presne ukazuje, ako servlet vytvorí a priradí kľúč obrázku uloženému v Properties
liste.
import java.awt.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class ChainImageSource extends HttpServlet {
int keynum = 0; // použijeme na vytvorenie unikátneho kľúča
public void doGet
(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
// získame objekt Image
String imageFile = getServletContext() .getRealPath(„/images/duke.gif“);
Image image = Toolkit.getDefaultToolkit() .getImage(imageFile);
// vytvoríme unikátny kľúč,
String key = „ChainImageSource.“ + keynum++;
// pod ktorým obrázok uložíme do Properties
System.getProperties().put(key, image);
// nastavíme vlastný content type
res.setContentType(„java/image-key“);
PrintWriter out = res.getWriter();
// kľúč pošleme
out.println(key);
}
}
Všimnite si, ako servlet vytvorí unikátny kľúč. Ako prefix použije svoje meno (pričom je vhodné pridať ešte aj celý názov balíčka, do ktorého servlet patrí), vďaka čomu si zabezpečíme unikátnosť nášho kľúča. Potom k prefixu pridá celé číslo, ktoré je pre každý nový obrázok navýšené o jednotku. Ako content type použijeme vlastný špeciálny typ (java/image-key), aby sme rozlíšili situáciu, kedy posielame iba odkaz na obrázok a nie obrázok samotný. Nasledujúci príklad zobrazuje druhú časť reťaze, teda servlet, ktorý použije kľúč na získanie skutočného objektu Image.
import java.awt.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class ChainImageDest extends HttpServlet {
public void doGet
(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
// zistíme prijatý content type
String contentType = req.getContentType();
Image image = null;
/* Ak ide o content type začínajúci na „image/*“ znamená to,
že obdržíme obrázok ako prúd zakódovaných bajtov. */
if (contentType != null && contentType.startsWith(„image“)) {
/* Do tohto bloku môžeme neskôr implementovať funkčnosť,
ktorú sme si ukázali vyššie v príklade ShrinkFilterServlet.java */
}
// „java/image-key“ content type znamená, že získame kľúč
else if („java/image-key“.equals(contentType)) {
// prečítame prvý riadok získaného obsahu, čím dostaneme kľúč
String key = req.getReader().readLine();
// získame obrázok uložený pod daným kľúčom
image = (Image)System.getProperties().get(key);
// obrázok uvoľníme z pamäte
System.getProperties().remove(key);
}
else {
throw new ServletException(„Content type musí byť ‚image/*‘ alebo ‚java/image-key'“);
}
/* Servlet môžeme pre jednoduchosť zakončiť týmto spôsobom.
V reálnom nasadení by sme objekt Image vhodným spôsobom využili. */
res.setContentType(„text/plain“);
PrintWriter out = res.getWriter();
out.println(„Získaný obrázok je: “ + image);
}
}
Vyššie popísaná technika na zdieľanie informácií využíva zoznam systémových properties, ktoré nájdete v triede java.lang.System
. Tento zoznam obsahuje štandardné systémové informácie, ako je java.version
alebo path.separator
. Môže však uchovávať nielen systémové, ale aj aplikačné vlastnosti. Pri triede Properties
sa predpokladá, že kľúč aj hodnota sú String
. Toto obmedzenie však môže byť ignorované (aj keď sa to pokladá za hack) čiastočne aj preto, pretože trieda Properties
je odvodená od triedy Hashtable
. Toto môže samozrejme spôsobiť výnimku pri behu programu (ClassCastException
), kedy metódy getProperty(), list(), save()
prirodzene predpokladajú, že kľúč aj hodnota sú typu String
.
Na záver chcem upozorniť, že informácie uložené v Properties
nie sú perzistentné medzi jednotlivými reštartmi servera, ďalej, že informácie môžu byť ľubovolne zmenené alebo zmazané akoukoľvek triedou bežiacou na danej JVM, a nakoniec, že niektoré bezpečnostné prvky servera nemusia umožniť servletu prístup k systémovým properties.
Na záver si môžete stiahnuť všetky použité súbory.
Starší komentáře ke článku
Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.
Mohlo by vás také zajímat
-
Souboj na trhu s CPU pro servery: AMD vs. Intel
8. prosince 2023 -
AI v programování: Jak používat GitHub Copilot (část 1)
12. února 2024 -
Lék na phishing a apatii ve světě e-mailového marketingu
18. března 2024 -
Zabezpečení e-mailů: Jak můžete chránit vaši firemní komunikaci
13. prosince 2023
Nejnovější
-
Výkonný a kompaktní: ASOME Max Studio s výjimečným poměrem cena/výkon
11. listopadu 2024 -
Šokující data od Microsoftu: Kyberútoky rostou o stovky procent!
8. listopadu 2024 -
Chcete jedinečnou doménu? Objevte koncovky FOOD, MEME a MUSIC!
7. listopadu 2024 -
OpenAI představilo novou funkci ChatGPT Search
6. listopadu 2024