JSP基础知识第四篇

JSP基础知识第四篇

        本次博文介绍了文件的上传与下载、Java上对Excel的处理、Servlet监听器、RBAC权限模型的简单介绍。大部分为示例操作,仅供参考,如有错误和不周请指出。

文件的上传与下载

   文件处理的包

commons-io.jar

        封装了常用的 IO 的相关操作,提供了 IOUtils 工具类供开发人员使用 commons-fileupload.jar 文件上传的处理包,因为文件上传也会涉及到 IO 操作,因此,该包需要配合commons-io.jar 使用

一些常用的API...

FileItemFactory 文件项工厂,主要提供创建文件项的功能

DiskFileItemFactory 磁盘文件项工厂,主要用于解析上传文件时,创建对应的文件项 ServletFileUpload Servlet 文件上传对象,主要用于判断请求是否是文件上传请求,以及请 求中的内容解析。解析时需要使用文件项工厂来创建文件项

文件上传

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>文件上传和下载</title>
  </head>
  <body>
<%--  使用form表单进行文件上次的时候,必须设置enctype属性,
而且这个属性值必须是multipart/form-data--%>
  <form action="upload" method="post" enctype="multipart/form-data">
    <input type="text" name="name">
    <input type="file" name="uploadFile">
    <input type="submit" value="上传">
  </form>

  <input type="file" id="uploadFile">
  <input type="button" value="上传" id="uploadBtn">

  <a href="download?name=图片.png">图片.png</a>
  </body>
<script type="text/javascript" src="jQuery/jquery-3.6.0.js" ></script>
<script type="text/javascript">
  $(function (){
    $("#uploadBtn").click(function (){
      let formData = new FormData();//创建一个表单数据对象 主要用于模拟表单数据
      formData.append("file",$("#uploadFile")[0].files[0])
      $.ajax({
      //   文件上传
        url:'upload',
        type: 'post',
        data:formData,
        processData: false,//告诉jQuery不要处理数据
        contentType: false,//告诉jQuery不要设置内容的类型
        success: function (resp){
          alert(resp);
        },
        error: function (xhr){

        }
      });
    })
  })
</script>
</html>

UploadServlet
package com.noy.jsp.servlet;

import com.noy.jsp.excel.ExcelUtil;
import com.noy.jsp.excel.read_writer;
import com.noy.jsp.pojo.Student;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.List;

/**
 * 文件上传
 */
@WebServlet("/upload")
public class UploadServlet extends HttpServlet {

    private static final String SAVE_DIR = "D:\\Java Practise\\jsp06\\upload";
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //首先判断它到底是不是文件上传请求
        if (ServletFileUpload.isMultipartContent(req)){//如果是文件上传的请求
            DiskFileItemFactory factory = new DiskFileItemFactory();//创建一个磁盘文件项工厂
            factory.setDefaultCharset("UTF-8");//设置字符集编码
            factory.setRepository(new File("java.io.tmpdir"));//设置临时文件的存储位置
            factory.setSizeThreshold(4096*1024);//设置每一个文件项的最大的大小为4096KB,超过了这个大小就会使用临时文件
            ServletFileUpload upload = new ServletFileUpload(factory);//创建文件按上传的对象
            upload.setFileSizeMax(5*1024*1024);//设置每一个上传文件的最大大小为5M
            upload.setSizeMax(50*1024*1024);//设置每次上传的所有文件的总大小最大为50M
            upload.setHeaderEncoding("UTF-8");
            try {
                List<FileItem> fileItems = upload.parseRequest(req);//开始 解析请求  得到文件项
                for(FileItem fileItem : fileItems){
                    if(fileItem.isFormField()){//判断是否是普通的表单字段,比如name:123
                        //打印参数名和参数值
                        System.out.println(fileItem.getFieldName() + "=>" + fileItem.getString());
                    }else {//说明是上传的文件,要保存到一个地方
                        File dir = new File(SAVE_DIR);
                        if(!dir.exists()) dir.mkdirs();
                        //创建保存文件
                        File saveFile = new File(dir,fileItem.getName());
                        InputStream is = fileItem.getInputStream();//获取上传文件的输入流
                        List<Student> students = read_writer.readExcel(is, Student.class);
                        OutputStream os = new FileOutputStream(saveFile);//获取上传文件的输出流
                        IOUtils.copy(is,os);// 将输入流中的信息拷贝至输出流 , 这就是文件保存
                        IOUtils.closeQuietly(is);//关闭流
                        IOUtils.closeQuietly(os);//关闭流
                    }
                }
                resp.setCharacterEncoding("UTF-8");
                resp.setContentType("text/html;charset=UTF-8");
                resp.getWriter().print("上传成功");
            } catch (FileUploadException e) {
                e.printStackTrace();//打印异常原因
                resp.setCharacterEncoding("UTF-8");
                resp.setContentType("text/html;charset=UTF-8");
                resp.getWriter().print("上传失败");
            }
        }else {//抛出运行时异常
            throw new RuntimeException("请求头中未发现multipart/form-data");
        }
    }
}

