franta-hg@64: package cz.frantovo.nekurak.servlet; franta-hg@64: franta-hg@64: import java.io.File; franta-hg@64: import java.io.FileInputStream; franta-hg@64: import java.io.IOException; franta-hg@64: import java.io.InputStream; franta-hg@64: import java.util.logging.Level; franta-hg@64: import java.util.logging.Logger; franta-hg@64: import java.util.regex.Pattern; franta-hg@64: import javax.servlet.ServletException; franta-hg@64: import javax.servlet.ServletOutputStream; franta-hg@64: import javax.servlet.http.HttpServlet; franta-hg@64: import javax.servlet.http.HttpServletRequest; franta-hg@64: import javax.servlet.http.HttpServletResponse; franta-hg@64: franta-hg@64: /** franta-hg@65: *

Servlet pro zpřístupnění fotek, které se nacházejí ve zvláštním adresáři.

franta-hg@64: * franta-hg@65: *

Má jeden inicializační parametr:

franta-hg@65: * franta-hg@64: * franta-hg@65: *

Očekává se struktura adresářů tohoto typu:

franta-hg@65: * franta-hg@65: *

Lze ovlivnit konstantami níže.

franta-hg@65: * franta-hg@65: *

URL respektují fyzické umístění fotek na serveru (není např. ID v GET parametru stylem /fotky?id=123), franta-hg@65: * díky tomu je možné, aby fotky později servírovala přímo reverzní proxy a nemusely procházet přes naši aplikaci franta-hg@65: * (fotky jsou veřejné, není potřeba řešit autorizaci)

franta-hg@65: * franta-hg@64: * @author fiki franta-hg@64: */ franta-hg@64: public class Fotky extends HttpServlet { franta-hg@64: franta-hg@64: /** Název inicializačního parametru */ franta-hg@64: private static final String INIT_ADRESAR = "adresar"; franta-hg@64: /** Název podadresáře obsahujícího fotku v plném rozlišení */ franta-hg@66: public static final String PODADRESAR_ORIGINAL = "original"; franta-hg@64: /** Název podadresáře obsahujícího výchozí náhled fotky */ franta-hg@66: public static final String PODADRESAR_NAHLED = "nahled"; franta-hg@66: public static final String PRIPONA = "jpg"; franta-hg@64: private static final String LOMITKO = File.separator; franta-hg@64: /** Regulární výraz */ franta-hg@64: private static final String VZOR_CESTY = "^" + LOMITKO + "(" + PODADRESAR_ORIGINAL + "|" + PODADRESAR_NAHLED + ")" + LOMITKO + "\\d+\\." + PRIPONA + "$"; franta-hg@64: private static final String MIME_TYP = "image/jpeg"; franta-hg@64: private File adresar; franta-hg@64: private static final Logger log = Logger.getLogger(Fotky.class.getSimpleName()); franta-hg@64: franta-hg@64: @Override franta-hg@64: public void init() throws ServletException { franta-hg@64: super.init(); franta-hg@64: String initAdresar = getServletConfig().getInitParameter(INIT_ADRESAR); franta-hg@64: adresar = new File(initAdresar); franta-hg@64: if (adresar.isDirectory()) { franta-hg@64: log.log(Level.INFO, "Servlet „Fotka“ byl úspěšně inicializován."); franta-hg@64: log.log(Level.INFO, "Adresář s fotkami: " + initAdresar); franta-hg@64: log.log(Level.INFO, "RegExp cesty: " + VZOR_CESTY); franta-hg@64: } else { franta-hg@64: throw new ServletException("Servlet „Fotka“ se nepodařilo inicializovat. Cesta: " + initAdresar); franta-hg@64: } franta-hg@64: } franta-hg@64: franta-hg@64: /** franta-hg@64: * @param pozadavek pouze GET (není důvod podporovat POST) franta-hg@65: * @param odpoved odešleme fotku s MIME typem podle konstanty, délkou a datem podle souboru. franta-hg@65: * @throws ServletException pokud je požadovaná cesta chybná (nevyhovuje vzoru) franta-hg@64: * @throws IOException franta-hg@64: */ franta-hg@64: @Override franta-hg@64: protected void doGet(HttpServletRequest pozadavek, HttpServletResponse odpoved) throws ServletException, IOException { franta-hg@64: franta-hg@64: String cesta = zkontrolujParametr(pozadavek.getPathInfo()); franta-hg@64: File soubor = new File(adresar, cesta); franta-hg@64: franta-hg@64: if (soubor.isFile() && soubor.canRead()) { franta-hg@64: franta-hg@64: if (soubor.lastModified() > pozadavek.getDateHeader("If-Modified-Since")) { franta-hg@64: /** Soubor se změnil nebo ho klient ještě nemá načtený. */ franta-hg@64: odpoved.setContentType(MIME_TYP); franta-hg@64: odpoved.setContentLength((int) soubor.length()); franta-hg@64: odpoved.setDateHeader("Last-Modified", soubor.lastModified()); franta-hg@64: franta-hg@69: ServletOutputStream vystup = odpoved.getOutputStream(); franta-hg@69: InputStream vstup = new FileInputStream(soubor); franta-hg@64: franta-hg@64: try { franta-hg@69: byte[] zasobnik = new byte[1024]; franta-hg@64: int bajtuNacteno; franta-hg@69: while ((bajtuNacteno = vstup.read(zasobnik)) != -1) { franta-hg@69: vystup.write(zasobnik, 0, bajtuNacteno); franta-hg@64: } franta-hg@64: } catch (Exception e) { franta-hg@64: throw new ServletException("Chyba při odesílání obrázku klientovi.", e); franta-hg@64: } finally { franta-hg@69: vstup.close(); franta-hg@69: vystup.close(); franta-hg@64: } franta-hg@64: } else { franta-hg@64: /** Soubor se od posledního načtení klientem nezměnil → není potřeba ho posílat znova. */ franta-hg@64: odpoved.setStatus(HttpServletResponse.SC_NOT_MODIFIED); franta-hg@64: } franta-hg@64: franta-hg@64: } else { franta-hg@64: /** Neexistující nebo nečitelný soubor → HTTP 404 chyba */ franta-hg@64: odpoved.sendError(HttpServletResponse.SC_NOT_FOUND); franta-hg@64: } franta-hg@64: } franta-hg@64: franta-hg@64: /** franta-hg@65: * @param cesta cesta požadovaná klientem: request.getPathInfo() franta-hg@64: * @throws ServletException pokud cesta nevyhovuje vzoru franta-hg@64: */ franta-hg@64: private static String zkontrolujParametr(String cesta) throws ServletException { franta-hg@64: if (Pattern.matches(VZOR_CESTY, cesta)) { franta-hg@64: /** cesta je v pořádku → pokračujeme */ franta-hg@64: return cesta; franta-hg@64: } else { franta-hg@64: /** Chybná cesta → HTTP 500 chyba */ franta-hg@64: throw new ServletException("Chybná cesta k obrázku: " + cesta); franta-hg@64: } franta-hg@64: } franta-hg@64: }