Java Servlets – skladanie a kompozícia grafiky
V predchádzajúcom článku sme zamerali naše úsilie na tvorbu jednoduchých obrázkov. V tomto článku si skúsime ukázať, ako kresliť do už existujúcich obrázkov a ako kombinovať vopred pripravené obrázky do väčších celkov. V závere si tiež ukážeme, ako zabezpečiť správnu obsluhu výnimiek, ktoré môžu vzniknúť v servletoch generujúcich grafiku.
Zapisovanie do obrázka
Po zamyslení sa nad príkladmi z predchádzajúcej časti sa môže klamlivo zdať, že jedným zo spôsobov ako zapisovať do existujúceho obrázku, je získať objekt Image
prostredníctvom Toolkit.getDefaultToolkit().getImage(imagename)
. Následne získať jeho grafický kontext cez getGraphics()
metódu, a potom zapísať do tohto kontextu požadovanú informáciu. Na pohľad to vyzerá byť „nice and easy“ avšak pravda je taká, že nemôžete použiť metódu getGraphics()
nad objektom Image
, ak nebol vytvorený prostredníctvom metódy createImage()
triedy Component
Ako teda problém šikovne riešiť? Začiatok bude rovnaký; musíte získať objekt Image
a povedať mu, aby sa zapísal do iného vopred pripraveného grafického kontextu. Až potom môžete využiť tento kontext a zapisovať doň. Aby som teda dlho nevysvetľoval, ukážeme si to na príklade.
TopSecretImage.java
import java.awt.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import Acme.JPM.Encoders.GifEncoder;
public class TopSecretImage extends HttpServlet {
Frame frame = null;
Graphics g = null;
public void init(ServletConfig config)
throws ServletException {
super.init(config);
//vytvoríme viacnásobne použiteľný frame
frame = new Frame();
frame.addNotify();
}
public void doGet(
HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
ServletOutputStream out = res.getOutputStream();
try {
//získame info o umiestnení obrázka
String source = req.getPathTranslated();
if (source == null) {
throw new ServletException(„Chyba v hľadaní obrázka“);
}
//natiahneme obrázok do pamäte
MediaTracker mt = new MediaTracker(frame);
Image image = Toolkit.getDefaultToolkit().getImage(source);
mt.addImage(image, 0);
try {
//spustíme načítanie
mt.waitForAll();
}
catch (InterruptedException e) {
getServletContext().log(„Chyba pri načítaní obrázka, e“);
throw new ServletException(e.getMessage());
}
/* vytvoríme nový grafický kontext s
rovnakými rozmermi aké má obrázok */
int w = image.getWidth(frame);
int h = image.getHeight(frame);
Image offscreen = frame.createImage(w, h);
g = offscreen.getGraphics();
//vložíme obrázok do graf. kontextu
g.drawImage(image, 0, 0, frame);
//do kontextu zapíšeme „Top Secret!“
g.setColor(Color.RED);
g.setFont(new Font(„Monospaced“, Font.BOLD, 30));
g.drawString(„Top Secret!“, 60, 80);
//zakódujeme a pošleme klientovi
res.setContentType(„image/gif“);
GifEncoder encoder = new GifEncoder(offscreen, out);
encoder.encode();
}
finally {
//uvoľníme zdroje
if (g != null) g.dispose();
}
}
//uvoľníme zdroje
public void destroy() {
if (frame != null) frame.removeNotify();
}
}
Popíšme si jednotlivé kroky bližšie. Na začiatku v metóde init()
vytvoríme Frame
, čo je niečo ako malovacie plátno. Tým zabezpečíme, že bude vytvorený pri prvej požiadavke na servlet. Pri ostatných požiadavkách je plátno už vytvorené, čím sa znižuje čas potrebný na vytvorenie odpovede. V druhom kroku získame názov a umiestnenie predpripraveného obrázka pomocou metódy getPathTranslated()
, ktorá transformuje informáciu za názvom servletu v URL do skutočnej cesty. Myslím, že z obrázka je to jasne viditeľné. Nasleduje načítanie obrázka do pamäte, s využitím triedy MediaTracker
. Potom získame informáciu o výške a šírke načítaného obrázku, vytvoríme nový objekt triedy Image
s rovnakými rozmermi, a získame jeho grafický kontext. Nasledovný obrázok ukazuje skutočné umiestnenie predpripraveného obrázka. Samozrejme, že daná adresa je relatívna k dokument rootu servera. Fyzicky je umiestnený v $CATALINA_HOME/webapps/examples/images.
Teraz nám už nič nebráni, aby sme do prázdneho obrázku vložili ten, pôvodne načítaný v pamäti. Všetky nasledovné kroky by vám už mohli byť jasné, pretože sme ich použili v predchádzajúcej časti. Všimnite si, akým spôsobom sú ošetrené výnimky. Niektoré z nich sú logované, aby mohli byť neskôr analyzované. Keďže servlet vracia iba obrázok, je problém získať nejaké textové informácie o vzniknutej výnimke. Tento obrázok ukazuje výsledok nášho príkladu.
Skladanie obrázkov
Už vieme, že servlet môže do predpripravených obrázkov zapisovať napríklad text. Nevieme však, že servlet môže kombinovať viacero predpripravených obrázkov do jedného výsledného obrazu. Technika je však veľmi podobná. Najprv je potrebné natiahnuť jednotlivé obrázky do pamäte, a následne ich zapísať do vhodne vytvoreného Image
objektu, ktorý po zakódovaní pošleme klientovi. Teoreticky to vyzerá jednoducho, ale v praxi je treba v súvislosti s tým vyriešiť niekoľko problémov. Vytvorme si ukážku grafického počítadla návštevnosti stránky.
ImageCounter.java
import java.awt.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import Acme.JPM.Encoders.GifEncoder;
public class ImageCounter extends HttpServlet {
public static final String DIR = „/images/numbers“;
public static final String COUNT = „9876543210“;
public void doGet(
HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
ServletOutputStream out = res.getOutputStream();
Frame frame = null;
Graphics g = null;
try {
/* zistíme hodnotu, ktorá sa má zobraziť,
musí to byť samostatný reťazec bez mena,
alebo sa použije hodnota COUNT */
String count = (String) req.getQueryString();
if (count == null) count = COUNT;
//vytvoríme jednorozmerné pole objektov Images
int countlen = count.length();
Image images[] = new Image[countlen];
//toto pole naplníme jednotlivými obrázkami
for (int i = 0; i < countlen; i++) {
String imageSrc = getServletContext().getRealPath(
DIR + „/“ + count.charAt(i) + „.gif“);
images[i] = Toolkit.getDefaultToolkit().getImage(imageSrc);
}
//vytvoríme frame
frame = new Frame();
frame.addNotify();
//natiahneme obrázky do pamäte
MediaTracker mt = new MediaTracker(frame);
for (int i = 0; i < countlen; i++) {
mt.addImage(images[i], i);
}
try {
mt.waitForAll();
}
catch (InterruptedException e) {
getServletContext().log(„Chyba pri načítaní obrázka“, e);
throw new ServletException(e.getMessage());
}
/* kontrola na vzniknuté chyby, ktoré
sa mohli objaviť pri načítaní obrázkov */
if (mt.isErrorAny()) {
/* ak máme problém, treba určiť,
o ktorý obrázok(ky) sa jedná */
StringBuffer errorChars = new StringBuffer();
for (int i = 0; i < countlen; i++) {
if (mt.isErrorID(i)) {
errorChars.append(count.charAt(i));
}
}
throw new ServletException(„Problém vznikol v: “ +
errorChars.toString());
}
//zistíme celkovú veľkosť obrázkov
int width = 0;
int height = 0;
for (int i = 0; i < countlen; i++) {
width += images[i].getWidth(frame);
height = Math.max(height, images[i].getHeight(frame));
}
/* vytvoríme prázdny Image s rozmermi
odpovedajúcimi celkovej veľkosti obrázkov,
a získame jeho grafický kontext */
Image image = frame.createImage(width, height);
g = image.getGraphics();
//vložíme jednotlivé obrázky
int xindex = 0;
for (int i = 0; i < countlen; i++) {
g.drawImage(images[i], xindex, 0, frame);
xindex += images[i].getWidth(frame);
}
//výsledok zakódujeme a pošleme
res.setContentType(„image/gif“);
GifEncoder encoder = new GifEncoder(image, out);
encoder.encode();
}
//uvoľníme zdroje
finally {
if (g != null) g.dispose();
if (frame != null) frame.removeNotify();
}
}
}
Tento servlet je o niečo komplikovanejší ako predchádzajúci. V prvom rade musí servlet najprv zistiť, aké číslo má na grafickom počítadle zobraziť. Zistí to z objektu HttpServletRequest
a v prípade že je NULL, použije prednastavenú hodnotu. Čo sa týka samotného počítania prístupov, touto problematikou sme sa už zaoberali v skorších častiach seriálu a musím vás odkázať na ne. Tu som pre jednoduchosť použil takýto mechanizmus, v praxi by si servlet musel sám evidovať počet prístupov, ideálne aj s prenosom medzi jednotlivými životnými cyklami. Algoritmus pokračuje tým, že pre každé číslo zadané v požiadavke na servlet, sa načíta príslušný obrázok. Aby som si to uľahčil, názov obrázku je tvorený len číslom, ktoré predstavuje. Nasleduje výpočet celkovej šírky, ktorý je súčtom šírok jednotlivých obrázkov. Čo sa týka výšky, použije sa maximálna hodnota. Získané hodnoty sa použijú na vytvorenie grafického kontextu, do ktorého sa jednotlivé obrázky zapíšu v poradí ako boli natiahnuté do pamäte. V závere sa uskutoční zakódovanie a odoslanie obrázka klientovi. Uvádzam pár ukážok:
… použije sa prednastavená hodnota
… a náhodne zvolené číslo
Na záver článku som zaradil možnosť stiahnuť si všetky potrebné zdrojáky, skompilované triedy a obrázky v jednom balíčku.
Mohlo by vás také zajímat
-
Jak se chránit před podvody na internetu – část 1
8. října 2024 -
Vaše pošta může být špatně nastavena – svěřte ji profesionálům
13. července 2023 -
Nejlevnější VPS: To je nový Cloud Server Mini od ZonerCloud
4. června 2024
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