文件下载

DownloadServlet
package com.noy.jsp.servlet;

import com.noy.jsp.excel.read_writer;
import com.noy.jsp.pojo.Student;
import org.apache.commons.io.IOUtils;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;


@WebServlet("/download")
public class DownloadServlet extends HttpServlet {

    private static final String DOWNLOAD_DIR = "D:\\";

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String name = req.getParameter("name");//获取下载的文件的名字
        File file = new File(DOWNLOAD_DIR,name);
        if (file.exists()) {
            byte[] data = name.getBytes(StandardCharsets.UTF_8);//获取下载的文件名的字节数据
            //转换编码的格式:重新构建字符串,因为浏览器默认支持的编码是IS0_8859_1,因此,要转换为这种编码下中文才能正常显示
            name = new String(data, StandardCharsets.ISO_8859_1);
            //设置内容处理方案:以附件的形式处理
            resp.setHeader("Content-Disposition","attachment;filename="+name);
            InputStream is = new FileInputStream(file);
            OutputStream os = resp.getOutputStream();//获取响应的输出流,这个流就会将信息输出到页面,从而形成下载的效果
            List<Student> students = new ArrayList<>();//这个是假数据,实际数据应该从数据库中查询出来
            read_writer.writerExcel(os, Student.class, "学生信息表", students);
            IOUtils.copy(is, os);//传输信息
            IOUtils.closeQuietly(is);//关闭这两个流
            IOUtils.closeQuietly(os);
        }else {
            resp.setCharacterEncoding("UTF-8");
            resp.setContentType("text/html;charset=UTF-8");
            PrintWriter writer = resp.getWriter();
            writer.print("下载的文件不存在");
            writer.flush();
            writer.close();
        }
    }
}

Excel的处理

 EasyExcel

        EasyExcel 是阿里巴巴开源的一个 Excel 处理框架,以使用简单、节省内存著称。

        EasyExcel 能大大减少占用内存的主要原因是在解析 Excel 时没有将文件数据一次性全部加载到内存中,而是从磁盘上一 行行读取数据,逐个解析。

EasyExcel 生成 Excel

        

package com.noy.jsp.pojo;

import com.alibaba.excel.annotation.ExcelProperty;

public class Student {
    //思考一个问题  这个name属性与excel中的哪一个表头对应
    @ExcelProperty("姓名")
    private String name;

    @ExcelProperty("性别")
    //ExcelProperty表示在生成的Excel表格中的属性,value表示该属性对应的表头名称, index表示该属性所处的列的位置
    private String gender;

    @ExcelProperty("年龄")
    private int age;

    @ExcelProperty("班级")
    private String className;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                ", age=" + age +
                ", className='" + className + '\'' +
                '}';
    }
}

封装一个工具类

        这个工具类是笔者以前写的,所以大部分的注释是复杂但可参考的,实际编写可以忽略

package com.noy.jsp.excel;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.noy.jsp.pojo.Student;
import java.io.*;
import java.util.ArrayList;
import java.util.List;

public class ExcelUtil {

    private static final int MAX_COUNT_PER_SHEET = 5000;//每个sheet中写的最大数量

