⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 fileservlet.java

📁 用java开发的
💻 JAVA
字号:
// FileServlet - servlet similar to a standard httpd
//
// Copyright (C)1996,1998 by Jef Poskanzer <jef@acme.com>. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
// Visit the ACME Labs Java page for up-to-date versions of this and other
// fine Java utilities: http://www.acme.com/java/
// 
// All enhancements Copyright (C)1998-2005 by Dmitriy Rogatkin
// http://tjws.sourceforge.net

package Acme.Serve;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import Acme.Utils;

/// Servlet similar to a standard httpd.
// <P>
// Implements the "GET" and "HEAD" methods for files and directories.
// Handles index.html, index.htm, default.htm, default.html.
// Redirects directory URLs that lack a trailing /.
// Handles If-Modified-Since.
// <P>
// <A HREF="/resources/classes/Acme/Serve/FileServlet.java">Fetch the software.</A><BR>
// <A HREF="/resources/classes/Acme.tar.Z">Fetch the entire Acme package.</A>
// <P>
// @see Acme.Serve.Serve

public class FileServlet extends HttpServlet {

	// We keep a single throttle table for all instances of the servlet.
	// Normally there is only one instance; the exception is subclasses.
	static Acme.WildcardDictionary throttleTab = null;

	static final String[] DEFAULTINDEXPAGES = { "index.html", "index.htm", "default.htm", "default.html" };

	static final DecimalFormat lengthftm = new DecimalFormat("#");

	static final String BYTES_UNIT = "bytes";

	protected String charSet = Serve.UTF8;// "iso-8859-1";

	private static final boolean logenabled = false;

	// true;

	private Method canExecute, getFreeSpace;

	// / Constructor.
	public FileServlet() {
		try {
			canExecute = File.class.getMethod("canExecute", Utils.EMPTY_CLASSES);
		} catch (SecurityException e) {
		} catch (NoSuchMethodException e) {
		}
		try {
			getFreeSpace = File.class.getMethod("getFreeSpace", Utils.EMPTY_CLASSES);
		} catch (SecurityException e) {
		} catch (NoSuchMethodException e) {
		}
	}

	// / Constructor with throttling.
	// @param throttles filename containing throttle settings
	// @param charset used for displaying directory page
	// @see ThrottledOutputStream
	public FileServlet(String throttles, String charset) throws IOException {
		this();
		if (charset != null)
			this.charSet = charset;
		readThrottles(throttles);
	}

	private void readThrottles(String throttles) throws IOException {
		Acme.WildcardDictionary newThrottleTab = ThrottledOutputStream.parseThrottleFile(throttles);
		if (throttleTab == null)
			throttleTab = newThrottleTab;
		else {
			// Merge the new one into the old one.
			Enumeration keys = newThrottleTab.keys();
			Enumeration elements = newThrottleTab.elements();
			while (keys.hasMoreElements()) {
				Object key = keys.nextElement();
				Object element = elements.nextElement();
				throttleTab.put(key, element);
			}
		}
	}

	// / Returns a string containing information about the author, version, and
	// copyright of the servlet.
	public String getServletInfo() {
		return "servlet similar to a standard httpd";
	}

