java/nekurak.net-web/src/java/cz/frantovo/nekurak/servlet/Fotky.java
author František Kučera <franta-hg@frantovo.cz>
Tue Mar 16 13:32:31 2010 +0100 (2010-03-16)
changeset 69 4964cf581166
parent 66 048531e09c09
child 145 0efefbf5f8b6
permissions -rw-r--r--
Hibernate: podniky budeme načítat včetně jejich fotek.
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@64
    40
    /** Název inicializačního parametru */
franta-hg@64
    41
    private static final String INIT_ADRESAR = "adresar";
franta-hg@64
    42
    /** Název podadresáře obsahujícího fotku v plném rozlišení */
franta-hg@66
    43
    public static final String PODADRESAR_ORIGINAL = "original";
franta-hg@64
    44
    /** Název podadresáře obsahujícího výchozí náhled fotky */
franta-hg@66
    45
    public static final String PODADRESAR_NAHLED = "nahled";
franta-hg@66
    46
    public static final String PRIPONA = "jpg";
franta-hg@64
    47
    private static final String LOMITKO = File.separator;
franta-hg@64
    48
    /** Regulární výraz */
franta-hg@64
    49
    private static final String VZOR_CESTY = "^" + LOMITKO + "(" + PODADRESAR_ORIGINAL + "|" + PODADRESAR_NAHLED + ")" + LOMITKO + "\\d+\\." + PRIPONA + "$";
franta-hg@64
    50
    private static final String MIME_TYP = "image/jpeg";
franta-hg@64
    51
    private File adresar;
franta-hg@64
    52
    private static final Logger log = Logger.getLogger(Fotky.class.getSimpleName());
franta-hg@64
    53
franta-hg@64
    54
    @Override
franta-hg@64
    55
    public void init() throws ServletException {
franta-hg@64
    56
	super.init();
franta-hg@64
    57
	String initAdresar = getServletConfig().getInitParameter(INIT_ADRESAR);
franta-hg@64
    58
	adresar = new File(initAdresar);
franta-hg@64
    59
	if (adresar.isDirectory()) {
franta-hg@64
    60
	    log.log(Level.INFO, "Servlet „Fotka“ byl úspěšně inicializován.");
franta-hg@64
    61
	    log.log(Level.INFO, "Adresář s fotkami: " + initAdresar);
franta-hg@64
    62
	    log.log(Level.INFO, "RegExp cesty: " + VZOR_CESTY);
franta-hg@64
    63
	} else {
franta-hg@64
    64
	    throw new ServletException("Servlet „Fotka“ se nepodařilo inicializovat. Cesta: " + initAdresar);
franta-hg@64
    65
	}
franta-hg@64
    66
    }
franta-hg@64
    67
franta-hg@64
    68
    /**
franta-hg@64
    69
     * @param pozadavek pouze GET (není důvod podporovat POST)
franta-hg@65
    70
     * @param odpoved odešleme fotku s MIME typem podle konstanty, délkou a datem podle souboru.
franta-hg@65
    71
     * @throws ServletException pokud je požadovaná cesta chybná (nevyhovuje vzoru)
franta-hg@64
    72
     * @throws IOException
franta-hg@64
    73
     */
franta-hg@64
    74
    @Override
franta-hg@64
    75
    protected void doGet(HttpServletRequest pozadavek, HttpServletResponse odpoved) throws ServletException, IOException {
franta-hg@64
    76
franta-hg@64
    77
	String cesta = zkontrolujParametr(pozadavek.getPathInfo());
franta-hg@64
    78
	File soubor = new File(adresar, cesta);
franta-hg@64
    79
franta-hg@64
    80
	if (soubor.isFile() && soubor.canRead()) {
franta-hg@64
    81
franta-hg@64
    82
	    if (soubor.lastModified() > pozadavek.getDateHeader("If-Modified-Since")) {
franta-hg@64
    83
		/** Soubor se změnil nebo ho klient ještě nemá načtený. */
franta-hg@64
    84
		odpoved.setContentType(MIME_TYP);
franta-hg@64
    85
		odpoved.setContentLength((int) soubor.length());
franta-hg@64
    86
		odpoved.setDateHeader("Last-Modified", soubor.lastModified());
franta-hg@64
    87
franta-hg@69
    88
		ServletOutputStream vystup = odpoved.getOutputStream();
franta-hg@69
    89
		InputStream vstup = new FileInputStream(soubor);
franta-hg@64
    90
franta-hg@64
    91
		try {
franta-hg@69
    92
		    byte[] zasobnik = new byte[1024];
franta-hg@64
    93
		    int bajtuNacteno;
franta-hg@69
    94
		    while ((bajtuNacteno = vstup.read(zasobnik)) != -1) {
franta-hg@69
    95
			vystup.write(zasobnik, 0, bajtuNacteno);
franta-hg@64
    96
		    }
franta-hg@64
    97
		} catch (Exception e) {
franta-hg@64
    98
		    throw new ServletException("Chyba při odesílání obrázku klientovi.", e);
franta-hg@64
    99
		} finally {
franta-hg@69
   100
		    vstup.close();
franta-hg@69
   101
		    vystup.close();
franta-hg@64
   102
		}
franta-hg@64
   103
	    } else {
franta-hg@64
   104
		/** Soubor se od posledního načtení klientem nezměnil → není potřeba ho posílat znova. */
franta-hg@64
   105
		odpoved.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
franta-hg@64
   106
	    }
franta-hg@64
   107
franta-hg@64
   108
	} else {
franta-hg@64
   109
	    /** Neexistující nebo nečitelný soubor → HTTP 404 chyba */
franta-hg@64
   110
	    odpoved.sendError(HttpServletResponse.SC_NOT_FOUND);
franta-hg@64
   111
	}
franta-hg@64
   112
    }
franta-hg@64
   113
franta-hg@64
   114
    /**
franta-hg@65
   115
     * @param cesta cesta požadovaná klientem: <code>request.getPathInfo()</code>
franta-hg@64
   116
     * @throws ServletException pokud cesta nevyhovuje vzoru
franta-hg@64
   117
     */
franta-hg@64
   118
    private static String zkontrolujParametr(String cesta) throws ServletException {
franta-hg@64
   119
	if (Pattern.matches(VZOR_CESTY, cesta)) {
franta-hg@64
   120
	    /** cesta je v pořádku → pokračujeme */
franta-hg@64
   121
	    return cesta;
franta-hg@64
   122
	} else {
franta-hg@64
   123
	    /** Chybná cesta → HTTP 500 chyba */
franta-hg@64
   124
	    throw new ServletException("Chybná cesta k obrázku: " + cesta);
franta-hg@64
   125
	}
franta-hg@64
   126
    }
franta-hg@64
   127
}