【搜集】迷你开源单文件 Java HTTP 服务器 NanoHTTPD.java

  1. package  positron.harness;
  2. import  java.io.BufferedReader;
  3. import  java.io.ByteArrayInputStream;
  4. import  java.io.File;
  5. import  java.io.FileInputStream;
  6. import  java.io.IOException;
  7. import  java.io.InputStream;
  8. import  java.io.InputStreamReader;
  9. import  java.io.OutputStream;
  10. import  java.io.PrintWriter;
  11. import  java.io.UnsupportedEncodingException;
  12. import  java.net.ServerSocket;
  13. import  java.net.Socket;
  14. import  java.net.URLEncoder;
  15. import  java.util.Date;
  16. import  java.util.Enumeration;
  17. import  java.util.HashMap;
  18. import  java.util.Locale;
  19. import  java.util.Map;
  20. import  java.util.Properties;
  21. import  java.util.StringTokenizer;
  22. import  java.util.TimeZone;
  23. /** 
  24.  * A simple, tiny, nicely embeddable HTTP 1.0 server in Java
  25.  *
  26.  * This class has been trivially modified from its original form, taken from
  27.  *  http://elonen.iki.fi/code/nanohttpd/NanoHTTPD.java 
  28.  *
  29.  * <p> NanoHTTPD version 1.1,
  30.  * Copyright © 2001,2005-2007 Jarno Elonen (elonen@iki.fi,  http://iki.fi/elonen/ )
  31.  *
  32.  * <p><b>Features + limitations: </b><ul>
  33.  *
  34.  *    <li> Only one Java file </li>
  35.  *    <li> Java 1.1 compatible </li>
  36.  *    <li> Released as open source, Modified BSD licence </li>
  37.  *    <li> No fixed config files, logging, authorization etc. (Implement yourself if you need them.) </li>
  38.  *    <li> Supports parameter parsing of GET and POST methods </li>
  39.  *    <li> Supports both dynamic content and file serving </li>
  40.  *    <li> Never caches anything </li>
  41.  *    <li> Doesn't limit bandwidth, request time or simultaneous connections </li>
  42.  *    <li> Default code serves files and shows all HTTP parameters and headers</li>
  43.  *    <li> File server supports directory listing, index.html and index.htm </li>
  44.  *    <li> File server does the 301 redirection trick for directories without '/'</li>
  45.  *    <li> File server supports simple skipping for files (continue download) </li>
  46.  *    <li> File server uses current directory as a web root </li>
  47.  *    <li> File server serves also very long files without memory overhead </li>
  48.  *    <li> Contains a built-in list of most common mime types </li>
  49.  *    <li> All header names are converted lowercase so they don't vary between browsers/clients </li>
  50.  *
  51.  * </ul>
  52.  *
  53.  * <p><b>Ways to use: </b><ul>
  54.  *
  55.  *    <li> Run as a standalone app, serves files from current directory and shows requests</li>
  56.  *    <li> Subclass serve() and embed to your own program </li>
  57.  *    <li> Call serveFile() from serve() with your own base directory </li>
  58.  *
  59.  * </ul>
  60.  *
  61.  * See the end of the source file for distribution license
  62.  * (Modified BSD licence)
  63.   */ 
  64. public   class  NanoHTTPD
  65. {
  66.      //  ==================================================
  67.      //  API parts
  68.      //  ================================================== 
  69.      /** 
  70.      * Override this to customize the server.<p>
  71.      *
  72.      * (By default, this delegates to serveFile() and allows directory listing.)
  73.      *
  74.      * @parm uri    Percent-decoded URI without parameters, for example "/index.cgi"
  75.      * @parm method    "GET", "POST" etc.
  76.      * @parm parms    Parsed, percent decoded parameters from URI and, in case of POST, data.
  77.      * @parm header    Header entries, percent decoded
  78.      *  @return  HTTP response, see class Response for details
  79.       */ 
  80.      public  Response serve( String uri, String method, Properties header, Properties parms )
  81.     {
  82.         System.out.println( method  +   "  ' "   +  uri  +   " '  "  );
  83.         Enumeration e  =  header.propertyNames();
  84.          while  ( e.hasMoreElements())
  85.         {
  86.             String value  =  (String)e.nextElement();
  87.             System.out.println(  "   HDR: ' "   +  value  +   " ' = ' "   + 
  88.                                 header.getProperty( value )  +   " ' "  );
  89.         }
  90.         e  =  parms.propertyNames();
  91.          while  ( e.hasMoreElements())
  92.         {
  93.             String value  =  (String)e.nextElement();
  94.             System.out.println(  "   PRM: ' "   +  value  +   " ' = ' "   + 
  95.                                 parms.getProperty( value )  +   " ' "  );
  96.         }
  97.          return  serveFile( uri, header,  new  File( " . " ),  true  );
  98.     }
  99.      /** 
  100.      * HTTP response.
  101.      * Return one of these from serve().
  102.       */ 
  103.      public   class  Response
  104.     {
  105.          /** 
  106.          * Default constructor: response = HTTP_OK, data = mime = 'null'
  107.           */ 
  108.          public  Response()
  109.         {
  110.              this .status  =  HTTP_OK;
  111.         }
  112.          /** 
  113.          * Basic constructor.
  114.           */ 
  115.          public  Response( String status, String mimeType, InputStream data )
  116.         {
  117.              this .status  =  status;
  118.              this .mimeType  =  mimeType;
  119.              this .data  =  data;
  120.         }
  121.          /** 
  122.          * Convenience method that makes an InputStream out of
  123.          * given text.
  124.           */ 
  125.          public  Response( String status, String mimeType, String txt )
  126.         {
  127.              this .status  =  status;
  128.              this .mimeType  =  mimeType;
  129.              this .data  =   new  ByteArrayInputStream( txt.getBytes());
  130.         }
  131.          /** 
  132.          * Adds given line to the header.
  133.           */ 
  134.          public   void  addHeader( String name, String value )
  135.         {
  136.             header.put( name, value );
  137.         }
  138.          /** 
  139.          * HTTP status code after processing, e.g. "200 OK", HTTP_OK
  140.           */ 
  141.          public  String status;
  142.          /** 
  143.          * MIME type of content, e.g. "text/html"
  144.           */ 
  145.          public  String mimeType;
  146.          /** 
  147.          * Data of the response, may be null.
  148.           */ 
  149.          public  InputStream data;
  150.          /** 
  151.          * Headers for the HTTP response. Use addHeader()
  152.          * to add lines.
  153.           */ 
  154.          public  Properties header  =   new  Properties();
  155.     }
  156.      /** 
  157.      * Some HTTP response status codes
  158.       */ 
  159.      public   static   final  String
  160.         HTTP_OK  =   " 200 OK " ,
  161.         HTTP_REDIRECT  =   " 301 Moved Permanently " ,
  162.         HTTP_FORBIDDEN  =   " 403 Forbidden " ,
  163.         HTTP_NOTFOUND  =   " 404 Not Found " ,
  164.         HTTP_BADREQUEST  =   " 400 Bad Request " ,
  165.         HTTP_INTERNALERROR  =   " 500 Internal Server Error " ,
  166.         HTTP_NOTIMPLEMENTED  =   " 501 Not Implemented " ;
  167.      /** 
  168.      * Common mime types for dynamic content
  169.       */ 
  170.      public   static   final  String
  171.         MIME_PLAINTEXT  =   " text/plain " ,
  172.         MIME_HTML  =   " text/html " ,
  173.         MIME_DEFAULT_BINARY  =   " application/octet-stream " ;
  174.      //  ==================================================
  175.      //  Socket & server code
  176.      //  ================================================== 
  177.      /** 
  178.      * Starts a HTTP server to given port.<p>
  179.      * Throws an IOException if the socket is already in use
  180.       */ 
  181.      public  NanoHTTPD(  int  port )  throws  IOException
  182.     {
  183.         myTcpPort  =  port;
  184.          final  ServerSocket ss  =   new  ServerSocket( myTcpPort );
  185.         Thread t  =   new  Thread(  new  Runnable()
  186.             {
  187.                  public   void  run()
  188.                 {
  189.                      try 
  190.                     {
  191.                          while (  true  )
  192.                              new  HTTPSession( ss.accept());
  193.                     }
  194.                      catch  ( IOException ioe )
  195.                     {}
  196.                 }
  197.             });
  198.         t.setDaemon(  true  );
  199.         t.start();
  200.     }
  201.      /** 
  202.      * Starts as a standalone file server and waits for Enter.
  203.       */ 
  204.      public   static   void  main( String[] args )
  205.     {
  206.         System.out.println(  " NanoHTTPD 1.1 (C) 2001,2005-2007 Jarno Elonen/n "   + 
  207.                              " (Command line options: [port] [--licence])/n "  );
  208.          //  Show licence if requested 
  209.          int  lopt  =   - 1 ;
  210.          for  (  int  i = 0 ; i < args.length;  ++ i )
  211.          if  ( args[i].toLowerCase().endsWith(  " licence "  ))
  212.         {
  213.             lopt  =  i;
  214.             System.out.println( LICENCE  +   " /n "  );
  215.         }
  216.          //  Change port if requested 
  217.          int  port  =   80 ;
  218.          if  ( args.length  >   0   &&  lopt  !=   0  )
  219.             port  =  Integer.parseInt( args[ 0 ] );
  220.          if  ( args.length  >   1   && 
  221.              args[ 1 ].toLowerCase().endsWith(  " licence "  ))
  222.                 System.out.println( LICENCE  +   " /n "  );
  223.          try  {
  224.              new  NanoHTTPD( port );
  225.         }  catch ( IOException ioe ) {
  226.             System.err.println(  " Couldn't start server:/n "   +  ioe );
  227.             System.exit(  - 1  );
  228.         }
  229.         System.out.println(  " Now serving files in port  "   +  port  +   "  from / ""  + 
  230.                              new  File( "" ).getAbsolutePath()  +   " / ""  ); 
  231.         System.out.println(  " Hit Enter to stop./n "  );
  232.          try  { System.in.read(); }  catch ( Throwable t ) {};
  233.     }
  234.      /** 
  235.      * Handles one session, i.e. parses the HTTP request
  236.      * and returns the response.
  237.       */ 
  238.      private   class  HTTPSession  implements  Runnable
  239.     {
  240.          public  HTTPSession( Socket s )
  241.         {
  242.             mySocket  =  s;
  243.             Thread t  =   new  Thread(  this  );
  244.             t.setDaemon(  true  );
  245.             t.start();
  246.         }
  247.          public   void  run()
  248.         {
  249.              try 
  250.             {
  251.                 InputStream is  =  mySocket.getInputStream();
  252.                  if  ( is  ==   null )  return ;
  253.                 BufferedReader in  =   new  BufferedReader(  new  InputStreamReader( is ));
  254.                  //  Read the request line 
  255.                 StringTokenizer st  =   new  StringTokenizer( in.readLine());
  256.                  if  (  ! st.hasMoreTokens())
  257.                     sendError( HTTP_BADREQUEST,  " BAD REQUEST: Syntax error. Usage: GET /example/file.html "  );
  258.                 String method  =  st.nextToken();
  259.                  if  (  ! st.hasMoreTokens())
  260.                     sendError( HTTP_BADREQUEST,  " BAD REQUEST: Missing URI. Usage: GET /example/file.html "  );
  261.                 String uri  =  decodePercent( st.nextToken());
  262.                  //  Decode parameters from the URI 
  263.                 Properties parms  =   new  Properties();
  264.                  int  qmi  =  uri.indexOf(  ' ? '  );
  265.                  if  ( qmi  >=   0  )
  266.                 {
  267.                     decodeParms( uri.substring( qmi + 1  ), parms );
  268.                     uri  =  decodePercent( uri.substring(  0 , qmi ));
  269.                 }
  270.                  //  If there's another token, it's protocol version,
  271.                  //  followed by HTTP headers. Ignore version but parse headers.
  272.                  //  NOTE: this now forces header names uppercase since they are
  273.                  //  case insensitive and vary by client. 
  274.                 Properties header  =   new  Properties();
  275.                  if  ( st.hasMoreTokens())
  276.                 {
  277.                     String line  =  in.readLine();
  278.                      while  ( line.trim().length()  >   0  )
  279.                     {
  280.                          int  p  =  line.indexOf(  ' : '  );
  281.                         header.put( line.substring( 0 ,p).trim().toLowerCase(), line.substring(p + 1 ).trim());
  282.                         line  =  in.readLine();
  283.                     }
  284.                 }
  285.                  //  If the method is POST, there may be parameters
  286.                  //  in data section, too, read it: 
  287.                  if  ( method.equalsIgnoreCase(  " POST "  ))
  288.                 {
  289.                      long  size  =   0x7FFFFFFFFFFFFFFFl ;
  290.                     String contentLength  =  header.getProperty( " content-length " );
  291.                      if  (contentLength  !=   null )
  292.                     {
  293.                          try  { size  =  Integer.parseInt(contentLength); }
  294.                          catch  (NumberFormatException ex) {}
  295.                     }
  296.                     String postLine  =   "" ;
  297.                      char  buf[]  =   new   char [ 512 ];
  298.                      int  read  =  in.read(buf);
  299.                      while  ( read  >=   0   &&  size  >   0   &&   ! postLine.endsWith( " /r/n " ) )
  300.                     {
  301.                         size  -=  read;
  302.                         postLine  +=  String.valueOf(buf,  0 , read);
  303.                          if  ( size  >   0  )
  304.                             read  =  in.read(buf);
  305.                     }
  306.                     postLine  =  postLine.trim();
  307.                     decodeParms( postLine, parms );
  308.                 }
  309.                  //  Ok, now do the serve() 
  310.                 Response r  =  serve( uri, method, header, parms );
  311.                  if  ( r  ==   null  )
  312.                     sendError( HTTP_INTERNALERROR,  " SERVER INTERNAL ERROR: Serve() returned a null response. "  );
  313.                  else 
  314.                     sendResponse( r.status, r.mimeType, r.header, r.data );
  315.                 in.close();
  316.             }
  317.              catch  ( IOException ioe )
  318.             {
  319.                  try 
  320.                 {
  321.                     sendError( HTTP_INTERNALERROR,  " SERVER INTERNAL ERROR: IOException:  "   +  ioe.getMessage());
  322.                 }
  323.                  catch  ( Throwable t ) {}
  324.             }
  325.              catch  ( InterruptedException ie )
  326.             {
  327.                  //  Thrown by sendError, ignore and exit the thread. 
  328.             }
  329.         }
  330.          /** 
  331.          * Decodes the percent encoding scheme. <br/>
  332.          * For example: "an+example%20string" -> "an example string"
  333.           */ 
  334.          private  String decodePercent( String str )  throws  InterruptedException
  335.         {
  336.              try 
  337.             {
  338.                 StringBuffer sb  =   new  StringBuffer();
  339.                  for (  int  i = 0 ; i < str.length(); i ++  )
  340.                 {
  341.                      char  c  =  str.charAt( i );
  342.                      switch  ( c )
  343.                     {
  344.                          case   ' + ' :
  345.                             sb.append(  '   '  );
  346.                              break ;
  347.                          case   ' % ' :
  348.                             sb.append(( char )Integer.parseInt( str.substring(i + 1 ,i + 3 ),  16  ));
  349.                             i  +=   2 ;
  350.                              break ;
  351.                          default :
  352.                             sb.append( c );
  353.                              break ;
  354.                     }
  355.                 }
  356.                  return   new  String( sb.toString().getBytes());
  357.             }
  358.              catch ( Exception e )
  359.             {
  360.                 sendError( HTTP_BADREQUEST,  " BAD REQUEST: Bad percent-encoding. "  );
  361.                  return   null ;
  362.             }
  363.         }
  364.          /** 
  365.          * Decodes parameters in percent-encoded URI-format
  366.          * ( e.g. "name=Jack%20Daniels&pass=Single%20Malt" ) and
  367.          * adds them to given Properties.
  368.           */ 
  369.          private   void  decodeParms( String parms, Properties p )
  370.              throws  InterruptedException
  371.         {
  372.              if  ( parms  ==   null  )
  373.                  return ;
  374.             StringTokenizer st  =   new  StringTokenizer( parms,  " & "  );
  375.              while  ( st.hasMoreTokens())
  376.             {
  377.                 String e  =  st.nextToken();
  378.                  int  sep  =  e.indexOf(  ' = '  );
  379.                  if  ( sep  >=   0  )
  380.                     p.put( decodePercent( e.substring(  0 , sep )).trim(),
  381.                            decodePercent( e.substring( sep + 1  )));
  382.             }
  383.         }
  384.          /** 
  385.          * Returns an error message as a HTTP response and
  386.          * throws InterruptedException to stop furhter request processing.
  387.           */ 
  388.          private   void  sendError( String status, String msg )  throws  InterruptedException
  389.         {
  390.             sendResponse( status, MIME_PLAINTEXT,  null ,  new  ByteArrayInputStream( msg.getBytes()));
  391.              throw   new  InterruptedException();
  392.         }
  393.          /** 
  394.          * Sends given response to the socket.
  395.           */ 
  396.          private   void  sendResponse( String status, String mime, Properties header, InputStream data )
  397.         {
  398.              try 
  399.             {
  400.                  if  ( status  ==   null  )
  401.                      throw   new  Error(  " sendResponse(): Status can't be null. "  );
  402.                 OutputStream out  =  mySocket.getOutputStream();
  403.                 PrintWriter pw  =   new  PrintWriter( out );
  404.                 pw.print( " HTTP/1.0  "   +  status  +   "  /r/n " );
  405.                  if  ( mime  !=   null  )
  406.                     pw.print( " Content-Type:  "   +  mime  +   " /r/n " );
  407.                  if  ( header  ==   null   ||  header.getProperty(  " Date "  )  ==   null  )
  408.                     pw.print(  " Date:  "   +  gmtFrmt.format(  new  Date())  +   " /r/n " );
  409.                  if  ( header  !=   null  )
  410.                 {
  411.                     Enumeration e  =  header.keys();
  412.                      while  ( e.hasMoreElements())
  413.                     {
  414.                         String key  =  (String)e.nextElement();
  415.                         String value  =  header.getProperty( key );
  416.                         pw.print( key  +   " :  "   +  value  +   " /r/n " );
  417.                     }
  418.                 }
  419.                 pw.print( " /r/n " );
  420.                 pw.flush();
  421.                  if  ( data  !=   null  )
  422.                 {
  423.                      byte [] buff  =   new   byte [ 2048 ];
  424.                      while  ( true )
  425.                     {
  426.                          int  read  =  data.read( buff,  0 ,  2048  );
  427.                          if  (read  <=   0 )
  428.                              break ;
  429.                         out.write( buff,  0 , read );
  430.                     }
  431.                 }
  432.                 out.flush();
  433.                 out.close();
  434.                  if  ( data  !=   null  )
  435.                     data.close();
  436.             }
  437.              catch ( IOException ioe )
  438.             {
  439.                  //  Couldn't write? No can do. 
  440.                  try  { mySocket.close(); }  catch ( Throwable t ) {}
  441.             }
  442.         }
  443.          private  Socket mySocket;
  444.     };
  445.      /** 
  446.      * URL-encodes everything between "/"-characters.
  447.      * Encodes spaces as '%20' instead of '+'.
  448.       */ 
  449.      private  String encodeUri( String uri )
  450.     {
  451.         String newUri  =   "" ;
  452.         StringTokenizer st  =   new  StringTokenizer( uri,  " /  " ,  true  );
  453.          while  ( st.hasMoreTokens())
  454.         {
  455.             String tok  =  st.nextToken();
  456.              if  ( tok.equals(  " / "  ))
  457.                 newUri  +=   " / " ;
  458.              else   if  ( tok.equals(  "   "  ))
  459.                 newUri  +=   " %20 " ;
  460.              else 
  461.             {
  462.                  try  { newUri  +=  URLEncoder.encode( tok,  " UTF-8 "  ); }  catch  ( UnsupportedEncodingException uee ) {}
  463.             }
  464.         }
  465.          return  newUri;
  466.     }
  467.      private   int  myTcpPort;
  468.      //  ==================================================
  469.      //  File server code
  470.      //  ================================================== 
  471.      /** 
  472.      * Serves file from homeDir and its' subdirectories (only).
  473.      * Uses only URI, ignores all headers and HTTP parameters.
  474.       */ 
  475.      public  Response serveFile( String uri, Properties header, File homeDir,
  476.                                 boolean  allowDirectoryListing )
  477.     {
  478.          //  Make sure we won't die of an exception later 
  479.          if  (  ! homeDir.isDirectory())
  480.              return   new  Response( HTTP_INTERNALERROR, MIME_PLAINTEXT,
  481.                                   " INTERNAL ERRROR: serveFile(): given homeDir is not a directory. "  );
  482.          //  Remove URL arguments 
  483.         uri  =  uri.trim().replace( File.separatorChar,  ' / '  );
  484.          if  ( uri.indexOf(  ' ? '  )  >=   0  )
  485.             uri  =  uri.substring( 0 , uri.indexOf(  ' ? '  ));
  486.          //  Prohibit getting out of current directory 
  487.          if  ( uri.startsWith(  " .. "  )  ||  uri.endsWith(  " .. "  )  ||  uri.indexOf(  " ../ "  )  >=   0  )
  488.              return   new  Response( HTTP_FORBIDDEN, MIME_PLAINTEXT,
  489.                                   " FORBIDDEN: Won't serve ../ for security reasons. "  );
  490.         File f  =   new  File( homeDir, uri );
  491.          if  (  ! f.exists())
  492.              return   new  Response( HTTP_NOTFOUND, MIME_PLAINTEXT,
  493.                                   " Error 404, file not found. "  );
  494.          //  List the directory, if necessary 
  495.          if  ( f.isDirectory())
  496.         {
  497.              //  Browsers get confused without '/' after the
  498.              //  directory, send a redirect. 
  499.              if  (  ! uri.endsWith(  " / "  ))
  500.             {
  501.                 uri  +=   " / " ;
  502.                 Response r  =   new  Response( HTTP_REDIRECT, MIME_HTML,
  503.                                             " <html><body>Redirected: <a href=/ ""  + uri +  " / " > "   + 
  504.                                            uri  +   " </a></body></html> " );
  505.                 r.addHeader(  " Location " , uri );
  506.                  return  r;
  507.             }
  508.              //  First try index.html and index.htm 
  509.              if  (  new  File( f,  " index.html "  ).exists())
  510.                 f  =   new  File( homeDir, uri  +   " /index.html "  );
  511.              else   if  (  new  File( f,  " index.htm "  ).exists())
  512.                 f  =   new  File( homeDir, uri  +   " /index.htm "  );
  513.              //  No index file, list the directory 
  514.              else   if  ( allowDirectoryListing )
  515.             {
  516.                 String[] files  =  f.list();
  517.                 String msg  =   " <html><body><h1>Directory  "   +  uri  +   " </h1><br/> " ;
  518.                  if  ( uri.length()  >   1  )
  519.                 {
  520.                     String u  =  uri.substring(  0 , uri.length() - 1  );
  521.                      int  slash  =  u.lastIndexOf(  ' / '  );
  522.                      if  ( slash  >=   0   &&  slash   <  u.length())
  523.                         msg  +=   " <b><a href=/ ""  + uri.substring(0, slash+1) +  " / " >..</a></b><br/> " ;
  524.                 }
  525.                  for  (  int  i = 0 ; i < files.length;  ++ i )
  526.                 {
  527.                     File curFile  =   new  File( f, files[i] );
  528.                      boolean  dir  =  curFile.isDirectory();
  529.                      if  ( dir )
  530.                     {
  531.                         msg  +=   " <b> " ;
  532.                         files[i]  +=   " / " ;
  533.                     }
  534.                     msg  +=   " <a href=/ ""  + encodeUri( uri + files[i] ) +  " / " > "   + 
  535.                            files[i]  +   " </a> " ;
  536.                      //  Show file size 
  537.                      if  ( curFile.isFile())
  538.                     {
  539.                          long  len  =  curFile.length();
  540.                         msg  +=   "   <font size=2>( " ;
  541.                          if  ( len  <   1024  )
  542.                             msg  +=  curFile.length()  +   "  bytes " ;
  543.                          else   if  ( len  <   1024   *   1024  )
  544.                             msg  +=  curFile.length() / 1024   +   " . "   +  (curFile.length() % 1024 / 10 % 100 )  +   "  KB " ;
  545.                          else 
  546.                             msg  +=  curFile.length() / ( 1024 * 1024 )  +   " . "   +  curFile.length() % ( 1024 * 1024 ) / 10 % 100   +   "  MB " ;
  547.                         msg  +=   " )</font> " ;
  548.                     }
  549.                     msg  +=   " <br/> " ;
  550.                      if  ( dir ) msg  +=   " </b> " ;
  551.                 }
  552.                  return   new  Response( HTTP_OK, MIME_HTML, msg );
  553.             }
  554.              else 
  555.             {
  556.                  return   new  Response( HTTP_FORBIDDEN, MIME_PLAINTEXT,
  557.                                   " FORBIDDEN: No directory listing. "  );
  558.             }
  559.         }
  560.          try 
  561.         {
  562.              //  Get MIME type from file name extension, if possible 
  563.             String mime  =   null ;
  564.              int  dot  =  f.getCanonicalPath().lastIndexOf(  ' . '  );
  565.              if  ( dot  >=   0  )
  566.                 mime  =  (String)theMimeTypes.get( f.getCanonicalPath().substring( dot  +   1  ).toLowerCase());
  567.              if  ( mime  ==   null  )
  568.                 mime  =  MIME_DEFAULT_BINARY;
  569.              //  Support (simple) skipping: 
  570.              long  startFrom  =   0 ;
  571.             String range  =  header.getProperty(  " Range "  );
  572.              if  ( range  !=   null  )
  573.             {
  574.                  if  ( range.startsWith(  " bytes= "  ))
  575.                 {
  576.                     range  =  range.substring(  " bytes= " .length());
  577.                      int  minus  =  range.indexOf(  ' - '  );
  578.                      if  ( minus  >   0  )
  579.                         range  =  range.substring(  0 , minus );
  580.                      try     {
  581.                         startFrom  =  Long.parseLong( range );
  582.                     }
  583.                      catch  ( NumberFormatException nfe ) {}
  584.                 }
  585.             }
  586.             FileInputStream fis  =   new  FileInputStream( f );
  587.             fis.skip( startFrom );
  588.             Response r  =   new  Response( HTTP_OK, mime, fis );
  589.             r.addHeader(  " Content-length " ,  ""   +  (f.length()  -  startFrom));
  590.             r.addHeader(  " Content-range " ,  ""   +  startFrom  +   " - "   + 
  591.                         (f.length() - 1 )  +   " / "   +  f.length());
  592.              return  r;
  593.         }
  594.          catch ( IOException ioe )
  595.         {
  596.              return   new  Response( HTTP_FORBIDDEN, MIME_PLAINTEXT,  " FORBIDDEN: Reading file failed. "  );
  597.         }
  598.     }
  599.      /** 
  600.      * Map (String)FILENAME_EXTENSION -> (String)MIME_TYPE
  601.       */ 
  602.      private   static  Map < String, String >  theMimeTypes  =   new  HashMap < String, String > ();
  603.      static 
  604.     {
  605.         StringTokenizer st  =   new  StringTokenizer(
  606.              " htm        text/html  " + 
  607.              " html        text/html  " + 
  608.              " txt        text/plain  " + 
  609.              " asc        text/plain  " + 
  610.              " gif        image/gif  " + 
  611.              " jpg        image/jpeg  " + 
  612.              " jpeg        image/jpeg  " + 
  613.              " png        image/png  " + 
  614.              " mp3        audio/mpeg  " + 
  615.              " m3u        audio/mpeg-url  "   + 
  616.              " pdf        application/pdf  " + 
  617.              " doc        application/msword  " + 
  618.              " ogg        application/x-ogg  " + 
  619.              " zip        application/octet-stream  " + 
  620.              " exe        application/octet-stream  " + 
  621.              " class        application/octet-stream  "  );
  622.          while  ( st.hasMoreTokens())
  623.             theMimeTypes.put( st.nextToken(), st.nextToken());
  624.     }
  625.      /** 
  626.      * GMT date formatter
  627.       */ 
  628.      private   static  java.text.SimpleDateFormat gmtFrmt;
  629.      static 
  630.     {
  631.         gmtFrmt  =   new  java.text.SimpleDateFormat(  " E, d MMM yyyy HH:mm:ss 'GMT' " , Locale.US);
  632.         gmtFrmt.setTimeZone(TimeZone.getTimeZone( " GMT " ));
  633.     }
  634.      /** 
  635.      * The distribution license
  636.       */ 
  637.      private   static   final  String LICENCE  = 
  638.          " Copyright (C) 2001,2005 by Jarno Elonen <elonen@iki.fi>/n " + 
  639.          " /n " + 
  640.          " Redistribution and use in source and binary forms, with or without/n " + 
  641.          " modification, are permitted provided that the following conditions/n " + 
  642.          " are met:/n " + 
  643.          " /n " + 
  644.          " Redistributions of source code must retain the above copyright notice,/n " + 
  645.          " this list of conditions and the following disclaimer. Redistributions in/n " + 
  646.          " binary form must reproduce the above copyright notice, this list of/n " + 
  647.          " conditions and the following disclaimer in the documentation and/or other/n " + 
  648.          " materials provided with the distribution. The name of the author may not/n " + 
  649.          " be used to endorse or promote products derived from this software without/n " + 
  650.          " specific prior written permission. /n " + 
  651.          "  /n " + 
  652.          " THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR/n " + 
  653.          " IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES/n " + 
  654.          " OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED./n " + 
  655.          " IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,/n " + 
  656.          " INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT/n " + 
  657.          " NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,/n " + 
  658.          " DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY/n " + 
  659.          " THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT/n " + 
  660.          " (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE/n " + 
  661.          " OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. " ;
  662. }

原文地址:http://www.blogjava.net/beansoft/archive/2008/10/18/235235.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值