自己动手,写个Web服务器(Java版)——第四篇 WebShare

来源:互联网 发布:信息与数据的联系 编辑:程序博客网 时间:2024/05/05 03:10

     在这篇中,我要根据现有的成果,做一个真正可用的小产品——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的界面:

 WebShare界面文件

 

可以设置共享的目录(即时生效),可以设置服务端口。

还有些问题没有解决,但也是可以使用了。


 

 

大家可以到这里下载程序和源码:

http://download.csdn.net/source/1604468

 

 

原创粉丝点击