java/nekurak.net-web/src/java/cz/frantovo/nekurak/servlet/Fotky.java
author František Kučera <franta-hg@frantovo.cz>
Sun Jun 20 14:46:47 2010 +0200 (2010-06-20)
changeset 145 0efefbf5f8b6
parent 69 4964cf581166
permissions -rw-r--r--
Formátování kódu, důsledné používání tabulátorů, drobné úpravy, StringBuilder
     1 package cz.frantovo.nekurak.servlet;
     2 
     3 import java.io.File;
     4 import java.io.FileInputStream;
     5 import java.io.IOException;
     6 import java.io.InputStream;
     7 import java.util.logging.Level;
     8 import java.util.logging.Logger;
     9 import java.util.regex.Pattern;
    10 import javax.servlet.ServletException;
    11 import javax.servlet.ServletOutputStream;
    12 import javax.servlet.http.HttpServlet;
    13 import javax.servlet.http.HttpServletRequest;
    14 import javax.servlet.http.HttpServletResponse;
    15 
    16 /**
    17  * <p>Servlet pro zpřístupnění fotek, které se nacházejí ve zvláštním adresáři.</p>
    18  *
    19  * <p>Má jeden inicializační parametr:</p>
    20  *  <ul>
    21  *     <li><code>adresar</code> – cesta k adresáři na disku, kde se nacházejí fotky.</li>
    22  *     <li>např. <code>/var/www/nekurak.net/fotky</code></li>
    23  *  </ul>
    24  *
    25  * <p>Očekává se struktura adresářů tohoto typu:</p>
    26  *  <ul>
    27  *	<li>Plné rozlišení: <code>/var/www/nekurak.net/fotky/original/1.jpg</code></li>
    28  *      <li>Náhled fotky:   <code>/var/www/nekurak.net/fotky/nahled/1.jpg</code></li>
    29  *  </ul>
    30  * <p>Lze ovlivnit konstantami níže.</p>
    31  *
    32  * <p>URL respektují fyzické umístění fotek na serveru (není např. ID v GET parametru stylem <code>/fotky?id=123</code>),
    33  * 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
    34  * (fotky jsou veřejné, není potřeba řešit autorizaci)</p>
    35  *
    36  * @author fiki
    37  */
    38 public class Fotky extends HttpServlet {
    39 
    40 	/** Název inicializačního parametru */
    41 	private static final String INIT_ADRESAR = "adresar";
    42 	/** Název podadresáře obsahujícího fotku v plném rozlišení */
    43 	public static final String PODADRESAR_ORIGINAL = "original";
    44 	/** Název podadresáře obsahujícího výchozí náhled fotky */
    45 	public static final String PODADRESAR_NAHLED = "nahled";
    46 	public static final String PRIPONA = "jpg";
    47 	private static final String LOMITKO = File.separator;
    48 	/** Regulární výraz */
    49 	private static final String VZOR_CESTY = "^" + LOMITKO + "(" + PODADRESAR_ORIGINAL + "|" + PODADRESAR_NAHLED + ")" + LOMITKO + "\\d+\\." + PRIPONA + "$";
    50 	private static final String MIME_TYP = "image/jpeg";
    51 	private File adresar;
    52 	private static final Logger log = Logger.getLogger(Fotky.class.getSimpleName());
    53 
    54 	@Override
    55 	public void init() throws ServletException {
    56 		super.init();
    57 		String initAdresar = getServletConfig().getInitParameter(INIT_ADRESAR);
    58 		adresar = new File(initAdresar);
    59 		if (adresar.isDirectory()) {
    60 			log.log(Level.INFO, "Servlet „Fotka“ byl úspěšně inicializován.");
    61 			log.log(Level.INFO, "Adresář s fotkami: {0}", initAdresar);
    62 			log.log(Level.INFO, "RegExp cesty: {0}", VZOR_CESTY);
    63 		} else {
    64 			throw new ServletException("Servlet „Fotka“ se nepodařilo inicializovat. Cesta: " + initAdresar);
    65 		}
    66 	}
    67 
    68 	/**
    69 	 * @param pozadavek pouze GET (není důvod podporovat POST)
    70 	 * @param odpoved odešleme fotku s MIME typem podle konstanty, délkou a datem podle souboru.
    71 	 * @throws ServletException pokud je požadovaná cesta chybná (nevyhovuje vzoru)
    72 	 * @throws IOException
    73 	 */
    74 	@Override
    75 	protected void doGet(HttpServletRequest pozadavek, HttpServletResponse odpoved) throws ServletException, IOException {
    76 
    77 		String cesta = zkontrolujParametr(pozadavek.getPathInfo());
    78 		File soubor = new File(adresar, cesta);
    79 
    80 		if (soubor.isFile() && soubor.canRead()) {
    81 
    82 			if (soubor.lastModified() > pozadavek.getDateHeader("If-Modified-Since")) {
    83 				/** Soubor se změnil nebo ho klient ještě nemá načtený. */
    84 				odpoved.setContentType(MIME_TYP);
    85 				odpoved.setContentLength((int) soubor.length());
    86 				odpoved.setDateHeader("Last-Modified", soubor.lastModified());
    87 
    88 				ServletOutputStream vystup = odpoved.getOutputStream();
    89 				InputStream vstup = new FileInputStream(soubor);
    90 
    91 				try {
    92 					byte[] zasobnik = new byte[1024];
    93 					int bajtuNacteno;
    94 					while ((bajtuNacteno = vstup.read(zasobnik)) != -1) {
    95 						vystup.write(zasobnik, 0, bajtuNacteno);
    96 					}
    97 				} catch (Exception e) {
    98 					throw new ServletException("Chyba při odesílání obrázku klientovi.", e);
    99 				} finally {
   100 					vstup.close();
   101 					vystup.close();
   102 				}
   103 			} else {
   104 				/** Soubor se od posledního načtení klientem nezměnil → není potřeba ho posílat znova. */
   105 				odpoved.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
   106 			}
   107 
   108 		} else {
   109 			/** Neexistující nebo nečitelný soubor → HTTP 404 chyba */
   110 			odpoved.sendError(HttpServletResponse.SC_NOT_FOUND);
   111 		}
   112 	}
   113 
   114 	/**
   115 	 * @param cesta cesta požadovaná klientem: <code>request.getPathInfo()</code>
   116 	 * @throws ServletException pokud cesta nevyhovuje vzoru
   117 	 */
   118 	private static String zkontrolujParametr(String cesta) throws ServletException {
   119 		if (Pattern.matches(VZOR_CESTY, cesta)) {
   120 			/** cesta je v pořádku → pokračujeme */
   121 			return cesta;
   122 		} else {
   123 			/** Chybná cesta → HTTP 500 chyba */
   124 			throw new ServletException("Chybná cesta k obrázku: " + cesta);
   125 		}
   126 	}
   127 }