在这篇中,我要根据现有的成果,做一个真正可用的小产品——WebShare。
在给学生上课的时候,经常遇到要共享文件的情况。Windows的文件共享是好用,但经常弹出没有权限的错误。自己也可以启动个Tomcat或是IIS,不过还是比较费资源的!那么是不是可以自己写个简单的Web服务器,它只用来共享文件呢?当然可以了!开始动手改吧!
以上便是WebShare诞生的灵感。简单说它就像Windows资源浏览器一样,只不过是通过80端口,可以使用浏览器下载文件的一个小服务器。话不多说,还是看代码吧!
这个文件是用来构造目录响应页面的模版文件。
package com.net;
public class TemplateDir {
StringBuilder filesList = new StringBuilder();
String htmlHead = "<!DOCTYPE HTML PUBLIC /"-//W3C//DTD HTML 4.0 Transitional//EN/"><HTML><HEAD><META http-equiv=Content-Type content=/"text/html; charset=gb2312/"></HEAD><BODY>";
String title = "<h1>你好,目录中有以下文件:</h1><BR><table>";
String htmlBody = "</table><h2>Allen WebShare Server1.0</BODY></HTML>";
public String getResponseBody() {
return htmlHead + title + filesList.toString() + htmlBody;
}
public void insertAFile(String fileRelativePath,String fineName,long size) {
filesList.append("<tr><td>"+fineName+"</td>");
filesList.append("<td>"+size+"</td>");
filesList.append("<td><a href=/"" + fileRelativePath + "/">打开文件</a></td></tr>");
}
public void insertADIR(String fileRelativePath,String fineName){
filesList.append("<tr><td>"+fineName+"</td>");
filesList.append("<td>...</td>");
filesList.append("<td><a href=/"" + fileRelativePath + "/">打开目录</a></td></tr>");
}
}
如果找不到文件或是出错了,那么输出这个模版文件
package com.net;
public class TemplateError {
StringBuilder errorInfo = new StringBuilder();
String htmlHead = "<!DOCTYPE HTML PUBLIC /"-//W3C//DTD HTML 4.0 Transitional//EN/"><HTML><HEAD><META http-equiv=Content-Type content=/"text/html; charset=gb2312/"></HEAD><BODY>";
String title = "出错信息<BR>";
String htmlBody = "</BODY></HTML>";
public String getResponseBody() {
return htmlHead + title + errorInfo.toString() + htmlBody;
}
public void insertError(String error) {
errorInfo.append(error + "<BR>");
}
}
一个简单的界面
package com;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import javax.swing.ImageIcon;
import javax.swing.InputVerifier;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JTextField;
import com.net.Server;
/**
*
* @author allen
*/
public class Main extends javax.swing.JFrame {
Server server;
/** Creates new form ServerFrame */
public Main() {
server = Server.getInstance();
try {
Server.out = new PrintStream("E://weblog.txt") {
public void println(String s) {
super.println();
super.flush();
logArea.append(s + "/n");
}
};
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
initComponents();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
this.setSize(500, 360);
// System.out.println("x=" + e.getX() + ";y=" + e.getY());
// this.addMouseListener(new MouseListener(){});
startButton = new javax.swing.JButton();
stopButton = new javax.swing.JButton();
jScrollPane1 = new javax.swing.JScrollPane();
logArea = new javax.swing.JTextArea();
statusLabel = new javax.swing.JLabel();
webRootPath = new javax.swing.JTextArea();
pathChoose = new javax.swing.JButton();
pathLabel = new javax.swing.JLabel();
portLabel = new javax.swing.JLabel();
webPort = new javax.swing.JTextField();
portChoose = new javax.swing.JButton();
allenIcon = new javax.swing.JLabel();
copyRight = new javax.swing.JLabel();
copyRight.setText("Code By Allen:) Email:akalong513@gmail.com");
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setTitle("Web文件共享器"); // NOI18N
startButton.setText("启动"); // NOI18N
startButton.setToolTipText("启动服务器!"); // NOI18N
startButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
startButtonActionPerformed(evt);
}
});
stopButton.setText("停止"); // NOI18N
stopButton.setToolTipText("停止服务器!"); // NOI18N
stopButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
stopButtonActionPerformed(evt);
}
});
logArea.setColumns(20);
logArea.setRows(5);
jScrollPane1.setViewportView(logArea);
statusLabel.setText("服务器日志!"); // NOI18N
webRootPath.setText(Server.appRootPath); // NOI18N
webRootPath.setToolTipText("文件共享根路径!"); // NOI18N
pathChoose.setText("更改"); // NOI18N
pathChoose.setToolTipText("更改文件共享根路径!"); // NOI18N
pathChoose.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
pathChooseActionPerformed(evt);
}
});
pathLabel.setText("ServerRoot:"); // NOI18N
portLabel.setText("ServerPort:"); // NOI18N
webPort.setText(String.valueOf(Server.PORT)); // NOI18N
portChoose.setText("更改"); // NOI18N
portChoose.setToolTipText("更改服务器端口!"); // NOI18N
portChoose.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
changePort(evt);
}
});
allenIcon.setIcon(new ImageIcon("icon.jpg")); // NOI18N
allenIcon.setText("Allen制作!"); // NOI18N
allenIcon.setName("allenIcon"); // NOI18N
getContentPane().setLayout(null);
startButton.setBounds(120, 250, 60, 20);
stopButton.setBounds(220, 250, 60, 20);
jScrollPane1.setBounds(10, 30, 450, 150);
statusLabel.setBounds(20, 0, 200, 30);
pathLabel.setBounds(20, 200, 120, 20);
portLabel.setBounds(20, 220, 120, 20);
webRootPath.setBounds(100, 200, 170, 20);
webPort.setBounds(100, 220, 170, 20);
pathChoose.setBounds(280, 200, 60, 20);
portChoose.setBounds(280, 220, 60, 20);
allenIcon.setBounds(350, 190, 130, 130);
copyRight.setBounds(30, 310, 300, 20);
this.add(startButton);
this.add(stopButton);
this.add(jScrollPane1);
this.add(statusLabel);
this.add(webRootPath);
this.add(pathChoose);
this.add(portChoose);
this.add(pathLabel);
this.add(portLabel);
this.add(webPort);
this.add(copyRight);
this.add(allenIcon);
this.setResizable(false);
}
private void startButtonActionPerformed(java.awt.event.ActionEvent evt) {
if (this.server == null) {
this.server = Server.getInstance();
}
if (!this.server.isServerOn()) {
server.startServer();
}
}
private void stopButtonActionPerformed(java.awt.event.ActionEvent evt) {
if (this.server == null) {
this.server = Server.getInstance();
}
if (this.server.isServerOn()) {
server.stopServer();
}
}
private void changePort(java.awt.event.ActionEvent evt) {
try {
Server.PORT = Integer.parseInt(webPort.getText());
} catch (Exception e) {
javax.swing.JOptionPane.showMessageDialog(this, "请输入正确的端口号!");
}
}
/**
* @param args
* the command line arguments
*/
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new Main().setVisible(true);
}
});
}
private void pathChooseActionPerformed(java.awt.event.ActionEvent evt) {
JFileChooser chooser = new JFileChooser();
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
int option = chooser.showOpenDialog(this);
if (option == JFileChooser.APPROVE_OPTION) {
try {
String tmpPath = "e://webroot//";
if (chooser.getSelectedFile() != null) {
tmpPath = chooser.getSelectedFile().getCanonicalPath();
}
if (!tmpPath.substring(tmpPath.length() - 1).equals("//")) {
tmpPath = tmpPath + "//";
}
Server.appRootPath = tmpPath;
webRootPath.setText(Server.appRootPath);
statusLabel.setText("You choose:" + Server.appRootPath);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
statusLabel.setText("You canceled.");
}
}
// Variables declaration - do not modify
private javax.swing.JLabel allenIcon;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTextArea logArea;
private javax.swing.JButton pathChoose;
private javax.swing.JLabel pathLabel;
private javax.swing.JButton portChoose;
private javax.swing.JLabel portLabel;
private javax.swing.JButton startButton;
private javax.swing.JLabel statusLabel;
private javax.swing.JButton stopButton;
private javax.swing.JTextField webPort;
private javax.swing.JTextArea webRootPath;
private javax.swing.JLabel copyRight;
// End of variables declaration
}
Server.java
package com.net;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.StringTokenizer;
/**
* @author allen
* @version 0.2
*/
public class Server {
public static void main(String[] args) throws InterruptedException {
Server server = Server.getInstance();
server.startServer();
Thread.sleep(10000);
server.stopServer();
}
private static Server SERVER;
public static PrintStream out = System.out;
public static String appRootPath = "E://webroot//";
public static int PORT = 80;
private boolean isServerOn = false;
ServerSocket ss;
Thread serverThread;
private Server() {
}
/**
* 用于获得服务器实例。
*
* @return
*/
public synchronized static Server getInstance() {
if (SERVER == null) {
SERVER = new Server();
}
return SERVER;
}
/**
* 启动服务器。
*/
public void startServer() {
isServerOn = true;
serverThread = new Thread(new Runnable() {
public void run() {
try {
ss = new ServerSocket(Server.PORT);
// 一直循环,知道遇到中断!(用于关闭服务器)
while (true && !Thread.interrupted()) {
Socket s = ss.accept();
HttpRequest request = new HttpRequest();
BufferedReader br = new BufferedReader(
new InputStreamReader(s.getInputStream()));
String requestLine = "";
char[] body;
boolean haveBody = false;
int length = 0;
boolean ifFirst = true;
do {
requestLine = br.readLine();
// Server.out.println(requestLine);
if (!requestLine.equals("")) {
if (ifFirst) {
request.addFirstLine(requestLine);
ifFirst = false;
} else {
request.addHead(requestLine);
}
// post方式提交的请求还要取得,请求体的内容
if (requestLine.startsWith("Content-Length")) {
StringTokenizer st = new StringTokenizer(
requestLine, ":");
st.nextToken();
String bodyLength = st.nextToken().trim();
length = Integer.parseInt(bodyLength);
haveBody = true;
}
}
if (requestLine.equals("") && haveBody) {
body = new char[length];
br.read(body, 0, length);
request.addBody(new String(body));
}
} while (!requestLine.equals(""));
// 开始响应
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(s.getOutputStream()));
HttpResponse response = new HttpResponse();
// Service.doService(request, response, bw);
Service.doService(request, response, s
.getOutputStream());
br.close();
s.close();
}
ss.close();
Server.out.println("服务器正常退出!");
isServerOn = false;
} catch (BindException e) {
Server.out.println("80端口已被占用!");
isServerOn = false;
} catch (IOException e) {
Server.out.println("服务器异常退出!");
isServerOn = false;
} finally {
// 资源清理代码
try {
if (ss != null) {
ss.close();
}
} catch (IOException e) {
}
}
}
});
serverThread.start();
}
public void stopServer() {
isServerOn = false;
serverThread.interrupt();
}
public boolean isServerOn() {
return isServerOn;
}
}
Service.java
package com.net;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
public class Service {
// public static String DefaultPage = "/index.html";
public static String DefaultPage = "";
public static void doService(HttpRequest request, HttpResponse response,
OutputStream os) {
// System.out.println(request.getAllRequestString());
FileReader htmlFR;
long fileLength = 0;
try {
String path = request.getRequestPath();
File requestFile = new File(Server.appRootPath.substring(0,
Server.appRootPath.length() - 1)
+ path);
if (requestFile.exists()) {
// 基于安全,如果是相对路径,可能访问私有文件。
String canonicalPath = requestFile.getCanonicalPath();
Server.out.println("Path:" + canonicalPath);
String tmp1 = canonicalPath.toLowerCase().replaceAll("",
"@");
String tmp2 = Server.appRootPath.toLowerCase().replaceAll(
"", "@");
tmp2 = tmp2.substring(0, tmp2.length() - 1);
if (tmp1.startsWith(tmp2)) {
// System.out.println("安全" + tmp1 + "|||||" + tmp2);
} else {
Server.out.println("不安全" + tmp1 + "|||||" + tmp2);
TemplateError error = new TemplateError();
error.insertError("超出范围!");
fileLength = error.getResponseBody().getBytes().length;
response.setBodyLength(fileLength);
response.setContentType("text/html");
response.endTheResponseHead();
// 输出响应
os.write(response.getHead().getBytes());
os.write(error.getResponseBody().getBytes());
return;
}
// 如果是目录的话,可以添加权限控制是否显示目录内容。
if (!requestFile.isDirectory()) {
fileLength = requestFile.length();
// Server.out.println("Filelength=" + fileLength);
response.setBodyLength(fileLength);
// // 设置响应内容的类型,不是text/html等类型时,要用*/*
if (canonicalPath.toLowerCase().indexOf("html") != -1) {
response.setContentType("text/html");
} else if (canonicalPath.toLowerCase().indexOf("jpg") != -1) {
response.setContentType("image/jpeg");
} else {
response.setContentType("*/*");
}
// response.setContentType("application/octec-stream");
response.endTheResponseHead();
// 输出响应头
os.write(response.getHead().getBytes());
InputStream is = new FileInputStream(requestFile);
byte[] buffer = new byte[1024];
while (is.available() > 0) {
int readin = is.read(buffer);
os.write(buffer, 0, readin);
}
is.close();
} else {
File[] files = requestFile.listFiles();
TemplateDir tmp = new TemplateDir();
for (int i = 0; i < files.length; i++) {
File tmpfile = files[i];
String filePath = tmpfile.getCanonicalPath();
String fileRelativePath = "/"
+ filePath.substring(Server.appRootPath
.length());
// Server.out.println(fileRelativePath);
String fileName = tmpfile.getName();
// Server.out.println(fileName);
if (tmpfile.isFile()) {
tmp.insertAFile(fileRelativePath, fileName, tmpfile
.length());
} else {
tmp.insertADIR(fileRelativePath, fileName);
}
}
response
.setBodyLength(tmp.getResponseBody().getBytes().length);
response.setContentType("text/html");
response.endTheResponseHead();
// 输出响应头
os.write(response.getHead().getBytes());
// 输出响应体
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(os));
bw.write(tmp.getResponseBody());
bw.flush();
}
} else {
TemplateError error = new TemplateError();
error.insertError("找不到文件!");
fileLength = error.getResponseBody().getBytes().length;
response.setBodyLength(fileLength);
response.setContentType("text/html");
response.endTheResponseHead();
// 输出响应
os.write(response.getHead().getBytes());
os.write(error.getResponseBody().getBytes());
}
os.flush();
os.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Request.java
package com.net;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;
public class HttpRequest {
StringBuilder requestBody = new StringBuilder();
StringBuilder requestHead = new StringBuilder();
Map requestMap = new HashMap();
public void addHead(String headLine) {
requestHead.append(headLine);
StringTokenizer token = new StringTokenizer(headLine, ":");
String propName = token.nextToken();
String propBody = token.nextToken();
requestMap.put(propName.trim(), propBody.trim());
}
public void addFirstLine(String line) {
StringTokenizer token = new StringTokenizer(line);
String method = token.nextToken();
String path = "";
try {
//如果GET请求中有中文,浏览器会使用UTF-8编码后发送中文。这里需要解码操作
path = java.net.URLDecoder.decode(token.nextToken(), "UTF-8");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String httpVersion = token.nextToken();
requestMap.put("method", method.trim());
requestMap.put("path", path.trim());
requestMap.put("httpVersion", httpVersion.trim());
}
public void addBody(String headBody) {
requestBody.append(headBody);
}
public String getRequestPath() {
return (String) requestMap.get("path");
}
public String getAllRequestString() {
StringBuilder allRequest = new StringBuilder();
java.util.Iterator it = this.requestMap.entrySet().iterator();
while (it.hasNext()) {
java.util.Map.Entry entry = (java.util.Map.Entry) it.next();
// entry.getKey() 返回与此项对应的键
// entry.getValue() 返回与此项对应的值
allRequest.append(entry.getKey() + ": ");
allRequest.append(entry.getValue() + "/n");
}
return allRequest.toString();
}
public static void main(String[] args) {
HttpRequest req = new HttpRequest();
req.addHead("a:1234");
req.addHead("b: 5678");
System.out.println(req.getAllRequestString());
}
}
Response.java
package com.net;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class HttpResponse {
StringBuilder responseBody = new StringBuilder();
StringBuilder responseHead = new StringBuilder();
// 拼接响应头
public HttpResponse() {
responseHead.append("HTTP/1.1 200 OK");
responseHead.append("/n");
responseHead.append("Server: AllenWebServer");
responseHead.append("/n");
}
public void setBodyLength(long bodylength) {
responseHead.append("Content-Length: " + bodylength);
responseHead.append("/n");
}
public void setContentType(String type) {
responseHead.append("Content-Type: " + type);
responseHead.append("/n");
}
public void endTheResponseHead() {
responseHead.append("/n");
}
public String getHead() {
return responseHead.toString();
}
public String getBody() {
return responseBody.toString();
}
public StringBuilder getResponseBody() {
return this.responseBody;
}
}
看看WebShare的界面:
可以设置共享的目录(即时生效),可以设置服务端口。
还有些问题没有解决,但也是可以使用了。
大家可以到这里下载程序和源码:
http://download.csdn.net/source/1604468