Servlet技术(下)

Servlet技术(下)

1下载文件

下载文件的实现:把需要下载的文件通过java InputStream 读入转换成二进制文件,再将InputStream中的内容读入到一个byte数组中,最后将byte数组中的内容写入到OutputStream输出流中,OutputStream的实例化对象由Tomcat容器创建的ServletResponse对象的获得

package mypack;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

public class DownloadServlet extends HttpServlet {
 public void doGet(HttpServletRequest request,HttpServletResponse response)
        throws ServletException, IOException {
   OutputStream out; //输出响应正文的输出流
   InputStream in;  //读取本地文件的输入流
   //获得filename请求参数
   String filename=request.getParameter("filename");
   
   if(filename==null){
     out=response.getOutputStream();
     out.write("Please input filename.".getBytes());
     out.close();
     return;
  }
   
   //创建读取本地文件的输入流
   /* getResourceAsStream(java.lang.String path)
   ** Returns the resource located at the named path as an InputStream object.
   **/
   in= getServletContext().getResourceAsStream("/store/"+filename);
    /* available()
    ** Returns an estimate of the number of bytes that can be read (or skipped over) from this input stream
    */
   int length=in.available();
   //设置响应正文的MIME类型,MIME类型设置为force-download,浏览器会以下载的方式来处理响应正文
   response.setContentType("application/force-download");
   response.setHeader("Content-Length",String.valueOf(length));
   response.setHeader("Content-Disposition", "attachment;filename=""+filename +"" ");
   
   /** 把本地文件中的数据发送给客户 */
   out=response.getOutputStream();
   int bytesRead = 0;
   byte[] buffer = new byte[512];
   /* read()
   ** Reads some number of bytes from the input stream and stores them into the buffer array b
   */
   while ((bytesRead = in.read(buffer)) != -1){
     /* out.write(byte[] b,int off, int len)
     ** Writes len bytes from the specified byte array starting at offset off to this output stream.
     */
     out.write(buffer, 0, bytesRead);
  }
     
   in.close();
   out.close();
}
}

/****************************************************
* 作者:孙卫琴                                     *
* 来源:<<Tomcat与Java Web开发技术详解>>           *
* 技术支持网址:www.javathinker.net               *
***************************************************/

 

2 读写Cookie

客户端首次访问服务器时,服务器先在客户端存放包含客户的相关信息的Cookie,以后客户端每次请求访问服务器时,都会在HTTP请求数据中包含Cookie(准确的说Cookie会包含在HTTP请求的请求头中),服务器解析HTTP请求中的Cookie,就能由此获得关于客户的信息

Tomcat作为Web服务器,提供了与Cookie交互的API,即 java.servlet.http.Cookie,因此Web应用中的Servlet可以直接通过Cookie类来访问HTTP请求中包含的Cookie中的数据。

package mypack;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

public class CookieServlet extends HttpServlet {
 int count=0;

 public void doGet(HttpServletRequest request,HttpServletResponse response)
   throws ServletException, IOException {
   
   response.setContentType("text/plain");
   PrintWriter out=response.getWriter();
   //获得HTTP请求中的所有Cookie
   Cookie[] cookies=request.getCookies();
   if(cookies!=null){
     //访问每个Cookie
     for(int i = 0; i < cookies.length; i++){
       out.println("Cookie name:"+cookies[i].getName());
       out.println("Cookie value:"+cookies[i].getValue());
       out.println("Max Age:"+cookies[i].getMaxAge()+" ");
    }
  }else{
     out.println("No cookie.");
  }

   //向客户端写一个Cookie
   //Cookie类的构造方法有两个参数,cookieName和cookieValue,都是字符串类型
   response.addCookie(new Cookie(
           "cookieName" + count, "cookieValue" + count));
   count++;
}
}

/* Cookie类一些其他常用的方法
** cookie.setMaxAge(60*60) //设置客户端在本地保存该Cookie的时间1h,想要删除cookie直接将参数改为0即可
** cookie.setValue(string value) //修改cookie的值,cookieName是final类型的,不能修改
*/

/* HTTPServletResponse提供的与Cookie相关的方法
** response.addCookie(Cookie cookie) //将该cookie加入到响应中
*/
/* HTTPServletRequest提供的与Cookie相关的方法
** Cookie[] response.getCookies() //返回HTTP请求中的所有Cookie,将其作为一个数组返回
*/


/****************************************************
* 作者:孙卫琴                                     *
* 来源:<<Tomcat与Java Web开发技术详解>>           *
* 技术支持网址:www.javathinker.net               *
***************************************************/

Cookie的共享范围

默认情况下,哪个web应用保存的cookie,cookie就只在这个web应用中可以访问。

在同一个Tomcat容器中共享Cookie

一个Servlet容器可以包含多个web应用,为了使同一个Servlet中的其他web应用也能访问该cookie

Cookie cookie=new Cookie("username","tom");
cookie.setPath("/");
response.addCookie(cookie);

setPath(string path)方法: Specifies a path for the cookie to which the client should send the cookie.

"/" 表示Tomcat服务器的根路径,相当于 localhost:8080/ 或者 webapp/ ,因此将cookie的path设置为 “/” 之后,客户端访问该浏览器中的其他web应用的时候也会发送该cookie

 

在不同的Tomcat容器中共享Cookie

Cookie cookie=new Cookie("username","tom");
cookie.setDomain(".cat.tom");
response.addCookie(cookie);

setDomain(string pattern)方法:Specifies the domain within which this cookie should be presented.

 

3 访问Web应用的工作目录

每个Web应用都有一个工作目录,Servlet容器会把与这个Web应用相关的临时文件存放在这个目录下,Tomcat为Web应用提供的默认的工作目录为

<CATALINA_HOME>/work/[enginename]/[hostname]/[contextpath]

例如:web应用helloapp被默认发布到Tomcat的名为Catalina的Engine的localhost虚拟主机中,那么helloapp应用的默认工作目录为:

<CATALINA_HOME>/work/Catalina/localhost/helloapp

设置work directory

web应用的工作目录可以在server.xml文件中的context标签中设置,通过workDir属性来设置

在Servlet中获得work directory

当Servlet容器在初始化一个Web应用时,会向刚创建的ServletContext对象中设置一个名为 “javax.servlet.context.tempdir” 的属性,该属性值为一个java.io.File类型的对象,代表当前Web应用的工作目录,因此,Servlet获取工作目录的方式为:

File workDir=(File)context.getAttribute(“javax.servlet.context.tempdir”);

 

4 转发和包含

Servlet对象由Servlet容器创建,并且Servlet对象的service()方法也有容器调用,而一个Servlet无法直接调用另一个Servlet对象的service()方法,因为这个Servlet对象无法获得另一个Servlet对象的引用

请求转发包含使一个Servlet对象可以调用另一个Servlet对象的方法

 

java.servlet.RequestDispatcher类:

该类表示请求分发器,包含include()forward()两个方法,分别表示请求和转发

void forward(ServletRequest request, ServletResponse response)
void include(ServletRequest request, ServletResponse response)

可以看到forward方法和include方法都是包含两个参数 ServletRequest和ServletResponse的, 因此被请求或者包含的Servlet与原Servlet共享 request和 response对象

获取RequestDispatcher对象的方法:

//方式一:调用ServletContext的getRequestDispatcher(String path)方法
//其中path表示被请求或者包含的Servlet的绝对路径,绝对路径以 “/” 打头,“/” 表示 localhost:8080/ 或者 webapp/
//方式二:调用ServletRequest的getRequestDispatcher(String path)方法
//其中path表示被请求或者包含的Servlet的绝对路径或者相对路径,绝对路径同上,相对路径表示原Servlet所在的目录

 

**dispatcher.forward(request,response)**方法的处理流程:

a.清空存放响应正文数据的缓冲去 因此原组件Servlet中已经通过PrintWriter或者ServletOutputStream写入响应正文中的内容会被清空(响应头中的内容是否会清空不知道,请试一试)。如果在调用forward之前调用了flushBuffer()或者close()方法,程序会抛出IllegalStateException异常

b.如果目标组件为Servlet或者JSP,调用其service()方法,把该方法中的响应结果发送给客户端;如果是静态的HTML文档,就读取文档中的数据并把它发送到客户端

 

dispatcher.include(request,response)方法的处理流程:

a.如果目标组件为Servlet或者JSP,调用其service()方法,把该方法中的响应结果添加到响应正文中;如果是静态的HTML文档,就读取文档中的数据并把它添加到响应正文中

b.返回到源组件的服务方法中,继续执行后续的代码块

 

5 重定向 sendRedirect(String location)

重定向机制是由HTTP协议提供的一种机制,存在于HttpServletResponse接口中,而ServletResponse接口中不包含该方法

重定向的运作流程:

1.用户在浏览器输入特定的URL,请求访问服务器端的某个组件

2.服务器端的组件返回一个状态码为302的响应结果,该响应结果的含义为:让浏览器端再请求访问另一个Web组件。响应结果中提供了另一个Web组件的URL,另一个Web组件可以在同一个Web服务器上,也可以在不同Web服务器上

3.浏览器接受到这种响应结果之后,再立即自动请求访问另一个Web组件

4.浏览器接受来自另一个Web组件的响应结果

 

HttpServletResponse.sendRedirect(String location)方法:

Servlet源组件生成的响应结果不会被发送到客户端,只有重定向的目标组件生成的响应结果才会被发送到客户端

如果在调用sendRedirect之前调用了flushBuffer()或者close()方法,程序会抛出IllegalStateException异常

源组件和目标组件不共享ServletRequest对象,因为浏览器重新发出请求

sendRedirect方法中的 location参数,如果以 “/” 打头,表示当前服务器根路径的URL , 如果以“http://” 开头,表示完整的URL

目标组件不需要是同一个Web服务器上的Web应用,可以是任意服务器上的

 

 

6 访问Servlet容器内的其他Web应用

同一个Servlet容器中可以有多个Web应用,Web应用之间是可以互相访问的

ServletContext接口中提供了getContext(String uripath) 方法,该方法可以获取其他Web应用的ServletContext对象,参数 uripath 指定其他Web应用的URL入口

可以通过<Context>元素的crossContext属性设置Web应用是否能访问其他Web应用资源

crossContext=false: Context元素对应的Web应用无法得到其他Web应用的ServletContext对象

crossContext=true: Context元素对应的Web应用可以得到其他Web应用的ServletContext对象

 

7 解决并发问题

一个Web应用可能在一个时间被多个浏览器请求服务,因此可能会产生并发的问题

解决并发的方案:

1.合理决定在Servlet中定义的变量的作用域类型

package mypack;

import javax.servlet.*;
import java.io.*;

public class HelloServlet1 extends GenericServlet {
private String username=null; //username为实例变量

/** 响应客户请求*/
public void service(ServletRequest request,
ServletResponse response)
throws ServletException, IOException {

//把username请求参数赋值给username实例变量
username=request.getParameter("username");

try{
Thread.sleep(3000); //特意延长响应客户请求的时间
}catch(Exception e){e.printStackTrace();}

//设置HTTP响应的正文的MIME类型及字符编码
response.setContentType("text/html;charset=GBK");

/*输出HTML文档*/
PrintWriter out = response.getWriter();
out.println("<html><head><title>HelloServlet</TITLE></head>");
out.println("<body>");
out.println("你好: "+username);
out.println("</body></html>");

out.close(); //关闭PrintWriter
}
}

在以上程序中,通过两个浏览器同时访问该Web应用

浏览器1: http://localhost:8080/helloapp/hello1?username=老鼠

浏览器2: http://localhost:8080/helloapp/hello1?username=小鸭

可能两个浏览器得到的结果都是:

你好:小鸭

这是由于并发问题导致的。

 

解决方案:将username的定义放到service方法中,这样不同浏览器请求web服务的时候会有不同的username变量

 

8.使用Java同步机制对多线程同步

 

原文地址:https://www.cnblogs.com/foodie-nils/p/14883930.html