Zip File Web Server in Java

This is a small bit of glue code to let you serve the contents of a zip file over HTTP. URL request paths are simply mapped to entries in the zip file by path/name. This server will extract content on-the-fly when you request it. This can be helpful for browsing very large zip files with many files inside.

For example, if you go to the Python Pandas documentation and download the pandas.zip file, you can serve it from your computer without unzipping it.

Note: you need to visit a page listed in the zip file, browsing to the root will not work.

Invoke it like this:
          java ZipWebServer 8080 ~/Downloads/jdk-7u6-apidocs.zip

If you don't have Java, it's free, here.

Code

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

/*
 * Serve contents of a zip file over HTTP.
 */
public class ZipWebServer implements HttpHandler {

    static final DateFormat df = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss ZZZ", Locale.ENGLISH);

    static {
        df.setTimeZone(TimeZone.getTimeZone("GMT"));
    }

    static void logOutput(String string) {
        System.out.println(new Date() + " WEB: " + string);
    }

    static void send(HttpExchange exchange, long len, long lastModified, InputStream in) throws IOException {
        exchange.getResponseHeaders().add("Last-Modified", df.format(new Date(lastModified)));
        exchange.sendResponseHeaders(200, len);
        byte[] buffer = new byte[4096];
        int bytesRead;
        OutputStream out = exchange.getResponseBody();
        while ((bytesRead = in.read(buffer)) != -1) {
            out.write(buffer, 0, bytesRead);
        }
    }

    ZipFile zip;

    public ZipWebServer(ZipFile zip) {
        this.zip = zip;
    }

    @Override
    public void handle(HttpExchange exchange) {
        URI url = exchange.getRequestURI();
        logOutput(exchange.getRequestMethod() + " " + url.toASCIIString());
        InputStream in = null;
        try {
            String path = URLDecoder.decode(url.toString(), Charset.defaultCharset().name()).substring(1);
            ZipEntry e = zip.getEntry(path);
            in = zip.getInputStream(e);
            send(exchange, e.getSize(), e.getTime(), in);
        }
        catch (Exception e) {
        }
        finally {
            if (in != null) {
                try {
                    in.close();
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
            }
            exchange.close();
        }
    }

    public static void main(String[] args) throws Exception {
        if (args.length != 2) {
            System.out.println("Usage:");
            System.out.println("      java ZipWebServer 8080 ~/Downloads/jdk-7u6-apidocs.zip");
            return;
        }

        int port = Integer.parseInt(args[0]);
        ZipFile zip = new ZipFile(args[1]);

        HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
        server.createContext("/", new ZipWebServer(zip));

        logOutput("Server started on: " + server.getAddress());
        logOutput("Home: " + zip.getName());
        server.start();
    }
}