    public static void main(String[] args) throws IOException {

        List<Student> students = new ArrayList<>();
        for (int i = 0; i < 30000; i++) {
            Student stu = new Student();
            stu.setName("作者的名字" + i);
            stu.setGender(i % 2 == 0 ? "男" : "女");
            if (stu.getGender() == "女") stu.setName("作者女朋友的名字" + i);
            stu.setAge(10 + i);
            stu.setClassName("计算机科学");
            if (stu.getName().startsWith("作者女朋友的名字")) stu.setClassName("汉语言文学");
            students.add(stu);
        }
        OutputStream os = new FileOutputStream("C:\\Users\\dril\\OneDrive\\Desktop\\学生信息表2.xlsx");
        exportExcel(os, Student.class, "学生信息表", students);
//    InputStream is = new FileInputStream("C:\\Users\\dril\\OneDrive\\Desktop\\学生信息表(创建).xlsx");
//    importExcel(is,Student.class);
//        List<Student> students = new ArrayList<>();
//        for (int i = 0; i < 100; i++) {
//            Student stu = new Student();
//            stu.setName("作者的名字" + i);
//            stu.setAge(10+i);
//            stu.setGender(i % 2 == 0 ? "男":"女");
//            stu.setClassName("计算机科学");
//            students.add(stu);
//        }
        //如何将这个集合中的数据写入一个excel中?
        //excel必须要有名称,有存放的位置
//        String excelPath = "C:\\Users\\dril\\OneDrive\\Desktop\\学生信息表(创建).xlsx";
//        writeExcel(excelPath, Student.class,"学生信息表", students);
//        String excelPath = "C:\\Users\\dril\\OneDrive\\Desktop\\学生信息表(创建).xlsx";
//        List<Student> students = readExcel(excelPath,"学生信息表", Student.class);
//
//        System.out.println(students.size());
    }

    public static<T> List<T> importExcel(InputStream is, Class<T> clazz) {
        List<T> dataList = new ArrayList<>();
        ReadListener<T> listener = new ReadListener<T>() {//实现一个监听器
            @Override
            public void invoke(T t, AnalysisContext analysisContext) {
                System.out.println("读取了一行数据" + t);
                dataList.add(t);
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext analysisContext) {
                System.out.println("excel读取一个sheet完毕");
            }
        };
        EasyExcel.read(is, clazz, listener).doReadAll();//全读了,包括所有sheet
        return dataList;
    }


    public static<T> void exportExcel(OutputStream os, Class<T> clazz,String sheetName,List<T> dataList) throws IOException {
        //EasyExcel写excel的时候需要指定excel存放的位置
        // 还需要指定写的时候excel的表头的对应关系
        ExcelWriter writer = EasyExcel.write(os, clazz).build();
        int size = dataList.size();//数据总条数
        //deepseek的代码写的真是美如画
        int sheetCount = (size + MAX_COUNT_PER_SHEET - 1) / MAX_COUNT_PER_SHEET;//计算sheet的数量

        for (int i = 0; i < sheetCount; i++) {
            WriteSheet sheet = new WriteSheet();
            sheet.setSheetNo(i);
            sheet.setSheetName(sheetName + (i+1));
            // 计算当前sheet的数据范围
            int start = i * MAX_COUNT_PER_SHEET;
            int end = Math.min(start + MAX_COUNT_PER_SHEET, size); // 防止越界

            List<T> sheetData = dataList.subList(start, end);
            writer.write(sheetData,sheet);
        }
        writer.finish();
        os.close();
    }



    public static<T> List<T> readExcel(String excelPath,String sheetName, Class<T> clazz) {
        List<T> dataList = new ArrayList<>();
        ReadListener<T> listener = new ReadListener<T>() {//实现一个监听器
            @Override
            public void invoke(T t, AnalysisContext analysisContext) {
                //System.out.println("读取了一行数据"+t);
                dataList.add(t);
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext analysisContext) {
                //System.out.println("excel读取一个sheet完毕");
            }
        };
        //EasyExcel读取excel时需要指定读取的excel的位置,还需要指定读取的类型,
        //因为这个类型中就指定了excel表头与类定义的属性的映射关系,
        //还需要指定新的监听器,因为EasyExcel是按行读取的,
        //这个监听器就是感知一行的读取过程
        EasyExcel.read(excelPath, clazz,listener)
                .sheet(sheetName)//指定读取的sheet的名称
                .doRead();//执行读
        return dataList;
    }

