Servlet学习
创始人
2024-06-02 18:37:17
0

文章目录

  • Servlet
    • 静态页面和动态页面
    • 第一个Servlet程序
      • xml配置路由映射
      • Servlet 注解的 web.xml 配置文件
      • @WebServlet
      • web.xml
      • 打包操作
      • 部署程序
    • Smart Tomcat
    • Servlet常见问题
      • 1.404
      • 2.405
      • 3.500
      • 4.空白页面
      • 5.无法访问此网站
    • Servlet API
      • HttpServlet
      • Serlet的生命周期
        • 代码实例:处理GET、POST、PUT请求
        • 乱码处理
      • HttpServletRequest
        • 代码示例:打印request中的信息
        • 代码示例:获取GET请求中的参数
        • 代码示例:获取POST请求中的参数
          • 通过form表单
          • 通过json构造的参数
          • Jackson Databind
          • 小结
      • HttpServletResponse
        • 代码示例:实现重定向(Location)
        • 代码示例:设置转态码
        • 代码示例:让页面自动刷新(Refresh)
        • 代码示例:留言墙
        • 代码示例:留言墙文件版本
        • 代码示例:数据库版本留言墙
      • 上传文件
        • 核心方法
        • 代码示例
          • @MultipartConfig


Servlet

Servlet是一种实现动态页面的技术,是一组Tomcat提供给程序员的API,帮助程序员简单高效的开发一个 web app(网站)。

静态页面和动态页面

静态页面也就是内容始终固定的页面,即使用户不同/时间不同/输入的参数不同,页面内容也不会发生变化(除非网站的开发人员修改源代码,否则页面内容始终不变)

对应的,动态页面指的就是 用户不同/时间不同/输入的参数不同,页面内容会发送变化

第一个Servlet程序

xml配置路由映射


metadata-complete="true">helloHelloServlethello/hello

Servlet 注解的 web.xml 配置文件


metadata-complete="false">
  1. 首先要创建一个 Maven项目

  2. 在Maven项目里的pom.xml里引入依赖,从中央仓库的Servlet api 的jar包

    选择Tomcat8对应的3.1版本的Servlet依赖

在这里插入图片描述


javax.servletjavax.servlet-api3.1.0provided

把依赖放到 pom.xml中的 dependencies标签中

  1. 开始写代码

    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

@WebServlet("/test")

@WebServlet 也是Servlet中提供的注解,功能就是把类和HTTP 特定请求进行关联,根据HTTP请求URL的路径来进行关联的

如果咱们的Tomcat收到了一个路径为 /test 的请求,就会调用到 TestServlet 的代码

如果这个请求是GET请求,就会调用到HelloServlet.doGet 方法

如果这个请求是POST请求,就会调用到HelloServlet.doPost方法

注意

super.doGet(req, resp)

重写方法后,一定要把这个调用父类代码的操作,给干掉

这个方法里面直接构造了一个错误的响应(状态码为 405 的响应)

web.xml

在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.0org.exampleTestServlet1.0-SNAPSHOT88warjavajavax.servletjavax.servlet-api3.1.0provided

Smart Tomcat

IDEA专业版自带 Tomcat的部署功能,不需要使用 Smart Tomcat插件

IDEA社区版没有自带Tomcat的部署功能,需要使用 Smart Tomcat插件

在这里插入图片描述

在这里插入图片描述

Servlet常见问题

1.404

出现404最大的原因就是URL的路径写错了

或者是@WebServlet注解的内容写错了,比如少了一个 /,或者是URL的路径和注解对不上号

在这里插入图片描述

如果 web.xml写错了,也可能导致404

2.405

405最重要的原因,请求的方法和代码中重写的方法对不上号

往浏览器里输入URL应该是GET请求,而实现的却是POST方法

在这里插入图片描述

还有一个原因就是调用父类的 doGet方法,没有删掉。

父类的doGet方法是直接返回405的

在这里插入图片描述

3.500

出现500最主要的原因就是代码中抛异常了,页面上/Tomcat日志里面会明确提示出异常的调用栈等详细信息

在这里插入图片描述

在实际开发中,异常调用栈直接展示在页面上,是一件非常危险的事情

一个商业产品,如果异常调用栈被用户看到了,就可能带来安全隐患

从异常调用栈里可以看到出错误的代码的具体位置,也知道了是用什么代码写的

同时也知道了 Tomcat的版本号

黑客手里一般都有漏洞库,就可以去找对应的版本的漏洞库,从而对你的服务器进行攻击(很可能会入侵成功,窃取破坏服务器上的信息)

4.空白页面

@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就可能出现这种情况

5.无法访问此网站

一般就是Tomcat启动失败了

在这里插入图片描述

Servlet API

学习Servlet的具体使用,核心掌握三个类就够了

  • HttpServlet
  • HttpServletRequest
  • HttpServletResponse

HttpServlet

