2-40 分页、上传和下载

EmpProject整合修改页面为EL+JSTL模式

1.将Standard.jar 和 jstl.jar包导入到工程中

2.修改showAllEmp.jsp页面

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page import="com.qf.emp.entity.Emp" %>
<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>查询所有员页面</title>
</head>
<body>
       <table border='1'>
            <tr>
                   <td>编号</td>
                   <td>姓名</td>
                   <td>工资</td>
                   <td>年龄</td>
                   <td colspan='2'>操作</td>
            </tr>
           <c:forEach var="emp" items="${emps}">
                    <tr>
                        <td>${emp.id}</td>
                        <td>${emp.name}</td>
                        <td>${emp.salary}</td>
                        <td>${emp.age}</td>

               <td><a href="<c:url context='${pageContext.request.contextPath}' value='/manager/safe/removeEmpController?id=${emp.id}'></c:url>">删除</a></td>
               <td><a href="<c:url context='${pageContext.request.contextPath}' value='/manager/safe/showEmpController?id=${emp.id}'></c:url>">修改</a></td>
                    </tr>
           </c:forEach>
       </table>


</body>
</html>

3.修改showUpdateEmpInfo.jsp页面

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page import="com.qf.emp.entity.Emp" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>修改员工信息页面</title>
</head>
<body>

         <form  action="<c:url context='${pageContext.request.contextPath}' value='/manager/safe/updateEmpController'></c:url>" method='post'>
               编号:<input type='text' name='id' value="${emp.id}" readonly/><br/>
               姓名:<input type='text' name='name' value="${emp.name}" /><br/>
               工资:<input type='text' name='salary' value="${emp.salary}" /><br/>
               年龄:<input type='text' name='age' value="${emp.age}" /><br/>
               <input type='submit' value='修改'/><br/>
       </form>
   
</body>
</html>

分页

概念

分页是Web应用应用程序非常总要的一个技术,数据库中数据可能是成千上万条,不可能把这么多数据一次性显示在浏览器页面上,一般会根据行数据在页面上所占的空间设置每页显示的若干行,例如:jd商品展示页面中,一页就展示60行数据

分页的实现思路

在mysql中如果需要进行限制显示数据条数 limit ,在web中会使用数据库中 limit 关键字进行分页操作

#抛开分页而言
select * from tableName limit n;  限制显示n条数
select * from tableName limit m,n ;m是显示行的起始位置  n 显示的个数
例如:
select * from tableName limit 0,20; 一次性显示1~20条的数据 即显示20条数据

#web分页
既然在mysql中提供了 limit 这个关键字,前端页面展示数据也是需要进行查询数据库完成,那么就可以使用 limit 关键字以达到显示前端页面展示数据条数
#例如: 展示20条数据
select * from tableName limit 20;
假如:每一页都展示20条数据,那么上面这个写法就不是很合理了,所以我们就利用 limit m,n 这种形式完成分页查询显示数据的效果

#问题:在于如何计算分页中展示数据的条数,并按照翻页来展示数据
解决:
select * from tableName limit 0,20  第一个参数代表第几条开始展示
#问题:前端页面传递给我们的式页数不是条数,所以如果计算当前也从什么位置开始展示数据?
  ps: 所有网页中数据的展示都是从 第1页也开始,所以m这个值就需要计算了
     #公式: m = 第一个参数(页数)-1(固定值)* 页面展示总条数(n的值) 
     #推算:3页面 每页20条数据 select * from tableName limit m,n 语法
     第一页:先通过公式计算m值
     select * from tableName limit (1-1)*20,20 -->
     select * from tableName limit  0,20  ---> 第一页展示【1-20条数据】
     第二页:先通过公式计算m值
     select * from tableName limit (2-1)*20,20 -->
     select * from tableName limit  20,20  ---> 第二页展示【21-40条数据】
     第三页:先通过公式计算m值
     select * from tableName limit (3-1)*20,20 -->
     select * from tableName limit  40,20  ---> 第三页展示【41-60条数据】
    
    limit 关键字中 m的值 = (pageIndex-1)*pageSize 即 limit关键字做分页数据查询时需要写成(计算)
    select * from tableName limit (pageIndex-1)*pageSize,pageSize;    
     #ps:计算分页公式是固定的,要修改页面展示条数,修改pageSize即可