    public static<T> void writeExcel(String excelPath, Class<T> clazz,String sheetName,List<T> dataList) {
        //EasyExcel写excel时必须指定excel存放的位置,
        // 还需要指定写的时候excel的表头的对应关系
        EasyExcel.write(excelPath,clazz)
                .sheet(sheetName) //指定写的时候sheet的名称
                .doWrite(dataList);//执行写数据操作
    }
}

        不管是文件上传还是EasyExcel都很适合初学者照着代码练习,有机会多多试试吧

还有几点要注意:

文件上传和下载:

文件上传: 上传的时候必须要带有multipart/form-data

文件上传有两种方式:form表单上传,ajax上传=>form表单模拟

文件下载: 下载文件的时候,一定要注意下载文件的文件名字符集处理

一定要记得带jar包:commons-io.jar    commons-fileupload.jar

Excel的导入导出:

导入的时候也需要注意导入的数据量,如果数据量过大,需要分批处理,每次处理的数量根据实际开发场景去定义.

导出的时候也需要注意导出的数据量,如果数据量过大,需要分批处理,每一批数据放在一个sheet中

Servlet监听器

什么是监听器?

        监听器顾名思义就是监听某种事件的发生,一旦监听的事件触发,那么监听器就将开始执行。例如:在 上课的时候,老师会观察每一位学生的听课情况,如果有学生上课打瞌睡,那么老师就会提醒他。这个 场景中,老师就是一个监听器,监听的是学生是否打瞌睡,一旦学生出现打瞌睡的情况,监听器就开始 执行(老师提醒学生)

ServletContextListener(Servlet上下文监听器)

        该监听器主要监听的是Servlet上下文的初始化和销毁。一旦Servlet上下文初始化或者销毁,ServletContextListener 就执行响应的操作。

具体实现:

public interface ServletContextListener extends EventListener {
    // Servlet上下文初始化时调用
    default void contextInitialized(ServletContextEvent sce) {
    }
    // Servlet上下文销毁时调用
    default void contextDestroyed(ServletContextEvent sce) {
    }
}

示例: 使用Servlet上下文监听器

在resources目录下创建jdbc.properties  -------  连接你的数据库

jdbc.url=jdbc:mysql://localhost:3306/jsp?serverTimezone=UTF-8
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.username=root
jdbc.password=root
 

在web.xml中配置上下文参数

<context-param>
    <param-name>jdbcConfig</param-name>
    <param-value>/jdbc.properties</param-value>
</context-param>

创建一个上下文监听器

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

@WebListener
public class ApplicationContextListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("Servlet Context初始化");
        ServletContext context = sce.getServletContext();
        String config = context.getInitParameter("jdbcConfig");
        if (config != null && !"".equals(config)) {
            InputStream is = this.getClass().getResourceAsStream(config);
            Properties props = new Properties();
            try {
                props.load(is);
                System.out.println(props);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("Servlet Context销毁");
    }
}
 

接下来可以测试了

启动服务器,查看控制台信息

关闭服务器,查看控制台信息

于是发现:Servelt上下文监听器可以读取到上下文参数,这些参数可以用来配置开发中所需要的环 境

DruidDataSource(德鲁伊数据源

        DruidDataSource是阿里巴巴开发的一款高性能的数据源。利用Servlet上下文监听器建立工程中需要的数据源

在开始之前,我们需要引入DruidDataSource的依赖包,然后创建一个JbdcUtil 工具类。

package com.noy.jsp.handler;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * 对查询的结果集进行处理的接口,具体如何处理需要用户来实现
 * @param <T>
 */
public interface ResultHandler<T>{

    T handle(ResultSet rs) throws SQLException;

}

对单个结果的处理

package com.noy.jsp.handler;

import org.apache.commons.beanutils.BeanUtils;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

public class SingleResultHandler<T> implements ResultHandler<T> {

    private Class<T> clazz;

    public SingleResultHandler(Class<T> clazz) {
        this.clazz = clazz;
    }

    @Override
    public T handle(ResultSet rs) throws SQLException {
        int totalCount = 0;

        while (rs.next()) {
            totalCount++;
            if (totalCount > 1) throw new RuntimeException("查询结果存在多条数据:"+totalCount);
            try {
                if (clazz.isPrimitive() || Integer.class == clazz || Long.class == clazz) {
                    return rs.getObject(1,clazz);
                }
                T t = clazz.newInstance();
                Map<String,Object> values = new HashMap<>();
                ResultSetMetaData rsmd = rs.getMetaData();
                int columnCount = rsmd.getColumnCount();
                for (int i = 1; i < columnCount; i++) {
                    String label = rsmd.getColumnLabel(i);
                    Object value = rs.getObject(label);
                    values.put(label, value);
                }
                //使用工具类对我们的对象的属性值进行注入
                BeanUtils.populate(t, values);
                return t;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

对多个结果的处理

package com.noy.jsp.handler;

import org.apache.commons.beanutils.BeanUtils;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.*;

public class MultiResultHandler<T> implements ResultHandler<List<T>> {

    private Class<T> clazz;

    public MultiResultHandler(Class<T> clazz) {
        this.clazz = clazz;
    }

    @Override
    public List<T> handle(ResultSet rs) throws SQLException {
        List<T> dataList = new ArrayList<>();
        while (rs.next()) {
            try {
                T t = clazz.newInstance();//newInstance()方法创建目标对象的实例
                Map<String,Object> values = new HashMap<>();
                ResultSetMetaData rsmd = rs.getMetaData();//获取 ResultSet 的元数据,用于获取列信息。
                int columnCount = rsmd.getColumnCount();//获取结果集中列的数量
                for (int i = 1; i <= columnCount; i++) {
                    String label = rsmd.getColumnLabel(i);//获取列的别名(标签)
                    Object value = rs.getObject(label);//获取列的值
                    values.put(label, value);
                }
                //使用工具类对我们的对象的属性值进行注入
                BeanUtils.populate(t, values);
                dataList.add(t);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return dataList;
    }
}

package com.noy.jsp.servlet;

import com.noy.jsp.service.StudentService;
import com.noy.jsp.service.impl.StudentServiceImpl;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/update")
public class UpdateServlet extends HttpServlet {

    private StudentService studentService = new StudentServiceImpl();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String id = req.getParameter("id");
        String name = req.getParameter("name");
        String gender = req.getParameter("gender");
        String age = req.getParameter("age");

        int result = studentService.updateStudent(id,name,gender,age);
        PrintWriter writer = resp.getWriter();
        writer.print(result);
        writer.flush();
        writer.close();
    }
}

package com.noy.jsp.listener;

import com.noy.jsp.jdbc.jdbcUtil;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

@WebListener//标明这是一个监听器
public class ApplicationContextListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {

        System.out.println("Servlet上下文初始化");
        //获取servlet上下文
        ServletContext context = sce.getServletContext();
        String jdbcConfig = context.getInitParameter("jdbcConfig");
        InputStream is = this.getClass().getResourceAsStream(jdbcConfig);
        Properties props = new Properties();
        try {
            props.load(is);
            System.out.println(props);
            //对数据源进行初始化操作
            jdbcUtil.initDataSource(props);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("Servlet上下文销毁");
        jdbcUtil.destroyDataSource();
    }

}

接下来可以修改jdbc.properties配置 编写访问接口,启动测试了。

RBAC权限模型

        Role-Based Access Control,表示基于角色的访问控制。

在RBAC中,有三个最常用的术语:

                                                        用户:系统资源的操作者

                                                        角色:具有一类相同操作权限的用户的总称

                                                        权限:能够访问资源的资格

资源:服务器上的一切数据都是资源,比如静态文件,查询的动态数据等。

RBAC怎么与用户建立联系?

        服务器感知用户是通过session来感知的,因此,RBAC的实现需要与 session配合。前提是用户需要登录,登录后将用户信息存储在session中,这样才能在session中获取用户的信息。

RBAC结构图

一个用户可能充当多个角色,而一个角色可能有不同的权限

RBAC的练习和Servlet监听器的练习都需要联系Navicat的数据库一起操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值