Java Servlets – komprimovanie obsahu

10. května 2004

V predchádzajúcich článkoch venovaných práci s grafikou sme komprimovanie obsahu posielaného servletom už využívali. Išlo však o nepriame komprimovanie, ktoré bolo súčasťou procesu kódovania obrázku do výsledného formátu. V praxi je však možné využiť aj komprimovanie HTML, ktoré môže výrazne znížiť objem dát a tým aj čas potrebný na prenos týchto dát od servera ku klientovi.

Komprimovaný obsah sa z pohľadu užívateľa ničím nelíši od nekomprimovaného. Je to tým, že browser automaticky pri obdržaní komprimovaného obsahu, tento obsah dekomprimuje. Jediný rozdiel môže byť v rýchlosti ktorou sa tento obsah zobrazí v prehliadači. Používať túto techniku sa oplatí spravidla vtedy, ak ide o väčšie množstvo dát vo formáte HTML. Keďže obrázky už sú v princípe komprimované, ich ďalšia komprimácia pravdepodobne neprinesie požadovaný efekt. Treba však mať na pamäti, že samotný proces komprimácie si vyžaduje viac práce zo strany servera. Preto pri posudzovaní efektívnosti tejto techniky, treba myslieť aj na tento aspekt. Viac práce totiž znamená menšiu výkonnosť a v podstate aj odozvu pri vyššom zaťažení. V neposlednom rade aj proces dekomprimácie na strane klienta si vyžaduje svoje prostriedky.

Balíček java.util.zip, ktorý budeme využívať, zabezpečuje podporu pre čítanie a zapisovanie do dvoch formátov GZIP a ZIP. Poskytuje tiež vhodné nástroje na to, aby servlet mohol vytvárať a posielať komprimovaný obsah. Určite viete, že servlet môže v hlavičke odoslať parameter Content-Type, aby tým oznámil klientovi aký typ informácie zasiela. V prípade, že sa bude posielať komprimovaný obsah, musí servlet v hlavičke odoslať aj parameter Content-Encoding, aby tým klientovi oznámil aký typ schémy bol použitý na skomprimovanie posielaného obsahu. Možné komprimačné schémy sú gzip (x-gzip) alebo compress ( x-compress).

Aby server vedel, ktorú schému je možné použiť, posiela klient vo svojej požiadavke parameter Accept-Encoding obsahujúci zoznam schém ktorým klient rozumie, oddelených čiarkou. V prípade, že klient túto informáciu nepošle, môže sa servlet rozhodnúť buď že pošle obsah nekomprimovaný alebo zo záhlavia požiadavky vyčíta hodnotu parametra User-Agent, na základe ktorého zistí, či klient podporuje danú schému alebo nie. Dnes je situácia taká, že väčšina klientov podporuje minimálne schému gzip.

Vytvorenie komprimovaného obsahu nie je až tak komplikované. V podstate ide o to, aby sa štandardný ServletOutputStream predal špecifickému objektu triedy GZIPOutputStream, alebo triedy ZipOutputStream. Je však treba pamätať na to, že na konci je vždy nutné zavolať metódu close().

Vytvorme si príklad. Pôjde o servlet, ktorému ako parameter predáme odkaz na nejaký súbor. Servlet tento súbor načíta, a vždy keď je to možné daný súbor zobrazí, pričom ho pošle skomprimovaný.

ViewFileCompress.java:

import java.io.*;
import java.util.*;
import java.util.zip.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class ViewFileCompress extends HttpServlet {
 public void doGet
            (HttpServletRequest req, HttpServletResponse res)
                    throws ServletException, IOException {
  OutputStream out = null;
  /* Vyberieme vhodný typ kódovania založený na
  hodnote parametra Accept-Encoding. Vyberieme GZIP ak hlavička
  obsahuje „gzip“. Alebo vyberieme ZIP ak hlavička
  obsahuje „compress“. Posledná varianta je bez kompresie. */

  String encodings = req.getHeader(„Accept-Encoding“);
  if (encodings != null && encodings.indexOf(„gzip“) != -1) {
   res.setHeader(„Content-Encoding“, „gzip“);
   out = new GZIPOutputStream(res.getOutputStream());
  }
  else if (encodings != null && encodings.indexOf
                                        („compress“) != -1) {
   res.setHeader(„Content-Encoding“, „x-compress“);
   out = new ZipOutputStream(res.getOutputStream());
   ((ZipOutputStream)out).putNextEntry(new ZipEntry
                                                („zip_name“));
  }
  else {
   out = res.getOutputStream();
  }
  // získame odkaz na súbor
  String file = req.getPathTranslated();
  // ak nie je súbor, vrátime SC_FORBIDDEN
  if (file == null) {
   res.sendError(res.SC_FORBIDDEN);
   return;
  }
  // získame a nastavíme typ súboru
  String contentType = getServletContext().getMimeType(file);
  res.setContentType(contentType);
  // vrátime súbor využitím triedy ServeFile
  try {
   ServeFile.returnFile(file, out);
  }
  catch (FileNotFoundException e) {
   res.sendError(res.SC_NOT_FOUND);
   return;
  }
  catch (IOException e) {
   getServletContext().log(„Problém s posielaním súboru“, e);
  }
  out.close();
 }
}

ServeFile.java:

import java.io.*;
public class ServeFile {
 // táto metóda pošle obsah súboru do výstupného prúdu
 public static void returnFile(String file, OutputStream out)
                  throws FileNotFoundException, IOException {
  FileInputStream fis = null;
  try {
   fis = new FileInputStream(file);
   byte[] buf = new byte[4 * 1024];
   int bytesRead;
   while ((bytesRead = fis.read(buf)) != -1) {
    out.write(buf, 0, bytesRead);
   }
  }
  finally {
   if (fis != null) fis.close();
  }
 }
}

Na začiatku servlet vytvorí prázdny objekt OutputStream a následne ho nastaví na GZIPOutputStream, ZipOutputStream alebo na ServletOutputStream v závislosti na hodnote záhlavia Accept-Encoding. Podľa tejto hodnoty sa potom nastaví príslušné záhlavie Content-Encoding. Toto záhlavie musí byť nastavené vždy keď posielate komprimovaný obsah, aby prehliadač vedel, ktorý dekódovací algoritmus má použiť.

Po spracovaní tejto logiky získame výstupný prúd dát, s ktorým môžeme pracovať ako s klasickým objektom OutputStream. Tento prúd potom môžeme „zabaliť“ napríklad do objektu PrintStream, PrintWriter, prípadne ho predať objektu GifEncoder na zakódovanie. Nezáleží čo konkrétne s daným výstupným prúdom urobíme, ale každopádne musíme na konci zavolať metódu close(), aby sa komprimovaný prúd správne uzavrel.

Keďže existuje samozrejme obsah, ktorý nemá veľký zmysel komprimovať, napríklad obrázky GIF a JPEG, ktoré sú komprimované už v procese ich tvorby, tak vylepšená verzia nášho servleta ViewFileCompress, by mohla mať zabudovaný algoritmus na rozlišovanie MIME typu súboru, ktorý sa má zobraziť. Alebo by mohol byť tento servlet nasadený ako filter, ktorý skomprimuje obsah a predá ho ďalšiemu článku v reťazi. Možností je viac. Na záver vám ponúkam všetky dnes 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.

Předchozí článek parfemy.cz
Štítky: Články

Mohlo by vás také zajímat

Nejnovější

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *