public void sendFile(ChannelHandlerContext ctx, MessageEvent e,
			final File file) throws IOException {
		RandomAccessFile raf;
		try {
			raf = new RandomAccessFile(file, "r");
		} catch (FileNotFoundException fnfe) {
			sendError(ctx, NOT_FOUND);
			return;
		}
		long fileLength = raf.length();

		HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
		response.setHeader("UPTYPE", "file");
		response.setHeader("UPNAME", file.getName());
		response.setHeader(Names.CACHE_CONTROL, file.lastModified());
		setContentLength(response, fileLength);
		setContentTypeHeader(response, file);

		Channel ch = e.getChannel();

		// Write the initial line and the header.
		ch.write(response);

		// Write the content.
		ChannelFuture writeFuture;
		if (ch.getPipeline().get(SslHandler.class) != null) {
			// Cannot use zero-copy with HTTPS.
			writeFuture = ch.write(new ChunkedFile(raf, 0, fileLength, 8192));
		} else {
			// No encryption - use zero-copy.
			final FileRegion region = new DefaultFileRegion(raf.getChannel(),
					0, fileLength);
			writeFuture = ch.write(region);
			writeFuture.addListener(new ChannelFutureProgressListener() {
				public void operationComplete(ChannelFuture future) {
					region.releaseExternalResources();
				}

				public void operationProgressed(ChannelFuture future,
						long amount, long current, long total) {
					String path = file.getPath();
					logger.info(path + ":" + current + "/" + total + " (+"
							+ amount + ")");
				}
			});
		}

		// Decide whether to close the connection or not.
		if (!isKeepAlive((HttpRequest) e.getMessage())) {
			// Close the connection when the whole content is written out.
			writeFuture.addListener(ChannelFutureListener.CLOSE);
		}
	}