借助EmpProject工程完成分页操作

1.在EmpProject工程中测试分页显示

1.1先修改dao包下EmpDao添加分页查询方法

public List<Emp> selectAll(int pageIndex,int pageSize);

1.2实现EmpDao中分页方法【EmpDaoImpl】

 @Override
    public List<Emp> selectAll(int pageIndex, int pageSize) {
        try {
            List<Emp> emps = queryRunner.query(DbUtils.getConnection(),
                    "select * from emp limit ?,?",
                    new BeanListHandler<Emp>(Emp.class),
                    pageIndex, pageSize);
            return emps;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return  null;
    }

1.3测试了结果

package com.qf.emp.dao.impl;

import com.qf.emp.dao.EmpDao;
import com.qf.emp.entity.Emp;

import java.util.List;

import static org.junit.Assert.*;

public class EmpDaoImplTest {
    //测试分页实现
    @org.junit.Test
    public void selectAll() {
        EmpDao ed = new EmpDaoImpl();
        int pageIndex = 2;//页码
        int pageSize = 5;//页面显示条数
        List<Emp> emps = ed.selectAll((pageIndex - 1) * pageSize, pageSize);
        for(Emp emp : emps){
            System.out.println(emp);
        }
    }
}

思考:pageIndex页面应该是前端页面传递给我们,而展示页面数据多少即pageSize应该是为我们设置好的

​ 如果超出查询范围,一页展示4数据数据共16条数据,已经到达第4也用户不小心点击下一页,如何处理?

2.分页在代码中核心处理

2.1对分页逻辑进行一个统一封装

会为所有操作到分页数据提供实例类【包含 页码,页大小,总条数,总页数,起始行】

提供一个分页的实体类Page,在entity包中

package com.qf.emp.entity;

/**
 * 分页实体类,即对分页中使用数据进行统一封装
 */
public class Page {
     private Integer pageIndex;//页码
     private Integer pageSize;//页大小即显示多少行数据
     private Integer totalCounts;//数据的总行数
     private Integer totalPages;//总页数
     private Integer startRows;//起始行

    //提供一个公有方法,外界可以进行操作,对pageIndex进行赋值
      public Page(Integer pageIndex){
          this(pageIndex,5);
      }
    //提供一个私有化构造方法,这个方法进行计算使用【分页计算】
    private  Page(Integer pageIndex , Integer pageSize){
        this.pageIndex = pageIndex;
        this.pageSize = pageSize;
        //设置起始行
        this.startRows = (pageIndex-1)*pageSize;
    }

    //在实际开发中page类中该提供哪些get和set方法由开发者决定
    public Integer getPageIndex() {
        return pageIndex;
    }

    public void setPageIndex(Integer pageIndex) {
        this.pageIndex = pageIndex;
    }

    public Integer getPageSize() {
        return pageSize;
    }

    public void setPageSize(Integer pageSize) {
        this.pageSize = pageSize;
    }

    public Integer getTotalCounts() {
        return totalCounts;
    }

    public void setTotalCounts(Integer totalCounts) {
        this.totalCounts = totalCounts;
        //总页数计算
        this.setTotalPages(totalCounts%pageSize == 0
                ?totalCounts/pageSize:totalCounts/pageSize+1);

    }

    public Integer getTotalPages() {
        return totalPages;
    }

    public void setTotalPages(Integer totalPages) {
        this.totalPages = totalPages;
    }

    public Integer getStartRows() {
        return startRows;
    }

    public void setStartRows(Integer startRows) {
        this.startRows = startRows;
    }
}

2.2修改dao包中EmpDao中的方法并添加新方法

 //修改分页方法
    public List<Emp> selectAll(Page page);
    //查询数据总行数
    public long selectCount();

2.3实现EmpDao包中新添加的方法

package com.qf.emp.dao.impl;

import com.qf.emp.dao.EmpDao;
import com.qf.emp.entity.Emp;

import com.qf.emp.entity.Page;
import com.qf.emp.utils.DbUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;

import java.sql.SQLException;
import java.util.List;

public class EmpDaoImpl implements EmpDao {

    private QueryRunner queryRunner = new QueryRunner();
    @Override
    public List<Emp> selectAll() {
        try {
            List<Emp> emps = queryRunner.query(DbUtils.getConnection(), "select * from emp", new BeanListHandler<Emp>(Emp.class));
            return  emps;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public int delete(int id) {
        try {
            int update = queryRunner.update(DbUtils.getConnection(), "delete from emp where id = ?", id);
            return update;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return 0;
    }

    @Override
    public int update(Emp emp) {
        try {
            int update = queryRunner.update(
                    DbUtils.getConnection()
                    , "update emp set name = ?,salary=?,age = ? where id = ?"
                    , emp.getName(), emp.getSalary(), emp.getAge(), emp.getId());
            return update;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return 0;
    }

    @Override
    public Emp select(int id) {
        try {
            Emp emp = queryRunner.query(DbUtils.getConnection(),
                    "select * from emp where id = ?", new BeanHandler<Emp>(Emp.class), id);
            return  emp;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return  null;
    }

    @Override
    public List<Emp> selectAll(Page page) {
        try {
            List<Emp> emps = queryRunner.query(DbUtils.getConnection(),
                    "select * from emp limit ?,?",
                    new BeanListHandler<Emp>(Emp.class),
                    page.getStartRows(),page.getPageSize());
            return emps;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return  null;
    }

    @Override
    public long selectCount() {
        try {
            return queryRunner.query(DbUtils.getConnection(),"select count(*) from emp",new ScalarHandler<>());
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return 0;
    }
}


2.4 添加业务实现对应Dao包中方法,在service包中对EmpService添加分页业务

public List<Emp> showAllEmp(Page page);

2.5 实现具体业务逻辑

 @Override
    public List<Emp> showAllEmp(Page page) {
        List<Emp> emps = null;
        try{
            DbUtils.begin();
            //获取总行数
            long count = empDao.selectCount();
            page.setTotalCounts((int)count);
            //根据传递的page查询对应数据
            emps = empDao.selectAll(page);
            DbUtils.commit();
        }catch(Exception e){
            DbUtils.rollback();
            e.printStackTrace();
        }
        return emps;
    }

2.6 修改controller包中showAllEmpController

package com.qf.emp.controller;

import com.alibaba.druid.sql.dialect.postgresql.ast.expr.PGMacAddrExpr;
import com.qf.emp.entity.Emp;
import com.qf.emp.entity.Page;
import com.qf.emp.service.EmpService;
import com.qf.emp.service.impl.EmpServiceImpl;

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.util.List;

@WebServlet(name = "ShowAllEmpController",value="/manager/safe/showAllEmpController")
public class ShowAllEmpController extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request,response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.需要获取URL中参数
        String pageIndex = request.getParameter("pageIndex");
        //2.如果是第一次访问
        if(pageIndex == null){
            pageIndex = "1";//必然展示第一页
        }
        //3.创建page对象进行分页查询
        Page page = new Page(Integer.valueOf(pageIndex));
        EmpService empService = new EmpServiceImpl();
        List<Emp> emps = empService.showAllEmp(page);
        
        //4.将查询结果和page对象设置到作用域中
        request.setAttribute("emps",emps);
        request.setAttribute("page",page);
        //通过请求转换,将数据转发另外一个Servlet进行处理[页面Servlet]
        request.getRequestDispatcher("/manager/safe/showAllEmp.jsp").forward(request,response);
    }
}


2.7最后修改showAllEmp.jsp页面

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page import="com.qf.emp.entity.Emp" %>
<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>查询所有员页面</title>
</head>
<body>
       <table border='1'>
            <tr>
                   <td>编号</td>
                   <td>姓名</td>
                   <td>工资</td>
                   <td>年龄</td>
                   <td colspan='2'>操作</td>
            </tr>

           <c:forEach var="emp" items="${emps}">
                    <tr>
                        <td>${emp.id}</td>
                        <td>${emp.name}</td>
                        <td>${emp.salary}</td>
                        <td>${emp.age}</td>

               <td><a href="<c:url context='${pageContext.request.contextPath}' value='/manager/safe/removeEmpController?id=${emp.id}'></c:url>">删除</a></td>
               <td><a href="<c:url context='${pageContext.request.contextPath}' value='/manager/safe/showEmpController?id=${emp.id}'></c:url>">修改</a></td>
                    </tr>
           </c:forEach>
           <tr>
               <td colspan="6">
                   <a href="<c:url context='${pageContext.request.contextPath}'
                              value='/manager/safe/showAllEmpController?pageIndex=1'></c:url>">首页</a>
<%--                   什么情况之下才可以出现山上一页--%>
                   <c:if test="${page.pageIndex > 1}">
                       <a href="<c:url context='${pageContext.request.contextPath}'
                              value='/manager/safe/showAllEmpController?pageIndex=${page.pageIndex-1}'></c:url>">上一页</a>
                   </c:if>
<%--                   什么情况下出现下一页--%>
                   <c:if test="${page.pageIndex < page.totalPages}">
                       <a href="<c:url context='${pageContext.request.contextPath}'
                              value='/manager/safe/showAllEmpController?pageIndex=${page.pageIndex+1}'></c:url>">下一页</a>
                   </c:if>
                   <a href="<c:url context='${pageContext.request.contextPath}'
                              value='/manager/safe/showAllEmpController?pageIndex=${page.totalPages}'></c:url>">尾页</a>

               </td>
           </tr>
       </table>


</body>
</html>


上传文件和下载文件

上传和下载的场景

在项目中,文件上传和下载都是常见功能,很多程序或者软件都会经常使用文件上传和下载

场景:

1.QQ或微信的头像【上传】

2.邮箱中有附件上传和下载

文件上传

逻辑:当用户在前端页面文件上传之后,用户上传的文件数据会提交给服务器,实现保存

文件上传实现步骤
1.提交方式

如果是文件上传必须使用form标签,而却method必须是post,因为post请求无数据限制

<form method="post"></form>

2.提交数据格式

表单中有一个属性 enctype 设置表单提交数据类型

如果是上传文件,这个属性必须是设置为【multipart/form-data】

它的含义是“以多段的形式进行拼接提交,以二进制流的方式处理表单种数据,会把指定内容封装进请求参数中”

<form enctype="multipart/form-data" method="post"></form>

3.提供组件

在input中可以使用file属性进行用户上文件组件提供

<form enctype="multipart/form-data" method="post">
	上传用户:<input type="text" name="username"><br/>
    上传文件:<input type="file" name="file"><br/>
    <input type = "submit" value = "提交">
</form>

具体实现文件上传操作

1.创建jsp页面,实现文件上传操作

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>文件上传页面</title>
</head>
<body>
  <form action="${pageContext.request.contextPath}/upload" enctype="multipart/form-data" method="post">
      上传用户:<input type="text" name="username"><br/>
      上传文件:<input type="file" name="file"><br/>
      <input type = "submit" value = "提交">
  </form>
</body>
</html>


2.实现文件接收Servlet

package com.qfed.UploadAndDownload.Upload;

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;

@WebServlet(name = "UploadServlet",value = "/upload")
public class UploadServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("文件上传过来了!!!");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doPost(request,response);
    }
}


在页面提交请求之后在请求头中多了一条数据

请添加图片描述

content-Type:表示提交的数据类型, multipart/form-data【表示提交的数据是以分段的形式进行拼接,并且以(二进制流)的形式发送给服务器】

boundary :表示每段数据都是以 分隔符进行分隔 ,这个分隔的产生是【虚线+字母和数字随机】

请添加图片描述

3.填充具体实现内容到uploadServlet中

在Servlet3.0及其以上版本的容器中进行服务器端文件上传的编程,是围绕着注解了类型MultipartConfig和javax.servlet.http.Part接口进行的,处理已上传文件的Servlet必须以@MultipartConfig注解进行

package com.qfed.UploadAndDownload.Upload;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.File;
import java.io.IOException;

@WebServlet(name = "UploadServlet",value = "/upload")
//maxFileSize 单个文件最大值【单个文件大小】
//maxRequestSize 一次请求多个文件 一共的大小是多少
@MultipartConfig(maxFileSize = 1024*1024*100,maxRequestSize = 1024*1024*200 )
public class UploadServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       // 设置防止乱码
         request.setCharacterEncoding("UTF-8");
         response.setContentType("text/html;charset=utf-8");
         //获取文件--->需要用过getPart方法
        Part file = request.getPart("file");//file就是上传文件对象
        //决定文件保存位置
        /*
        1.开发中我们会将上传的文件保存在服务器目录中即WEB-INF
        2.还可以保存在实体盘符目录中【绝对路径】
         */
        //getRealPath 文件上传保存的真是路径,会最终出现在out目录下的artifacts目录下的WEB-INF目录中
        String uploadPath = request.getServletContext().getRealPath("/WEB-INF/upload");
        //创建文件夹
        File f = new File(uploadPath);
        if(!f.exists()){//判断文件夹是否存在
            f.mkdirs(); //不存在直接创建
        }
        //上传文件不是空的,进行保存
        if(file != null){
            //getSubmittedFileName获取文件名
            file.write(uploadPath+File.separator+file.getSubmittedFileName());
        }
        response.getWriter().println("上传成功!"+file.getSubmittedFileName());

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doPost(request,response);
    }
}


文件上传注意的小细节

上述代码虽然可以成功上传文件到服务器指定目录中,但是文件上传功能有许多需要注意的小细节问题

1.安全问题

为了保证服务器安全,上传文件应该放在外界无法访问到目录下,习惯性将上传文件放置到WEB-INF目录下

request.getServletContext().getRealPath("/WEB-INF/upload");

2.文件覆盖

当上传重名文件的时候,为了防止文件覆盖的现象发生,要为上传文件生产一个唯一的文件名

ps:这个功能需要根据实际开发中决定。

package com.qfed.UploadAndDownload.Utils;

import java.util.UUID;

public class UploadUtils {
    private  UploadUtils(){}
    //这个名字的拼接方式是你决定的,将来下载的时候如何还原为原有名字那么就需要通过拼接决定
    //使用Java中提供唯一标识符,唯一标识符的生成需要一个类【UUID】
    public static String NewFileName(String fileName){
        return  UUID.randomUUID().toString().replace("-","")+"_"+fileName;
    }
    
}


修改uploadServlet中的代码

//上传文件不是空的,进行保存
        if(file != null){
            //保证不覆盖原有文件
            String oldName = file.getSubmittedFileName();
            String newName = UploadUtils.NewFileName(oldName);
            //getSubmittedFileName获取文件名
            file.write(uploadPath+File.separator+newName);
        }

3.散列存储

为了防止一个目录下出现太多文件,要使用hash算法生成二级、三级目录,散列存储山回传文件

 /**
     * 散列计算二、三级目录
     * @param basePath 原始真是路径【即一级目录的路径】
     * @param filename  文件的名字
     * @return 文件存储在服务器中目录的路径
     */
    public static String NewFilePath(String  basePath,String filename){
          //1.先文件名字的hashcode值
        int  hashCode = filename.hashCode();
        //2.使用hashcode值进行进行二级目录计算
        int path1 = hashCode & 15;
        //3.产生三级目录
        int path2 =  (hashCode >> 4) & 15;
        // 与一级目录进行片接形成新的二、三级目录
        String  dir = basePath+ File.separator+path1+File.separator+path2;
        File file = new File(dir);
        if(!file.exists()){
            file.mkdirs();
        }
        return  dir;

    }

请添加图片描述

修改uploadServlet类

 //上传文件不是空的,进行保存
        if(file != null){
            //保证不覆盖原有文件,getSubmittedFileName获取文件名
            String oldName = file.getSubmittedFileName();
            String newName = UploadUtils.NewFileName(oldName);
            //通过散列生成 二、三级目录
            String newPath = UploadUtils.NewFilePath(uploadPath, oldName);
            file.write(newPath+File.separator+newName);
        }

4.文件类型限制

要限制上传文件类型,在在收到文件名字的时候,判断后缀是否合法

修改uploadServlet类

//上传文件不是空的,进行保存
        if(file != null){
            //保证不覆盖原有文件,getSubmittedFileName获取文件名
            String oldName = file.getSubmittedFileName();
            //创建一个集合,集合中存储和着文件类型(后缀名字)
            List<String> fileNameList = new ArrayList<>();
            Collections.addAll(fileNameList,".jpg",".bmp",".png");
            String  subFileName = oldName.substring(oldName.lastIndexOf("."));
            if(!fileNameList.contains(subFileName)){
                response.getWriter().println(oldName+"不符合文件上传规则,上传失败!!");
                return;
            }
            String newName = UploadUtils.NewFileName(oldName);
            //通过散列生成 二、三级目录
            String newPath = UploadUtils.NewFilePath(uploadPath, oldName);
            file.write(newPath+File.separator+newName);
        }

多文件上传

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>多文件上传页面</title>
</head>
<body>
  <form action="${pageContext.request.contextPath}/upload2" enctype="multipart/form-data" method="post">
      上传用户:<input type="text" name="username"><br/>
      上传文件1:<input type="file" name="file1"><br/>
      上传文件2:<input type="file" name="file2"><br/>
      <input type = "submit" value = "提交">
  </form>
</body>
</html>


package com.qfed.UploadAndDownload.Upload;

import com.qfed.UploadAndDownload.Utils.UploadUtils;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

@WebServlet(name = "UploadServlet",value = "/upload2")
//maxFileSize 单个文件最大值【单个文件大小】
//maxRequestSize 一次请求多个文件 一共的大小是多少
@MultipartConfig(maxFileSize = 1024*1024*100,maxRequestSize = 1024*1024*200 )
public class MoreUploadServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       // 设置防止乱码
         request.setCharacterEncoding("UTF-8");
         response.setContentType("text/html;charset=utf-8");
        //getRealPath 文件上传保存的真是路径,会最终出现在out目录下的artifacts目录下的WEB-INF目录中
        String basePath = request.getServletContext().getRealPath("/WEB-INF/upload");
        File dir= new File(basePath);
        if(!dir.exists()){
            dir.mkdirs();
        }
        //获取多段数据的集合【获取表单中所有数据】
        Collection<Part> parts = request.getParts();
        if(parts != null){
            for(Part part : parts){
                //获取文件提交名字
                String fileName = part.getSubmittedFileName();
                if(fileName != null){ //文件
                    //为了防止某个上传文件操作没有提供数据
                    if(fileName.trim().equals("")){
                        continue;
                    }
                    //获取包含UUID的文件名字
                    String newFileName = UploadUtils.NewFileName(fileName);
                    //散列文件路径
                    String newFilePath = UploadUtils.NewFilePath(basePath, fileName);
                    //存储
                    part.write(newFilePath+File.separator+newFileName);
                    response.getWriter().println(fileName+"上传成功");

                }else{ //不是文件【普通表单项】
                    //获取input标签中的name名字
                    String name = part.getName();
                    String value = request.getParameter(name);
                    System.out.println(name+"------"+value);

                }
            }
        }


    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletExceptwe
        ion, IOException {
            doPost(request,response);
    }
}


文件下载

概念

我们将Web应用系统中的文件资源提供给用户进行下载,首先我们需要先提供一个页面列出上传文件目录下的所有文件,当用户点击文件下下载链接的时候进行下载

FileListController类显示所有文件列表

package com.qfed.UploadAndDownload.Download;

import com.qfed.UploadAndDownload.Utils.UploadUtils;

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.File;
import java.io.IOException;
import java.util.HashMap;

@WebServlet(name = "FileListController",value="/fileListController" )
public class FileListController extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request,response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //1.乱码
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=utf-8");
        //2.获取文件列表【key是uuid,value是原有文件的名字】
        HashMap<String,String> filemap = new HashMap<>();
        String savePath = request.getServletContext().getRealPath("WEB-INF/upload");
        //遍历【防止文件夹中还有文件夹】
        UploadUtils.getFileList(new File(savePath),filemap);
        //转发:
        request.setAttribute("map",filemap);
        request.getRequestDispatcher("/list.jsp").forward(request,response);


    }
}


