Servlet是一种实现动态页面的技术,是一组Tomcat提供给程序员的API,帮助程序员简单高效的开发一个 web app(网站)。
静态页面也就是内容始终固定的页面,即使用户不同/时间不同/输入的参数不同,页面内容也不会发生变化(除非网站的开发人员修改源代码,否则页面内容始终不变)
对应的,动态页面指的就是 用户不同/时间不同/输入的参数不同,页面内容会发送变化
metadata-complete="true">hello HelloServlet hello /hello
metadata-complete="false">
首先要创建一个 Maven项目
在Maven项目里的pom.xml里引入依赖,从中央仓库的Servlet api 的jar包
选择Tomcat8对应的3.1版本的Servlet依赖
javax.servlet javax.servlet-api 3.1.0 provided
把依赖放到 pom.xml中的 dependencies标签中
开始写代码
1.先创建好一个类继承HttpServlet
2.重写doGet方法
@WebServlet("/test")
public class TestServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//这个调用父类的方法一定要干掉//super.doGet(req, resp);resp.getWriter().write("hello Servlet");}
}
//doGet是HttpServlet这个父类的方法
//这个方法的参数分别是
// HttpServletRequest:HTTP请求
//HttpServletResponse:HTTP响应
//doGet方法里要做的事情,就是根据请求,生成响应
注意:重写的 doGet 方法之后,并不需要手动调用 doGet,而是由Tomcat自动来调用,也不需要咱们手动的创建 TestServlet 实例,也是由Tomcat自动创建实例
resp.getWriter.write("hello Servlet");
这个操作,就是往HTTP响应的body中,写了一个 "hello Servlet"字符串
@WebServlet("/test")
@WebServlet 也是Servlet中提供的注解,功能就是把类和HTTP 特定请求进行关联,根据HTTP请求URL的路径来进行关联的
如果咱们的Tomcat收到了一个路径为 /test 的请求,就会调用到 TestServlet 的代码
如果这个请求是GET请求,就会调用到HelloServlet.doGet 方法
如果这个请求是POST请求,就会调用到HelloServlet.doPost方法
注意
super.doGet(req, resp)
重写方法后,一定要把这个调用父类代码的操作,给干掉
这个方法里面直接构造了一个错误的响应(状态码为 405 的响应)
在main目录中,创建一个webapp目录,里面再创建一个 WEB-INF目录,在WEB-INF里再创建 web.xml文件
把下面的代码复制到web.xml中
Archetype Created Web Application
webapp 目录就是未来部署到 Tomcat 中的一个重要的目录. 当前我们可以往 webapp 中放一些静态资源, 比如 html , css 等.
在这个目录中还有一个重要的文件 web.xml. Tomcat 找到这个文件才能正确处理 webapp 中的动态资源
双击package就能进行打包,或者右击运行
由于Maven默认打成的是一个 jar格式的包,而Tomcat不能识别 jar格式的包。Tomcat识别的是war格式的包,所以要修改 maven 中的 pom.xml 的配置,修改打包类型为 war
在 pom,xml中添加下面这行代码,就能让maven打成的包为 war格式
war
打包的包名默认是 artifact id + version,名字太复杂
在pom.xml中添加下面的代码就能修改打包的包名
java
把war包拷贝到 Tomcat的webapps目录中即可,启动Tomcat之后,就会解压缩出来一个同名的目录
完整的pom.xml
4.0.0 org.example TestServlet 1.0-SNAPSHOT 8 8 war java javax.servlet javax.servlet-api 3.1.0 provided
IDEA专业版自带 Tomcat的部署功能,不需要使用 Smart Tomcat插件
IDEA社区版没有自带Tomcat的部署功能,需要使用 Smart Tomcat插件
出现404最大的原因就是URL的路径写错了
或者是@WebServlet注解的内容写错了,比如少了一个 /,或者是URL的路径和注解对不上号
如果 web.xml写错了,也可能导致404
405最重要的原因,请求的方法和代码中重写的方法对不上号
往浏览器里输入URL应该是GET请求,而实现的却是POST方法
还有一个原因就是调用父类的 doGet方法,没有删掉。
父类的doGet方法是直接返回405的
出现500最主要的原因就是代码中抛异常了,页面上/Tomcat日志里面会明确提示出异常的调用栈等详细信息
在实际开发中,异常调用栈直接展示在页面上,是一件非常危险的事情
一个商业产品,如果异常调用栈被用户看到了,就可能带来安全隐患
从异常调用栈里可以看到出错误的代码的具体位置,也知道了是用什么代码写的
同时也知道了 Tomcat的版本号
黑客手里一般都有漏洞库,就可以去找对应的版本的漏洞库,从而对你的服务器进行攻击(很可能会入侵成功,窃取破坏服务器上的信息)
@WebServlet("/test")
public class TestServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//resp.getWriter().write("hello Servlet");}
}
如果没有调用resp.getWriter().write,没往body里写就会出现空白页面。有时候可能里面的代码比较复杂,没有注意是否调用resp.getWriter().write就可能出现这种情况
一般就是Tomcat启动失败了
学习Servlet的具体使用,核心掌握三个类就够了
方法名 | 调用时机 |
---|---|
init | 在 HttpServlet 实例化之后被调用一次 |
destory | 在 HttpServlet 实例不再使用的时候调用一次 |
service | 收到 HTTP 请求的时候调用 |
doGet | 收到 GET 请求的时候调用(由 service 方法调用) |
doPost | 收到 POST 请求的时候调用(由 service 方法调用 |
doPut/doDelete/doOptions/… | 收到其他请求的时候调用(由 service 方法调用) |
实际开发中主要重写 doxxx方法,很少会重写init/destory/service。
这些方法的调用时机,就称为"Serlet生命周期",也就是描述了一个Servlet实例从生到死的过程
一个常见的面试题:说一下Servlet的生命周期
创建一个 MethodServlet类
@WebServlet("/method")
public class MethodServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//设置响应的内容类型和字符编码resp.setContentType("text/html;charset=utf-8");resp.getWriter().write("get 请求");}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("text/html;charset=utf-8");resp.getWriter().write("post 请求");}@Overrideprotected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("text/html; charset=utf-8");resp.getWriter().write("PUT 请求");}
}
html代码
MethodServlet
setContentType
这 个方法设置发送到客户端的响应的内容类型,此时响应还没有提交。给出的内容类型可以包括字符编码说明,此处注明响应编码是 utf-8
该方法如果在getWriter()方法被调用之后或者在被提交之后调用,将不会设置响应的字符编码
由Tomcat把字符串结构的请求解析成了一个结构化的数据
从字符串到结构化数据的过程,称为"反序列化"
URL和URI
URL和URI含义是类似的,都是表示网络上的一个资源
L Lication,资源的位置
I Id, 资源的标识符
所以这俩东西往往使用的场景都差不多,也就不做过多区分了
方法 | 描述 |
---|---|
String getProtocol() | 返回请求协议的名称和版本 |
String getMethod() | 返回请求的 HTTP 方法的名称,例如,GET、POST 或 PUT |
String getRequestURI() | 从协议名称直到 HTTP 请求的第一行的查询字符串中,返回该请求的 URL 的一部分 |
String getContextPath() | 返回指示请求上下文的请求 URI 部分 |
String getQueryString() | 返回包含在路径后的请求 URL 中的查询字符串,得到的是完整的QueryString |
Enumeration getParameterNames() | 返回一个 String 对象的枚举,包含在该请求中包含的参数的名称,也就是header中所有键值对的key |
String getParameter(String name) | 以字符串形式返回请求参数的值,或者如果参数不存在则返回null,输入key返回jQuery中的value值 |
String[] getParameterValues(String name) | 返回一个字符串对象的数组,包含所有给定的请求参数的值,如果参数不存在则返回 null |
Enumeration getHeaderNames() | 返回一个枚举,包含在该请求中包含的所有的头名 |
String getHeader(String name) | 以字符串形式返回指定的请求头的值,header中的键值对的value |
String getCharacterEncoding() | 返回请求主体中使用的字符编码的名称 |
String getContentType() | 返回请求主体的 MIME 类型,如果不知道类型则返回 null |
int getContentLength() | 以字节为单位返回请求主体的长度,该方法用于获取请求的 Body 的长度,如果不确定长度,则返回 -1 |
InputStream getInputStream() | 用于读取请求的 body 内容. 返回一个 InputStream 对象 |
通过这些方法可以获取到一个请求中的各个方面的信息
注意:请求对象是服务器收到的内容, 不应该修改. 因此上面的方法也都只是 “读” 方法, 而不是 “写”
方法
@WebServlet("/showRequest")
public class ShowRequestServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("text/html; charset=utf-8");StringBuilder requestBody = new StringBuilder();//获取协议的名称和版本requestBody.append(req.getProtocol());requestBody.append("
");//返回HTTP方法名称requestBody.append(req.getMethod());requestBody.append("
");//返回资源的标识和URL类似requestBody.append(req.getRequestURI());requestBody.append("
");//返回ContextPath,就是那个用来区分webapp的根路径requestBody.append(req.getContextPath());requestBody.append("
");//返回完整的querystringrequestBody.append(req.getQueryString());requestBody.append("headers:
");//返回一个枚举类型返回请求头 header中所有键值对的 keyEnumeration headerNames = req.getHeaderNames();//类似于迭代器的遍历while (headerNames.hasMoreElements()) {String headerName = headerNames.nextElement();requestBody.append(headerName+": ");//getHeader是根据key获取valuerequestBody.append(req.getHeader(headerName));requestBody.append("
");}resp.getWriter().write(requestBody.toString());}
}
GET请求中的参数一般都是通过query string 传递给服务器的列如:
http://127.0.0.1:8080/TestServlet/showRequest?userId=111&classId=222
此时浏览器通过 query string 给服务器传递了两个参数, userId 和 classId,值分别是 111和222
在服务端就可以通过 getParameter来获取到参数的值
@WebServlet("/getParameter")
public class GetParameter extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//设置响应的内容类型和字符编码resp.setContentType("text/html; charset=utf-8");String userId = req.getParameter("userId");String classId = req.getParameter("classId");resp.getWriter().write("userId: "+userId+" classId: "+classId);}
}
部署到浏览器可以看到
getParameter的返回值类型是String,必要时可以把它转换为int
POST请求中的参数,主要是放在 body中(POST也是可以在query string中放参数的,但是比较少)
POST请求的body中的格式有好几种
application/x-www-form-urlencoded
类似于a=111&b=222,和 query string的格式类似,直接还是使用 getParameter方法来获取(和获取query string没区别)
mutlipart/form-data
这个种格式比较复杂,主要用来提交文件的,生成一个分隔符
application/json
这就是 jion的格式
{a: 111,b: 222
}
约定请求如同
TestServlet/postParameter
uestId=111&classId=222
这种构造POST请求的方式,还是可以用getParameter来获取
@WebServlet("/postParameter")
public class PostParameterServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("text/html; charset=utf-8");String userId = req.getParameter("userId");String classId = req.getParameter("classId");resp.getWriter().write("userId: "+userId+" classId: "+classId);}
}
把html文件放入webapp目录中
json格式本身解析起来是比较复杂的(json里面能嵌套),更好的办法,是直接使用第三方库。
Java世界中能够解析JSON的第三方库,有很多。
使用 Jackson这样的库,Jsckson是Spring全家桶里面指定使用的库
可以通过Maven从中央仓库下载 Jackson Databind
通过jackson 的核心对象, ObjectMapper,调用readValue就完成了JSON格式的字符串到Java对象的解析。
此处的body 的值为
{"userId":111,"classId":222}
- 先把JSON格式的字符串转换成类似于HashMap的键值对结构
userId => 111
classId => 222
根据类对象,获取到要转换结果的类,都有哪些属性,每个属性的名字
此处就通过 Student 获取到,里面的属性有两个,名字分别是userId和classId(通过放射机制)
拿着Student这里的每个属性的名字,去上面第一步构造的哈希表里查,如果查到了,就把查询的值赋值到Student对应的属性里面
class Student {public int userId;public int classId; } //此处创建这个Student类的时候,就需要这里的成员名字和JSON字符串里的key是匹配的
//通过这个类表示json解析后的结果
class Student {public int userId;public int classId;
}
@WebServlet("/postParameterJson")
public class PostParameterJsonServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//读取整个bodyString jsonBody = getBody(req);//jackson 的核心对象, ObjectMapperObjectMapper objectMapper = new ObjectMapper();//下面这行代码就完成了JSON格式的字符串到Java对象的解析过程Student student = objectMapper.readValue(jsonBody,Student.class);resp.setContentType("text/html; charset=utf-8");//resp.getWriter().write("userId: "+student.userId+" classId: "+student.classId);}private String getBody(HttpServletRequest req) throws IOException {//读取 body 需要根据 req getInputStream 得到一个流对象, 从这个流对象中读取InputStream inputStream = req.getInputStream();//获取body中的字节数int contentLength = req.getContentLength();//建立一个body大小的缓冲区byte[] buffer = new byte[contentLength];inputStream.read(buffer);inputStream.close();return new String(buffer,"utf-8");}
}
对应的HTML构造请求的代码
平时使用HttpServletRequest最常用的工作,救赎获取得到请求中的参数
HttpServletResponse和HTTP协议的响应格式,是对应的
HttpServletRequest,里面的内容,是客户端构造的,服务器需要做的是获取到这里的内容(尤其是程序员自定义的数据)
HttpServletResponse,里面的内容,是服务器构造的,要返回给客户端
对于HTTP响应来说,header中的key并不要求非得是唯一的,有些key可以重复出现,典型的就是Set-Cookie属性
核心方法
方法 | 描述 |
---|---|
void setStatus(int sc) | 为该响应设置状态码 |
void setHeader(String name, String value) | 设置一个带有给定的名称和值的 header. 如果 name 已经存在, 则覆盖旧的值 |
void addHeader(String name, String value) | 添加一个带有给定的名称和值的 header. 如果 name 已经存在, 不覆盖旧的值, 并列添加新的键值对 |
void setContentType(String type) | 设置被发送到客户端的响应的内容类型 |
void setCharacterEncoding(String charset) | 设置被发送到客户端的响应的字符编码(MIME 字符集)例 如,UTF-8 |
void sendRedirect(String location) | 使用指定的重定向位置 URL 发送临时重定向响应到客户端 |
PrintWriter getWriter() | 用于往 body 中写入文本格式数据 |
OutputStream getOutputStream() | 用于往 body 中写入二进制格式数据 |
注意:对于状态码/响应头的设置要放到 getWriter / getOutputStream 之前. 否则可能设置失效
注意:响应对象是服务器要返回给浏览器的内容, 这里的重要信息都是程序猿设置的. 因此上面的方
法都是 “写” 方法.
如何构造一个重定向响应呢?
重定向就是"呼叫转移",Location字段,描述的就是重定向的位置
把状态码设置为302,在header中设置 Location就可以了
或者直击使用 sendRedirect 方法,发送重定向响应给客户端
@WebServlet("/redirectServlet")
public class RedirectServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// resp.setStatus(302);
// resp.setHeader("Location","https://www.baidu.com");resp.sendRedirect("https://www.baidu.com");}
}
用户在浏览器通过参数指定要返回响应的状态码
@WebServlet("/setStatus")
public class statusServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 让用户传入一个请求.// 请求在 query string 中带一个参数. 就表示响应的状态码// 然后根据用户的输入, 返回不同的状态码的响应String status = req.getParameter("status");resp.setContentType("text/html; charset=utf-8");if (status == null) {resp.getWriter().write("status没有参数");return;}resp.getWriter().write("status: "+Integer.parseInt(status));}
}
HTTP响应中可以设置一个 header,Refresh,值就是刷新的时间(单位是s)
通过 HTTP 响应报头中的 Refresh 字段, 可以控制浏览器自动刷新的时机.实现每秒刷新一次显示当前时间戳
@WebServlet("/autoRefresh")
public class AutoRefreshServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//HTTP响应中可以设置一个 header,Refresh,值就是刷新的间隔时间resp.setHeader("Refresh", "1");resp.setContentType("text/html; charset=utf-8");//获得当前的毫秒级时间戳long time = System.currentTimeMillis();resp.getWriter().write("time: "+time);}
}
服务端的代码:
注意:TestMessage这个类的字段必须要是 public,不然 objectMapper.readValue方法,json字符串转java对象会报错
注意:Content-Type是 text/html 或者text /plain,body的数据就是String
Content-Type是一个application/json,body的数据就会被jQuery给自动转换成 object/Array
class TestMessage {//这一定要是publicpublic String from;public String to;public String message;
}
@WebServlet("/message")
public class MessageWallServlet extends HttpServlet {//jackson的核心对象,用来解析json字符串private ObjectMapper objectMapper = new ObjectMapper();//这个list用来存储已经保存的留言信息private List messagesList = new ArrayList();//init用来生成数据,测试从服务器是否能成功获取数据@Overridepublic void init() throws ServletException {TestMessage testMessage = new TestMessage();testMessage.from = "张三";testMessage.to = "李四";testMessage.message = "哈哈哈";messagesList.add(testMessage);}//doGet方法用来从服务器获取已经保存的留言信息@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("application/json;charset=utf-8");//writeValue方法,会把 messagesList的数据转换为json格式的字符串通过getWriter写入resp中objectMapper.writeValue(resp.getWriter(),messagesList);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//通过readValue方法,把流对象从body读取出来的json格式的数据转换为java对象TestMessage message = objectMapper.readValue(req.getInputStream(),TestMessage.class);messagesList.add(message);resp.setContentType("application/json; charset=utf-8");resp.getWriter().write("{\"ok\": 1}");}
}
HTML代码
留言墙
留言墙
输入后点击提交, 会将信息显示在墙上
谁对谁说什么
当前服务器是把数据保存到了 messagesList 变量中,变量就是内存,如果服务器重启,内存中的数据就消失了
刚才版本的留言墙数据不能保存,下面就实现一个用文件保存的留言墙
这里涉及到了一个 类 :BufferedReader 传入一个 FileReader就能进行行读取,同样Scanner 也可以实现读取一行
还有一个类:FileWriter
这个类有一个构造方法,传入一个字符串路径,第二个参数一定要注意,传入一个boolean类型的变量,这里的第二个参数为true,则表示文件会以追加的方式打开,不会清楚原来的数据,直接在文件末尾进行拼接写入文件
如果为false就会把文件清空,重新开始写
@WebServlet("/message")
public class MessageWallFileServlet extends HttpServlet {//用来解析JSON字符串private ObjectMapper objectMapper = new ObjectMapper();//保存留言信息文件的路径private static final String FILEPATH = "D:/message.txt";//doGet方法用来处理从服务器获取到的消息的数据@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("application/json;charset=utf-8");List messages = readFile();//把messages里的数据读取出来转换成json格式写到resp中objectMapper.writeValue(resp.getWriter(),messages);}private List readFile() {List result = new ArrayList();//此处我们需要按行读取. FileReader 本身不支持. 需要套上一层 BufferedReader//使用Scanner也行try (BufferedReader bufferedReader = new BufferedReader(new FileReader(FILEPATH))) {System.out.println("开始从文件读取数据");while (true) {//一次读取出一行数据String line = bufferedReader.readLine();if (line == null) {//如果数据读完了就会返回nullbreak;}String[] strMessage = line.split("\t");TestMessage testMessage = new TestMessage();testMessage.from = strMessage[0];testMessage.to = strMessage[1];testMessage.message = strMessage[2];result.add(testMessage);}System.out.println("数据读取完毕");} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return result;}//Post方法用来处理客户端提交的数据@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//把req里的json格式的数据解析成 java对象TestMessage testMessage = objectMapper.readValue(req.getInputStream(),TestMessage.class);save(testMessage);//返回一个响应resp.getWriter().write("{\"ok\": 1}");}private void save(TestMessage testMessage) {System.out.println("向文件中写入数据");//FileWriter 的使用方法, 和PrintWriter 差不多. 里面都是有一个关键的方法叫做 write//这里的第二个参数为true,则表示文件会以追加的方式打开,不会清楚原来的数据,直接在文件末尾进行拼接写入文件try (FileWriter fileWriter = new FileWriter(FILEPATH,true)) {//以\t分割数据和上面的读数据的分割对应//写入文件的格式也有很多方式. 可以直接写 json, 也可以使用行文本(每个记录占一行, 字段之间使用分隔符区分)fileWriter.write(testMessage.from+"\t"+testMessage.to+"\t"+testMessage.message+"\n");} catch (IOException e) {e.printStackTrace();}}
}
服务器代码
@WebServlet("/messageSql")
public class MessageWallSQLServlet extends HttpServlet {private DataSource connection = null;//用来解析JSON字符串private ObjectMapper objectMapper = new ObjectMapper();//从服务器获取数据@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//从数据库中读取所有数据List messages = sqlLoad();resp.setContentType("application/json; charset=utf-8");//把message中的数据读取出来转换成json格式的数据写入respobjectMapper.writeValue(resp.getWriter(),messages);}/*** 从数据库中读取数据* @return*/private List sqlLoad() {List result = new ArrayList<>();Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;System.out.println("开始从数据库中读取数据");try {//1.与数据库建立连接connection = DBUtil.getConnection();//2.拼装 sqlString sql = "select * from message";statement = connection.prepareStatement(sql);//3.statement 对象中的SQL查询,然后返回一个 ResultSet 对象的结果集resultSet = statement.executeQuery();//遍历结果集合while (resultSet.next()) {TestMessage message = new TestMessage();message.from = resultSet.getString("from");message.to = resultSet.getString("to");message.message = resultSet.getString("message");result.add(message);}} catch (SQLException throwables) {throwables.printStackTrace();} finally {//释放资源DBUtil.close(resultSet,statement,connection);}System.out.println("读取数据完成");return result;}//处理客户端发送过来的数据@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//把req里的json数据通过流对象全部读取出来 转换为java对象TestMessage message = objectMapper.readValue(req.getInputStream(),TestMessage.class);sqlSave(message);//给客户端发送响应resp.setContentType("application/json; charset=utf-8");resp.getWriter().write("{\"ok\": 1}");}/*** 把数据写入数据库* @param message*/private void sqlSave(TestMessage message) {Connection connection = null;PreparedStatement statement = null;try {//1.和数据库建立连接connection = DBUtil.getConnection();//2.拼装sqlString sql = "insert into message values (?,?,?)";statement = connection.prepareStatement(sql);statement.setString(1,message.from);statement.setString(2,message.to);statement.setString(3,message.message);//3.执行sqllong line = statement.executeLargeUpdate();if (line == 1) {System.out.println("数据插入成功");} else {System.out.println("数据插入失败");}} catch (SQLException throwables) {throwables.printStackTrace();} finally {//释放资源DBUtil.close(null,statement,connection);}}
}
连接数据库代码
public class DBUtil {private static final String URL = "jdbc:mysql://127.0.0.1:3306/java?characterEncoding=utf8&useSSL=false";private static final String USER = "root";private static final String PASSWORD = "root";private static volatile DataSource dataSource = null;private DBUtil(){}private static DataSource getDataSource() {if (dataSource == null) {synchronized (DBUtil.class) {if (dataSource == null) {dataSource = new MysqlDataSource();((MysqlDataSource)dataSource).setURL(URL);((MysqlDataSource)dataSource).setUser(USER);((MysqlDataSource)dataSource).setPassword(PASSWORD);}}}return dataSource;}public static Connection getConnection() throws SQLException {return getDataSource().getConnection();}/*** 释放资源* @param resultSet* @param statement* @param connection*/public static void close(ResultSet resultSet, PreparedStatement statement, Connection connection) {if (resultSet != null) {try {resultSet.close();} catch (SQLException throwables) {throwables.printStackTrace();}}if (statement != null) {try {statement.close();} catch (SQLException throwables) {throwables.printStackTrace();}}if (connection != null) {try {connection.close();} catch (SQLException throwables) {throwables.printStackTrace();}}}
}
对于上传文件,Servlet中也是有一些相关的支持的
HttpServletRequest类方法
方法 | 描述 |
---|---|
Part getPart(String name) | 获取请求中给定 name 的文件 |
Collection getParts() | 获取所有的文件 |
Part 类方法
方法 | 描述 |
---|---|
String getSubmittedFileName() | 获取提交的文件名 |
String getContentType() | 获取提交的文件类型 |
long getSize() | 获取文件的大小 |
void write(String path) | 把提交的文件数据写入磁盘文件 |
加上@MultipartConfig注解之后,Servlet才能够正确的读取请求中的文件内容
实现一个上传图片的代码
@MultipartConfig
@WebServlet("/upload")
public class UploadFileServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获取请求中 name为image的文件(form表单中input的name对应)Part imagePart = req.getPart("image");System.out.println(imagePart);//获取真实的文件名System.out.println(imagePart.getName());//获取文件的类型System.out.println(imagePart.getContentType());//获取文件的大小System.out.println(imagePart.getSize());//把文件写到磁盘中imagePart.write("D:/root/test.jpg");resp.setContentType("text/html; charset=utf-8");resp.getWriter().write("提交成功");}
}
html代码
注意
如果form表单是上传文件,那么 enctype字段的参数必须是 “multipart/form-data”
input中的name要和 java代码中的 getPart 通过name获取的参数对应
下载文件咋办?
下载文件只需要把文件从磁盘上读取出来,放到body中即可
Content-Type也根据文件的类型来进行设置