方法名调用时机
init在 HttpServlet 实例化之后被调用一次
destory在 HttpServlet 实例不再使用的时候调用一次
service收到 HTTP 请求的时候调用
doGet收到 GET 请求的时候调用(由 service 方法调用)
doPost收到 POST 请求的时候调用(由 service 方法调用
doPut/doDelete/doOptions/…收到其他请求的时候调用(由 service 方法调用)

实际开发中主要重写 doxxx方法,很少会重写init/destory/service。

这些方法的调用时机,就称为"Serlet生命周期",也就是描述了一个Servlet实例从生到死的过程

Serlet的生命周期

一个常见的面试题:说一下Servlet的生命周期

  1. Servlet在实例化之后调用一次 init
  2. Servlet每次收到请求,调用一次service
  3. Servlet在销毁之前,调用一次destroy

代码实例:处理GET、POST、PUT请求

创建一个 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()方法被调用之后或者在被提交之后调用,将不会设置响应的字符编码

在这里插入图片描述

HttpServletRequest

由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 对象

通过这些方法可以获取到一个请求中的各个方面的信息

注意:请求对象是服务器收到的内容, 不应该修改. 因此上面的方法也都只是 “读” 方法, 而不是 “写”
方法

代码示例:打印request中的信息

@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请求中的参数

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请求中的参数

POST请求中的参数,主要是放在 body中(POST也是可以在query string中放参数的,但是比较少)

POST请求的body中的格式有好几种

  1. application/x-www-form-urlencoded

    类似于a=111&b=222,和 query string的格式类似,直接还是使用 getParameter方法来获取(和获取query string没区别)

  2. mutlipart/form-data

    这个种格式比较复杂,主要用来提交文件的,生成一个分隔符

  3. application/json

    这就是 jion的格式

    {a: 111,b: 222
    }
    
通过form表单

约定请求如同

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构造的参数
Jackson Databind

json格式本身解析起来是比较复杂的(json里面能嵌套),更好的办法,是直接使用第三方库。

Java世界中能够解析JSON的第三方库,有很多。

使用 Jackson这样的库,Jsckson是Spring全家桶里面指定使用的库

可以通过Maven从中央仓库下载 Jackson Databind

在这里插入图片描述

通过jackson 的核心对象, ObjectMapper,调用readValue就完成了JSON格式的字符串到Java对象的解析。

此处的body 的值为

{"userId":111,"classId":222}
  1. 先把JSON格式的字符串转换成类似于HashMap的键值对结构

userId => 111

classId => 222

  1. 根据类对象,获取到要转换结果的类,都有哪些属性,每个属性的名字

    此处就通过 Student 获取到,里面的属性有两个,名字分别是userId和classId(通过放射机制)

  2. 拿着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最常用的工作,救赎获取得到请求中的参数

  1. 通过 query string , getParameter方法
  2. 通过 body(applocation/x-www-form-urlencoded), 也是 getParameter方法
  3. 通过body(application.json),先把整个body读取出来,然后再使用json库来解析

HttpServletResponse

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)

如何构造一个重定向响应呢?

重定向就是"呼叫转移",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));}
}

在这里插入图片描述

代码示例:让页面自动刷新(Refresh)

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

加上@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也根据文件的类型来进行设置


相关内容

热门资讯

122.(leaflet篇)l... 听老人家说:多看美女会长寿 地图之家总目录(订阅之前建议先查看该博客) 文章末尾处提供保证可运行...
育碧GDC2018程序化大世界... 1.传统手动绘制森林的问题 采用手动绘制的方法的话,每次迭代地形都要手动再绘制森林。这...
育碧GDC2018程序化大世界... 1.传统手动绘制森林的问题 采用手动绘制的方法的话,每次迭代地形都要手动再绘制森林。这...
Vue使用pdf-lib为文件... 之前也写过两篇预览pdf的,但是没有加水印,这是链接:Vu...
PyQt5数据库开发1 4.1... 文章目录 前言 步骤/方法 1 使用windows身份登录 2 启用混合登录模式 3 允许远程连接服...
Android studio ... 解决 Android studio 出现“The emulator process for AVD ...
Linux基础命令大全(上) ♥️作者:小刘在C站 ♥️个人主页:小刘主页 ♥️每天分享云计算网络运维...
再谈解决“因为文件包含病毒或潜... 前面出了一篇博文专门来解决“因为文件包含病毒或潜在的垃圾软件”的问题,其中第二种方法有...
南京邮电大学通达学院2023c... 题目展示 一.问题描述 实验题目1 定义一个学生类,其中包括如下内容: (1)私有数据成员 ①年龄 ...
PageObject 六大原则 PageObject六大原则: 1.封装服务的方法 2.不要暴露页面的细节 3.通过r...
【Linux网络编程】01:S... Socket多进程 OVERVIEWSocket多进程1.Server2.Client3.bug&...
数据结构刷题(二十五):122... 1.122. 买卖股票的最佳时机 II思路:贪心。把利润分解为每天为单位的维度,然后收...
浏览器事件循环 事件循环 浏览器的进程模型 何为进程? 程序运行需要有它自己专属的内存空间࿰...
8个免费图片/照片压缩工具帮您... 继续查看一些最好的图像压缩工具,以提升用户体验和存储空间以及网站使用支持。 无数图像压...
计算机二级Python备考(2... 目录  一、选择题 1.在Python语言中: 2.知识点 二、基本操作题 1. j...
端电压 相电压 线电压 记得刚接触矢量控制的时候,拿到板子,就赶紧去测各种波形,结...
如何使用Python检测和识别... 车牌检测与识别技术用途广泛,可以用于道路系统、无票停车场、车辆门禁等。这项技术结合了计...
带环链表详解 目录 一、什么是环形链表 二、判断是否为环形链表 2.1 具体题目 2.2 具体思路 2.3 思路的...
【C语言进阶:刨根究底字符串函... 本节重点内容: 深入理解strcpy函数的使用学会strcpy函数的模拟实现⚡strc...
Django web开发(一)... 文章目录前端开发1.快速开发网站2.标签2.1 编码2.2 title2.3 标题2.4 div和s...