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