	// / Services a single request from the client.
	// @param req the servlet request
	// @param req the servlet response
	// @exception ServletException when an exception has occurred
	public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		boolean headOnly;
		if (req.getMethod().equalsIgnoreCase("get"))
			headOnly = false;
		else if (req.getMethod().equalsIgnoreCase("head"))
			headOnly = true;
		else {
			res.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
			return;
		}
		req.setCharacterEncoding(Serve.UTF8);
		String path = Utils.canonicalizePath(req.getPathInfo());
		//res.setBufferSize(Utils.COPY_BUF_SIZE/2);
		dispatchPathname(req, res, headOnly, path);
	}

	private void dispatchPathname(HttpServletRequest req, HttpServletResponse res, boolean headOnly, String path)
			throws IOException {
		String filename = req.getPathTranslated() != null ? req.getPathTranslated().replace('/', File.separatorChar)
				: "";
		File file = new File(filename);
		log("showing '" + filename + "' for path " + path);
		if (file.exists()) {
			if (!file.isDirectory())
				serveFile(req, res, headOnly, path, file);
			else {
				log("showing dir " + file);
				if (redirectDirectory(req, res, path, file) == false)
					showIdexFile(req, res, headOnly, path, filename);
			}
		} else
			res.sendError(HttpServletResponse.SC_NOT_FOUND);
	}

	private void showIdexFile(HttpServletRequest req, HttpServletResponse res, boolean headOnly, String path,
			String parent) throws IOException {
		log("showing index in directory " + parent);
		for (int i = 0; i < DEFAULTINDEXPAGES.length; i++) {
			File indexFile = new File(parent, DEFAULTINDEXPAGES[i]);
			if (indexFile.exists()) {
				serveFile(req, res, headOnly, path, indexFile);
				return;
			}
		}
		// index not found
		serveDirectory(req, res, headOnly, path, new File(parent));
	}

	private void serveFile(HttpServletRequest req, HttpServletResponse res, boolean headOnly, String path, File file)
			throws IOException {
		log("getting " + file);
		if (logenabled) {
			Enumeration enh = req.getHeaderNames();
			while(enh.hasMoreElements()) {
				String hn = (String )enh.nextElement();
				log("hdr:"+hn+":"+req.getHeader(hn));
			}
		}
		if (!file.canRead()) {
			res.sendError(HttpServletResponse.SC_FORBIDDEN);
			return;
		} else
			// by Niel Markwick
			try {
				file.getCanonicalPath();
			} catch (Exception e) {
				res.sendError(HttpServletResponse.SC_FORBIDDEN, "Forbidden, exception:" + e);
				return;
			}

		// Handle If-Modified-Since.
		res.setStatus(HttpServletResponse.SC_OK);
		long lastMod = file.lastModified();
		String ifModSinceStr = req.getHeader("If-Modified-Since");
		long ifModSince = -1;
		if (ifModSinceStr != null) {
			int semi = ifModSinceStr.indexOf(';');
			if (semi != -1)
				ifModSinceStr = ifModSinceStr.substring(0, semi);
			try {
				ifModSince = DateFormat.getDateInstance().parse(ifModSinceStr).getTime();
			} catch (Exception ignore) {
				log("Can't parse " + ifModSinceStr + " " + ignore);
			}
		}
		if (ifModSince != -1 && ifModSince >= lastMod) {
			res.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
			headOnly = true;
		}
		// TODO add processing If-None-Match, If-Unmodified-Since and If-Match
		res.setContentType(getServletContext().getMimeType(file.getName()));
		long flen = file.length();
		// check for range
		String range = req.getHeader("Range");
		long sr = 0;
		long er = -1;
		if (range != null) {
			log("Range:"+range);
			if (range.regionMatches(true, 0, BYTES_UNIT, 0, BYTES_UNIT.length())) {
				int i = range.indexOf('-');
				if (i > 0) {
					try {
						sr = Long.parseLong(range.substring(BYTES_UNIT.length() + 1, i));
						if (sr < 0)
							throw new NumberFormatException("Invalid start range value:"+sr);
						try {
							er = Long.parseLong(range.substring(i + 1));
						}catch(NumberFormatException nfe) {
							er = flen-1;
						}
					}catch(NumberFormatException nfe) {
						
					}
				} // else invalid range? ignore?
			} // else other units not supported
			log("range values "+sr+ " to "+er);
		}
		long clen = er < 0 ? flen : (er - sr + 1);
		if (clen < Integer.MAX_VALUE)
			res.setContentLength((int) clen);
		else
			res.setHeader("Content-Length", Long.toString(clen));
		res.setDateHeader("Last-modified", lastMod);

		if (er > 0) {
			if (sr > er || er >= flen) {
				// TODO If-Range presence can change behavior
				res.setHeader("Content-Range", BYTES_UNIT + " */" + flen);
				res.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
				return;
			}
			res.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
			res.setHeader("Content-Range", BYTES_UNIT + " " + sr + '-' + er + '/' + flen);
			log("content-range:"+BYTES_UNIT + " " + sr + '-' + er + '/' + flen);
		}
		// String ifRange = req.getHeader("If-Range");
		// res.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);

		OutputStream out = null;
		InputStream in = null;
		try {
			out = res.getOutputStream();
			if (!headOnly) {
				// Check throttle.
				if (throttleTab != null) {
					ThrottleItem throttleItem = (ThrottleItem) throttleTab.get(path);
					if (throttleItem != null) {
						// !!! Need to count for multiple simultaneous fetches.
						out = new ThrottledOutputStream(out, throttleItem.getMaxBps());
					}
				}

				in = new FileInputStream(file);
				while (sr > 0) {
					long sl = in.skip(sr);
					if (sl > 0)
						sr -= sl;
					else {
						res.sendError(HttpServletResponse.SC_CONFLICT, "Conflict");
						// better can be Internal Server Error
						return;
					}
				}
				copyStream(in, out, clen);
			}
		} finally {
			if (in != null)
				try {
					in.close();
				} catch (IOException ioe) {
				}
			if (out != null) {
				out.flush();
				out.close();
			}
		}
	}

	// / Copy a file from in to out.
	// Sub-classes can override this in order to do filtering of some sort.
	public void copyStream(InputStream in, OutputStream out, long len) throws IOException {
		Acme.Utils.copyStream(in, out, len);
	}

	private void serveDirectory(HttpServletRequest req, HttpServletResponse res, boolean headOnly, String path,
			File file) throws IOException {
		log("indexing " + file);
		if (!file.canRead()) {
			res.sendError(HttpServletResponse.SC_FORBIDDEN);
			return;
		}
		res.setStatus(HttpServletResponse.SC_OK);
		res.setContentType("text/html;charset=" + charSet);
		OutputStream out = res.getOutputStream();
		if (!headOnly) {
			PrintStream p = new PrintStream(new BufferedOutputStream(out), false, charSet); // 1.4
			p.println("<HTML><HEAD>");
			p.println("<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=" + charSet + "\">");
			p.println("<TITLE>Index of " + path + "</TITLE>");
			p.println("</HEAD><BODY " + Serve.BGCOLOR);
			p.println("><H2>Index of " + path + "</H2>");
			p.println("<PRE>");
			p.println("mode         bytes  last-changed    name");
			p.println("<HR>");
			String[] names = file.list();
			// TODO consider not case sensetive search
			Arrays.sort(names);
			long total = 0;
			for (int i = 0; i < names.length; ++i) {
				File aFile = new File(file, names[i]);
				String aFileType;
				long aFileLen;
				if (aFile.isDirectory())
					aFileType = "d";
				else if (aFile.isFile())
					aFileType = "-";
				else
					aFileType = "?";
				String aFileRead = (aFile.canRead() ? "r" : "-");
				String aFileWrite = (aFile.canWrite() ? "w" : "-");
				String aFileExe = "-";
				if (canExecute != null)
					try {
						if (((Boolean) canExecute.invoke(aFile, Utils.EMPTY_OBJECTS)).booleanValue())
							aFileExe = "x";
					} catch (IllegalArgumentException e) {
					} catch (IllegalAccessException e) {
					} catch (InvocationTargetException e) {
					}
				String aFileSize = lengthftm.format(aFileLen = aFile.length());
				total += (aFileLen + 1023) / 1024; // 
				while (aFileSize.length() < 12)
					aFileSize = " " + aFileSize;
				String aFileDate = Acme.Utils.lsDateStr(new Date(aFile.lastModified()));
				while (aFileDate.length() < 14)
					aFileDate += " ";
				String aFileDirsuf = (aFile.isDirectory() ? "/" : "");
				String aFileSuf = (aFile.isDirectory() ? "/" : "");
				p.println(aFileType + aFileRead + aFileWrite + aFileExe + "  " + aFileSize + "  " + aFileDate + "  "
						+ "<A HREF=\"" + URLEncoder.encode(names[i], charSet) /* 1.4 */
						+ aFileDirsuf + "\">" + names[i] + aFileSuf + "</A>");
			}
			if (total != 0)
				total += 3;
			p.println("total " + total);
			p.println("</PRE>");
			p.println("<HR>");
			Serve.Identification.writeAddress(p);
			p.println("</BODY></HTML>");
			p.flush();
		}
		out.close();
	}

	/**
	 * 
	 * @param req
	 *            http request
	 * @param res
	 *            http response
	 * @param path
	 *            web path
	 * @param file
	 *            file system path
	 * @return true if redirection required and happened
	 * @throws IOException
	 *             in redirection
	 */
	private boolean redirectDirectory(HttpServletRequest req, HttpServletResponse res, String path, File file)
			throws IOException {
		int pl = path.length();
		if (pl > 0 && path.charAt(pl - 1) != '/') {
			// relative redirect
			int sp = path.lastIndexOf('/');
			if (sp < 0)
				path += '/';
			else
				path = path.substring(sp + 1) + '/';
			log("redirecting dir " + path);
			res.sendRedirect(path);
			return true;
		}
		return false;
	}

	public void log(String msg) {
		if (logenabled)
			super.log(msg);
	}
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -