第三章 深入Servlet技术
3.1 配置Servlet
- <servlet-name>,<servlet-class>是必须配置的,以便于web容器知道浏览器具体访问的是哪个servlet。
- <init-param>用于初始化参数,在servlet中可使用getServletContext().getInitParam(String paramName)来获取初始化参数值。
- <load-on-startup>配置该servlet加载方式,置1时Tomcat将在启动时便加载该servlet,否在会在第一次请求时加载。
3.2 配置<servlet-mapping>
3.3 response生成图片验证码
//创建BufferedImage对象,设置图片的长度宽度和色彩。
BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB); OutputStream os = response.getOutputStream(); //取得Graphics对象,用来绘制图片
Graphics g = image.getGraphics();
……
……
……
//释放此图形的上下文以及它使用的所有系统资源,类似于关闭流
g.dispose(); //通过ImageIO对象的write静态方法将图片输出。
ImageIO.write(image, "JPEG", os);
os.close();
3.4 读取web.xml参数
上一节中创建一个servlet用来输出图片,当需要输出其它格式时,需要修改源代码。因此这种常量可以写在配置文件中,servlet读取参数即可。
例如:
3.5 上下文参数(context-param)
上一节中在web.xml中配置的初始化参数都是在特定的servlet中配置的,因此只能被此servlet读取。使用<context-param>可配置全局参数。例如:
3.6 资源注射(@Resource)
3.7 注射数据源
3.8 上传客户端
代码如下:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>上传文件</title>
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="this is my page">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" href="css/style.css">
</head> <body>
<form action="servlet/UploadServlet" method="post" enctype="multipart/form-data">
<div align="center"><br/>
<fieldset style="width:90%">
<legend>上传文件</legend><br/>
<div class='line'>
<div align='left' class="leftDiv">上传文件一</div>
<div align='left' class="rightDiv">
<input type="file" name="file1" class="text">
</div>
</div>
<div class='line'>
<div align='left' class="leftDiv">上传文件二</div>
<div align='left' class="rightDiv">
<input type="file" name="file2" class="text">
</div>
</div>
<div class='line'>
<div align='left' class="leftDiv">上传文件说明一</div>
<div align='left' class="rightDiv"><input type="text" name="description1" class="text"></div>
</div>
<div class='line'>
<div align='left' class="leftDiv">上传文件说明二</div>
<div align='left' class="rightDiv"><input type="text" name="description2" class="text"></div>
</div>
<div class='line'>
<div align='left' class="leftDiv"></div>
<div align='left' class="rightDiv"><br/>
<input type="submit" value=" 上传文件 " class="button">
</div>
</div>
</fieldset>
</div>
</form>
</body>
</html> 注意:上传文件必须设置method="post" enctype="multipart/form-data"
3.9 上传服务器
上一节中,通过html界面向服务器传送两个文件,和两个文件说明。这一节,将会对收到的请求进行解析,获取文件和文件说明。这里需要使用org.apache.commons.fileupload包。
代码如下:
File file1 = null, file2 = null;//声明文件流
String description1 = null, description2 = null; // 使用 DiskFileUpload 对象解析 request
DiskFileUpload diskFileUpload = new DiskFileUpload();
List<FileItem> list = diskFileUpload.parseRequest(request); for(FileItem fileItem : list){ //对list中的所有FileItem进行遍历,并判断其类型
if(fileItem.isFormField()){ //判断一个参数域是普通的表单输入域,还是文件上传域,如果该方法返回真的话,则是前者,如果为假,则是后者。
// 如果是 文本域
if("description1".equals(fileItem.getFieldName())){
// 如果该 FileItem 名称为 description1
out.println("遍历到 description1 ... <br/>");
description1 = new String(fileItem.getString().getBytes(), "UTF-8");
}
if("description2".equals(fileItem.getFieldName())){
// 如果该 FileItem 名称为 description2
out.println("遍历到 description2 ... <br/>");
description2 = new String(fileItem.getString().getBytes(), "UTF-8");
}
}
else{
// 否则,为文件域
if("file1".equals(fileItem.getFieldName())){
// 服务器端文件,放在 attachment文件夹下
file1 = new File(this.getServletContext().getRealPath("attachment"), remoteFile.getName());//第二个参数为子目录名
file1.getParentFile().mkdirs();
file1.createNewFile(); // 写文件,将 FileItem 的文件内容写到文件中
InputStream ins = fileItem.getInputStream();
OutputStream ous = new FileOutputStream(file1); try{
byte[] buffer = new byte[1024];
int len = 0;
while((len=ins.read(buffer)) > -1)
ous.write(buffer, 0, len);
out.println("已保存文件" + file1.getAbsolutePath() + "<br/>");
}finally{
ous.close();
ins.close();
}
}
思路:
- DiskFileUpload 对象通过parseRequest解析request并存入list集合,存放类型为FileItem
- 遍历list,通过对list中每个元素调用fileItem.isFormField(),返回false则说明该元素是文件
- 通过fileItem.getFieldName()获取该元素的文件名,通过InputStream ins = fileItem.getInputStream();创建输入流,
OutputStream ous = new FileOutputStream(File file1);创建输出流。
将输入流写入到输入流。
3.10 上传进度条
前端:Ajax查询上传进度
后端:写一个监听器(继承ProgressListener,是org.apache.commons.fileupload的一个接口),文件上传过程中,会不断调用这个监听器。
写一个状态类,用于保存上传状态相关数据(比如文件大小,上传时间,上传速度,百分百)
后端代码:
public class ProgressUploadServlet extends HttpServlet { private static final long serialVersionUID = -4935921396709035718L; public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { // 上传状态
UploadStatus status = new UploadStatus(); // 监听器
UploadListener listener = new UploadListener(status); // 把 UploadStatus 放到 session 里
request.getSession(true).setAttribute("uploadStatus", status); // Apache 上传工具
ServletFileUpload upload = new ServletFileUpload(
new DiskFileItemFactory()); // 设置 listener
upload.setProgressListener(listener); try {
List itemList = upload.parseRequest(request); for (Iterator it = itemList.iterator(); it.hasNext();) {
FileItem item = (FileItem) it.next();
if (item.isFormField()) {
System.out.println("FormField: " + item.getFieldName()
+ " = " + item.getString());
} else {
System.out.println("File: " + item.getName()); // 统一 Linux 与 windows 的路径分隔符
String fileName = item.getName().replace("/", "\\");
fileName = fileName.substring(fileName.lastIndexOf("\\")); File saved = new File("C:\\upload_test", fileName);
saved.getParentFile().mkdirs(); InputStream ins = item.getInputStream();
OutputStream ous = new FileOutputStream(saved); byte[] tmp = new byte[1024];
int len = -1; while ((len = ins.read(tmp)) != -1) {
ous.write(tmp, 0, len);
} ous.close();
ins.close(); response.getWriter().println("已保存文件:" + saved);
}
}
} catch (Exception e) {
e.printStackTrace();
response.getWriter().println("上传发生错误:" + e.getMessage());
}
} public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {//Ajax访问该servlet时会调用此函数 response.setHeader("Cache-Control", "no-store");
response.setHeader("Pragrma", "no-cache");
response.setDateHeader("Expires", 0); UploadStatus status = (UploadStatus) request.getSession(true)
.getAttribute("uploadStatus"); if (status == null) {
response.getWriter().println("没有上传信息");
return;
} long startTime = status.getStartTime();
long currentTime = System.currentTimeMillis(); // 已传输的时间 单位:s
long time = (currentTime - startTime) / 1000 + 1; // 传输速度 单位:byte/s
double velocity = ((double) status.getBytesRead()) / (double) time; // 估计总时间 单位:s
double totalTime = status.getContentLength() / velocity; // 估计剩余时间 单位:s
double timeLeft = totalTime - time; // 已完成的百分比
int percent = (int) (100 * (double) status.getBytesRead() / (double) status
.getContentLength()); // 已完成数 单位:M
double length = ((double) status.getBytesRead()) / 1024 / 1024; // 总长度 单位:M
double totalLength = ((double) status.getContentLength()) / 1024 / 1024; // 格式:百分比||已完成数(M)||文件总长度(M)||传输速率(K)||已用时间(s)||估计总时间(s)||估计剩余时间(s)||正在上传第几个文件
String value = percent + "||" + length + "||" + totalLength + "||"
+ velocity + "||" + time + "||" + totalTime + "||" + timeLeft
+ "||" + status.getItems(); response.getWriter().println(value);
} }
前端代码:
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<style type="text/css">
body, td, div {font-size: 12px; font-familly: 宋体; }
#progressBar {width: 400px; height: 12px; background: #FFFFFF; border: 1px solid #000000; padding: 1px; }
#progressBarItem {width: 30%; height: 100%; background: #FF0000; }
</style>
</head> <body> <iframe name=upload_iframe width=0 height=0></iframe> <form action="servlet/ProgressUploadServlet" method="post" enctype="multipart/form-data" target="upload_iframe" onsubmit="showStatus(); "> <input type="file" name="file1" style="width: 350px; "> <br />
<input type="file" name="file2" style="width: 350px; "> <br />
<input type="file" name="file3" style="width: 350px; "> <br />
<input type="file" name="file4" style="width: 350px; "> <input type="submit"
value=" 开始上传 " id="btnSubmit"></form> <div id="status" style="display: none; ">
上传进度条:
<div id="progressBar"><div id="progressBarItem"></div></div>
<div id="statusInfo"></div>
</div> <br/>
<br/>
<br/>
<br/>
<br/> <script type="text/javascript"> var _finished = true; function $(obj){
return document.getElementById(obj);
} function showStatus(){ //主函数
_finished = false;
$('status').style.display = 'block';
$('progressBarItem').style.width = '1%';
$('btnSubmit').disabled = true; setTimeout("requestStatus()", 1000); //每隔1000ms执行一次requestStatus()函数
} function requestStatus(){ if(_finished) return; var req = createRequest(); //建立请求 req.open("GET", "servlet/ProgressUploadServlet");
req.onreadystatechange=function(){callback(req);}// Ajax得到响应进入callback(req)函数
req.send(null); setTimeout("requestStatus()", 1000);
} function createRequest()
{
if(window.XMLHttpRequest)//ns
{
return new XMLHttpRequest();
}else//IE
{
try{
return new ActiveXObject("Msxml2.XMLHTTP");
}catch(e){
return new ActiveXObject("Microsoft.XMLHTTP");
}
}
return null;
}
function callback(req){ if(req.readyState == 4) {
if(req.status != 200){
_debug("发生错误。 req.status: " + req.status + "");
return;
} _debug("status.jsp 返回值:" + req.responseText); var ss = req.responseText.split("||"); // 格式:百分比||已完成数(M)||文件总长度(M)||传输速率(K)||已用时间(s)||估计总时间(s)||估计剩余时间(s)||正在上传第几个文件
$('progressBarItem').style.width = '' + ss[0] + '%';
$('statusInfo').innerHTML = '已完成百分比: ' + ss[0] + '% <br />已完成数(M): ' + ss[1] + '<br/>文件总长度(M): ' + ss[2] + '<br/>传输速率(K): ' + ss[3] + '<br/>已用时间(s): ' + ss[4] + '<br/>估计总时间(s): ' + ss[5] + '<br/>估计剩余时间(s): ' + ss[6] + '<br/>正在上传第几个文件: ' + ss[7]; if(ss[1] == ss[2]){
_finished = true;
$('statusInfo').innerHTML += "<br/><br/><br/>上传已完成。";
$('btnSubmit').disabled = false;
}
}
}
function _debug(obj){
var div = document.createElement("DIV");
div.innerHTML = "[debug]: " + obj;
document.body.appendChild(div);
} </script> </body>
</html
监听器代码:
import org.apache.commons.fileupload.ProgressListener; public class UploadListener implements ProgressListener { private UploadStatus status; public UploadListener(UploadStatus status) {
this.status = status;
} public void update(long bytesRead, long contentLength, int items) {
status.setBytesRead(bytesRead);
status.setContentLength(contentLength);
status.setItems(items);
}
}
状态类代码:
public class UploadStatus { private long bytesRead; private long contentLength; private int items; private long startTime = System.currentTimeMillis(); public long getBytesRead() {
return bytesRead;
} public void setBytesRead(long bytesRead) {
this.bytesRead = bytesRead;
} public long getContentLength() {
return contentLength;
} public void setContentLength(long contentLength) {
this.contentLength = contentLength;
} public int getItems() {
return items;
} public void setItems(int items) {
this.items = items;
} public long getStartTime() {
return startTime;
} public void setStartTime(long startTime) {
this.startTime = startTime;
} }
3.11 Servlet生命周期
之前在web.xml中配置的参数需要每次都在doGet()或doPost()中获取,效率较低。因此可以在servlet中写入init()函数,在函数中获取web.xml参数。这样,当用户第一次访问该servlet时,会获取初始化参数,而下一次访问servlet时便不再初始化参数,因此提高效率。
3.12 Servlet直接的跳转(Forward)
3.13 Servlet之间的跳转(重定向Redirect)
3.14 Servlet之间的跳转(自动刷新Refresh)
3.15 线程安全
servlet不是线程安全的,因此谨慎使用类的变量,应尽量将变量定义在函数doGet()内部。
第三章 深入Servlet技术的更多相关文章
- 第三章 使用Servlet处理HTTP响应
回顾上一章的知识: Java Servlet是运行在Web服务器或应用服务器上的Java程序 Servlet规范对Servlet功能进行了严格定义 Servlet API与容器进行通讯 Servlet ...
- 2017.11.2 JavaWeb----第六章 Servlet技术
JavaWeb ------第六章 Servlet技术 (1)在Web应用程序开发中,一般由JSP JavaBean技术和 Servlet技术的结合实现MVC开发模式.在MVC开发模式中将Web程序的 ...
- Java Web程序设计笔记 • 【第5章 Servlet技术】
全部章节 >>>> 本章目录 5.1 Servlet 技术介绍 5.1.1 为什么需要Servlet 5.1.1 Servlet 简介 5.1.2 Java Web 处理流 ...
- [转]TEC1401.Report开发技术总结 - 第三章 使用Oracle Reports开发报表-创建一个分组报表(2/4)
本文转自:http://blog.csdn.net/deepsea_allen/article/details/53900284 第三章 创建一个分组报表 1. 建立数据模型 数据模型用于 ...
- Mysql技术内幕-笔记-第三章 查询处理
第三章 查询处理 逻辑查询处理:(8) SELECT (9) DISTINCT <select_list> (1) FROM <left_table> (3) <join ...
- Java面试题精选(三) JSP/Servlet Java面试逻辑题
-- JSP/Servlet Java面试逻辑题 -- 很显然,Servlet/JSP的WEB前端动态制作的重要性比HTML/CSS/JS的价值高很多,但我们都知道他们都是建立在HT ...
- 精通Web Analytics 2.0 (5) 第三章:点击流分析的奇妙世界:指标
精通Web Analytics 2.0 : 用户中心科学与在线统计艺术 第三章:点击流分析的奇妙世界:指标 新的Web Analytics 2.0心态:搞定它.新的闪亮系列工具:是的.准备好了吗?当然 ...
- 《Linux内核设计与实现》课本第三章自学笔记——20135203齐岳
<Linux内核设计与实现>课本第三章自学笔记 进程管理 By20135203齐岳 进程 进程:处于执行期的程序.包括代码段和打开的文件.挂起的信号.内核内部数据.处理器状态一个或多个具有 ...
- 3-Spark高级数据分析-第三章 音乐推荐和Audioscrobbler数据集
偏好是无法度量的. 相比其他的机器学习算法,推荐引擎的输出更直观,更容易理解. 接下来三章主要讲述Spark中主要的机器学习算法.其中一章围绕推荐引擎展开,主要介绍音乐推荐.在随后的章节中我们先介绍S ...
随机推荐
- rails 中http请求发生access-control-allow-origin错误
在api项目中 本地项目无法访问服务器api 百度了下,查出原因 接着找到rails项目的解决方法,安装rack-cors这个gem包 具体方法如下: Gemfile中加入 gem 'rack-cor ...
- C 没得写的水文
引言 - 没得写 最近工作上需要处理事情很多(接手, 维稳, 危机), 还有深入读书计划也提上了日程. 为了每月水经验, 这里带大家写个 C 的多值返回吧 : ) #include <stdio ...
- 2017-2018-1 20155239 《信息安全系统设计基础》第五周学习总结+mybash的实现
2017-2018-1 20155239 <信息安全系统设计基础>第五周学习总结+mybash的实现 mybash的实现 使用fork,exec,wait实现mybash 写出伪代码,产品 ...
- 10 ORM 多表操作 查询
1.子查询:基于对象的跨表查询 def query(request): """ 跨表查询: 1.基于对象查询 2.基于双下划线查询 3.聚合.分组查询 4. F Q 查询 ...
- [SCOI2016]幸运数字 树链剖分,线性基
[SCOI2016]幸运数字 LG传送门 为了快乐,我们用树剖写这题. 强行树剖,线段树上每个结点维护一个线性基,每次查询暴力合并. 瞎分析一波复杂度:树剖两点之间\(\log n\)条重链,每条重链 ...
- kruskal重构树
kruskal重构树 kruskal重构树,顾名思义,是在kruskal的时候顺便搞出来的一棵重构树,具体地说是一个堆. 先说说这个东西是怎么搞出来的吧:默认事先把边按边权从小到大排序,在kruska ...
- msfvenom生成linux后门
msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST=ip LPORT=port -f elf > shell.elf
- pathon之多线程详解
一.线程理论 1.什么是线程 线程指的是一条流水线的工作过程 进程根本就不是一个执行单位,进程其实是一个资源单位--------将资源集合到一起: 一个进程内自带一个线程,线程才是CPU上的执行单位 ...
- Object C学习笔记9-字符串NSMutableString
NSMutableString类继承自NSString,所以在NSString中的方法在NSMutableString都可以使用. NSMutableString和NSString的区别在于NSMut ...
- Excel表格生成sql语句
假如excel表格中有A.B.C三列数据,希望导入到数据库users表中,对应的字段分别是name,sex,age ,在你的excel表格中增加一列,利用excel的公式自动生成sql语句,方法如下: ...