在uploadUtils类中创建了一个方法遍历文件夹下的文件

 /**
     * 遍历文件及存文件值
     * @param file  存文件的路径
     * @param filemap 存储文件的集合
     */
    public static void getFileList(File file, HashMap<String, String> filemap) {
          //获取当前文件对象下所有内容【文件、文件夹】
        File[] files = file.listFiles();
        //如果数据不为空,证明有文件和文件夹
         if(files != null){
             //每次拿到的文件对象进行判断是【文件还是文件夹】
             for(File file1 :files){
                 if(file1.isDirectory()){
                     getFileList(file1,filemap);
                 }else{
                     //获取文件的名字
                     String filename = file1.getName();
                     //获取第一个_的下标位
                     int i = filename.indexOf("_");
                     //获取uuid和原来文件名字
                     String realName = filename.substring(i + 1);
                     filemap.put(filename,realName);
                 }
             }
         }

    }

提供下载页面list.jsp页面

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--
  Created by IntelliJ IDEA.
  User: jkmaster
  Date: 2020/10/19
  Time: 15:04
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>下载页面</title>
</head>
<body>
   <h2>下载文件列表</h2>
   <table>
       <tr>
           <th>文件名</th>
           <th>操作</th>
       </tr>
        <c:forEach items="${map}" var = "entry">
            <tr>
                <td>
                    ${entry.value}
                </td>
                <td>
                <a href="${pageContext.request.contextPath}/down?filename=${entry.key}">下载</a>
                </td>
            </tr>
        </c:forEach>
   </table>

