imagehandler.java
来自「一款Java实现的HTTP代理服务器」· Java 代码 · 共 331 行
JAVA
331 行
package rabbit.handler;import java.io.BufferedReader;import java.io.File;import java.io.FileReader;import java.io.IOException;import java.nio.ByteBuffer;import rabbit.http.HttpHeader;import rabbit.proxy.Connection;import rabbit.proxy.FileResourceSource;import rabbit.proxy.HttpProxy;import rabbit.proxy.TrafficLoggerHandler;import rabbit.util.Logger;import rabbit.util.SProperties;/** This handler first downloads the image runs convert on it and * then serves the smaller image. * @author <a href="mailto:robo@khelekore.org">Robert Olofsson</a> */public class ImageHandler extends BaseHandler { private static final String STD_CONVERT = "/usr/bin/convert"; private static final String STD_CONVERT_ARGS = "-quality 10 -flatten $filename +profile \"*\" jpeg:$filename.c"; private SProperties config = new SProperties (); private boolean doConvert = true; private boolean converted = false; private long lowQualitySize = -1; private File convertedFile = null; private File typeFile = null; /** For creating the factory. */ public ImageHandler () { } /** Create a new ImageHandler for the given request. * @param con the Connection handling the request. * @param request the actual request made. * @param clientBuffer the client side buffer. * @param response the actual response. * @param content the resource. * @param mayCache May we cache this request? * @param mayFilter May we filter this request? * @param size the size of the data beeing handled. */ public ImageHandler (Connection con, TrafficLoggerHandler tlh, HttpHeader request, ByteBuffer clientBuffer, HttpHeader response, ResourceSource content, boolean mayCache, boolean mayFilter, long size, SProperties config, boolean doConvert) { super (con, tlh, request, clientBuffer, response, content, mayCache, mayFilter, size); if (size == -1) con.setKeepalive (false); con.setChunking (false); this.config = config; this.doConvert = doConvert; } public Handler getNewInstance (Connection con, TrafficLoggerHandler tlh, HttpHeader header, ByteBuffer buffer, HttpHeader webHeader, ResourceSource content, boolean mayCache, boolean mayFilter, long size) { return new ImageHandler (con, tlh, header, buffer, webHeader, content, mayCache, mayFilter, size, config, doConvert); } /** * ®return true this handler modifies the content. */ @Override public boolean changesContentSize () { return true; } /** Images needs to be cacheable to be compressed. * @return true */ @Override protected boolean mayCacheFromSize () { return true; } /** Check if this handler may force the cached resource to be less than the cache max size. * @return false */ @Override protected boolean mayRestrictCacheSize () { return false; } /** Try to convert the image before letting the superclass handle it. */ @Override public void handle () { try { tryconvert (); } catch (IOException e) { failed (e); } } @Override protected void addCache () { if (!converted) super.addCache (); // if we get here then we have converted the image // and do not want a cache... } /** clear up the mess we made (remove intermediate files etc). */ @Override protected void finish (boolean good) { try { if (convertedFile != null) convertedFile.delete (); } finally { super.finish (good); } } /** Remove the cachestream and the cache entry. */ @Override protected void removeCache () { super.removeCache (); if (convertedFile != null) convertedFile.delete (); } /** Try to convert the image. This is done like this: * <xmp> * super.addCache (); * readImage(); * convertImage(); * cacheChannel = null; * </xmp> * We have to use the cachefile to convert the image, and if we * convert it we dont want to write the file to the cache later * on. */ protected void tryconvert () throws IOException { if (doConvert && mayFilter && mayCache && size > 2000) { super.addCache (); // check if cache setup worked. if (cacheChannel == null) super.handle (); else readImage (); } else { super.handle (); } } /** Read in the image * @throws IOException if reading of the image fails. */ protected void readImage () throws IOException { content.addBlockListener (new ImageReader ()); } private class ImageReader implements BlockListener { public void bufferRead (ByteBuffer buf) { // TODO: another thread? try { writeCache (buf); totalRead += buf.remaining (); buf.position (buf.limit ()); content.addBlockListener (this); } catch (IOException e) { failed (e); } } public void finishedRead () { try { if (size > 0 && totalRead != size) setPartialContent (totalRead, size); cacheChannel.close (); cacheChannel = null; convertImage (); } catch (IOException e) { failed (e); } } public void failed (Exception cause) { ImageHandler.this.failed (cause); } public void timeout () { ImageHandler.this.failed (new IOException ("Timeout")); } } private void closeStreams (Process ps) throws IOException { ps.getInputStream ().close (); ps.getOutputStream ().close (); ps.getErrorStream ().close (); } /** Convert the image into a small low quality image (normally a jpeg). * @throws IOException if conversion fails. */ protected void convertImage () { con.getProxy ().runThreadTask (new Runnable () { public void run () { try { internalConvertImage (); converted = true; returnOk (); } catch (IOException e) { returnWithFailure (e); } } }); } private void returnWithFailure (final Exception cause) { con.getProxy ().runMainTask (new Runnable () { public void run () { failed (cause); } }); } private void returnOk () { con.getProxy ().runMainTask (new Runnable () { public void run () { // resume normal operations... ImageHandler.super.handle (); } }); } protected void internalConvertImage () throws IOException { long origSize = size; String convert = config.getProperty ("convert", STD_CONVERT); String convargs = config.getProperty ("convertargs", STD_CONVERT_ARGS); int idx = 0; HttpProxy proxy = con.getProxy (); String entryName = proxy.getCache ().getEntryName (entry.getId (), false); try { while ((idx = convargs.indexOf ("$filename")) > -1) { convargs = convargs.substring (0, idx) + entryName + convargs.substring (idx + "$filename".length()); } String command = convert + " " + convargs; getLogger ().logAll ("ImageHandler running: '" + command + "'"); Process ps = Runtime.getRuntime ().exec (command); try { ps.waitFor (); closeStreams (ps); int exitValue = ps.exitValue (); if (exitValue != 0) { getLogger ().logWarn ("Bad conversion: " + entryName + ", got exit value: " + exitValue); throw new IOException ("failed to convert image, " + "exit value: " + exitValue); } } catch (InterruptedException e) { getLogger ().logWarn ("Interupted during wait for: " + entryName); } convertedFile = new File (entryName + ".c"); typeFile = new File (entryName + ".type"); lowQualitySize = convertedFile.length (); if (lowQualitySize > 0 && origSize > lowQualitySize) { String ctype = checkFileType (typeFile); response.setHeader ("Content-Type", ctype); /** We need to remove the existing file first for * windows system, they will not overwrite files in a move. * Spotted by: Michael Mlivoncic */ File oldEntry = new File (entryName); if (oldEntry.exists ()) oldEntry.delete (); if (convertedFile.renameTo (new File (entryName))) convertedFile = null; else getLogger ().logWarn ("rename failed: " + convertedFile.getName () + " => " + entryName); } } finally { if (convertedFile != null) convertedFile.delete (); if (typeFile != null && typeFile.exists ()) typeFile.delete (); } size = (lowQualitySize < origSize ? lowQualitySize : origSize); response.setHeader ("Content-length", "" + size); con.setExtraInfo ("imageratio:" + origSize + "/" + lowQualitySize + "=" + ((float)lowQualitySize / origSize)); content.release (con); content = new FileResourceSource (entryName, con.getProxy ()); convertedFile = null; } private String checkFileType (File typeFile) throws IOException { String ctype = "image/jpeg"; if (typeFile.exists () && typeFile.length() > 0) { BufferedReader br = null; try { br = new BufferedReader (new FileReader (typeFile)); ctype = br.readLine(); } finally { if (br != null) br.close (); } } return ctype; } @Override public void setup (Logger logger, SProperties prop) { if (prop != null) { config = prop; doConvert = true; String conv = prop.getProperty ("convert", STD_CONVERT); File f = new File (conv); if (!f.exists () || !f.isFile()) { logger.logWarn ("convert -" + conv + "- not found, is your path correct?"); doConvert = false; } } }}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?