</body>
</html>


DownServlet下载操作

ps:千万不要忘了流!!!!!!!

package com.qfed.UploadAndDownload.Download;

import com.qfed.UploadAndDownload.Utils.UploadUtils;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;

@WebServlet(name = "DownServlet",value="/down")
public class DownServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request,response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.乱码
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=utf-8");
        String savepath = request.getServletContext().getRealPath("WEB-INF/upload");
        System.out.println(savepath);
        //1.获取文件的名称
        String uuidFileName = request.getParameter("filename");
        //如果没有做分目录,下面这两个操作可以不用,做了就必须要用
        //1.才分UUID和原有文件的名字
        String fileName = uuidFileName.split("_")[1];
        //2.通过源文件的名字得到分散路径
        String downPath = UploadUtils.NewFilePath(savepath, fileName);
        System.out.println(downPath);

        //3.在回之前,通过响应头告客户端返回数据类型是什么【选做】
        response.setContentType(getServletContext().getMimeType(savepath+"/"+uuidFileName));
        //4.设置响应有,告诉浏览器如何处理流,(附件在下载)【核心】
        /*
         这个设置可以防止浏览器直接打开文件,而是使用附件下载的方式下载
         content-disposition 响应头,表示收到数据后如何处理
         attachment 表示附件,表示附件下载使用
         filename  表示文件的名字
         URLEncoder.encode  指定编码集防止中文乱码
         */
        response.setHeader("content-disposition",
                "attachment;filename="+ URLEncoder.encode(uuidFileName,"utf-8"));
        //使用流读取下载
        FileInputStream fis = new FileInputStream(downPath+"/"+uuidFileName);
        ServletOutputStream outputStream = response.getOutputStream();
        //下载流
        byte[] buf = new byte[1024*1024*100];
        int len = 0;
        while(((len = fis.read(buf))!=-1)){
            outputStream.write(buf,0,len);
        }
        outputStream.close();
        fis.close();


    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值