Day41

Day41

文件的上传

浏览器底层是通过输入流读取文件,通过输出流传输到服务器,服务器通过输入流读取数据,通过输出流将文件保存在本地。注意:浏览器的表单不许用post请求,get请求会将数据显示在地址栏里。

上传头像

场景:在一个注册页面上传头像。

register.jsp:

<%--
  Created by IntelliJ IDEA.
  User: Gu
  Date: 2024-06-13
  Time: 18:43
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        body {
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            font-family: Arial, sans-serif;
            background: #f0f0f0;
        }
    </style>
</head>
<body>


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

    <h2>欢迎来到注册页面</h2>
    <br/>
    账号:<input type="text" name="username" /><br />
    密码:<input type="password" name="password" /><br />
    姓名:<input type="text" name="name" /><br />
    年龄:<input type="text" name="age" /><br />
    性别:
    <input type="radio" name="sex" value="man" checked="checked"/>男
    <input type="radio" name="sex" value="woman"/>女
    <br />
    爱好:
    <input type="checkbox" name="hobbies" value="football" />足球
    <input type="checkbox" name="hobbies" value="basketball" />篮球
    <input type="checkbox" name="hobbies" value="shop" />购物
    <br />

    <input type="submit" value="注册" /><br/>
    <input type="file" name="photo"/><br/>
    <button type="button" οnclick="fun01()">返回</button>

</form>

<script type="text/javascript">
    function fun01(){
        window.location = "register.jsp";
    }
</script>

</body>
</html>

注意:1.使用post请求,设置enctype=“multipart/form-data”(以二进制流的形式上传数据,效率比较低,不设置的话以默认的纯文本数据上传。)。2.input type=“file”。

版本一:原生文件方式处理文件

待优化点:1.相同的文件名会覆盖文件;2.存储路径绝对,其他系统无法存储。

RegisterServlet:

package com.qf.servlet;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

@WebServlet("/RegisterServlet")
public class RegisterServlet extends HttpServlet {

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

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");

        //创建文件上传类对象的工厂对象
        DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
        //利用工厂对象创建文件上传类对象
        ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);

        try {
            //解析请求对象
            List<FileItem> list = servletFileUpload.parseRequest(request);
            //遍历获取的集合
            for(FileItem fileItem:list){
                //如果是文本数据
                if(fileItem.isFormField()){
                    String fieldName = fileItem.getFieldName();//属性名
                    String value = fileItem.getString("UTF-8");//值
                    System.out.println(fieldName + "--" + value);
                }else {//二进制数据
                    String name = fileItem.getName();//用户输入的文件的名
                    System.out.println(name);
                    InputStream inputStream = fileItem.getInputStream();
                    String path="F:\\text\\头像01.jpg";
                    FileOutputStream fileOutputStream = new FileOutputStream(path);
                    byte[] bs = new byte[1024];
                    int len;
                    while((len=inputStream.read(bs))!=-1){
                        fileOutputStream.write(bs);
                    }
                    inputStream.close();
                    fileOutputStream.close();
                }
            }
        } catch (FileUploadException e) {
            throw new RuntimeException(e);
        }

    }
}

注:不能使用request.getparameter()获取数据了,用request.getInputStream()。

版本二:使用到发布路径 + 文件名

待优化点:文件名重复仍会覆盖。

注意:1.使用IOUtil工具类2.编辑发布路径,将文件保存到项目的发布路径里,需要将IDEA里的路径改为服务器(Tomcat)里的webapp下自己创建的文件名里,如:\apache-tomcat-8.0.49\webapps\Day41_upload_war_exploded

public void method02(HttpServletRequest request,HttpServletResponse response) throws UnsupportedEncodingException {
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");

        //创建文件上传类工厂
        DiskFileItemFactory factory = new DiskFileItemFactory();
        //创建文件上传类
        ServletFileUpload upload = new ServletFileUpload(factory);

        try {
            //解析数据
            List<FileItem> list = upload.parseRequest(request);
            for(FileItem file:list){
                if(file.isFormField()){
                    //是文本数据
                    System.out.println(file.getFieldName()+"--"+file.getString("UTF-8"));
                }
                else {
                    //处理二进制数据
                    String path = this.getServletContext().getRealPath("upload");//获取系统的发布路径
                    //创建文件夹
                    File file1 = new File(path);
                    if(!file1.exists()){
                        file1.mkdirs();
                    }
                    //设置文件路径
                    String name = file.getName();
                    String outPath = path+File.separator+name;

                    //上传文件

                    InputStream in = file.getInputStream();
                    FileOutputStream out = new FileOutputStream(outPath);
                    IOUtils.copy(in,out);
                    in.close();
                    out.close();
                }
            }

        } catch (FileUploadException | IOException e) {
            throw new RuntimeException(e);
        }
    }

版本三:使用UUID解决文件名重复问题

缺点:改变了文件名

public void method03(HttpServletRequest request,HttpServletResponse response) throws UnsupportedEncodingException {
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");


        DiskFileItemFactory factory = new DiskFileItemFactory();
        ServletFileUpload upload = new ServletFileUpload(factory);
        try {
            List<FileItem> list = upload.parseRequest(request);
            for(FileItem fileItem:list){
                if (fileItem.isFormField()){
                    System.out.println(fileItem.getFieldName()+"--"+fileItem.getString("UTF-8"));
                }else {
                    //获取项目发布路径
                    String realPath = this.getServletContext().getRealPath("upload");
                    File file = new File(realPath);
                    if(!file.exists()){
                        file.mkdirs();
                    }
                    String name = fileItem.getName();
                    UUID uuid = UUID.randomUUID();
                    String fileName = uuid + name;

                    String filePath = realPath +File.separator+ fileName;

                    InputStream in = fileItem.getInputStream();
                    FileOutputStream out = new FileOutputStream(filePath);
                    IOUtils.copy(in,out);
                    in.close();
                    out.close();
                }
            }
        } catch (FileUploadException | IOException e) {
            throw new RuntimeException(e);
        }
    }

最终版:

思路:先自定义一个parseRequest()方法。通过上传文件工厂创建上传文件对象,再解析用户在注册页面填写的所有数据,其中对每一个数据项进行判别,如果是文本数据则直接用ConcurrentHashmap存储数据,其中需要注意的点为遇到多选的时候需要判断当前的键(属性名)是否已经存在于map中,如果存在就添加在后面。如果是二进制数据则需要先获取发布路径(利用File类创建文件夹),然后通过发布路径和文件名创造二进制数据下载的绝对路径,最后用map把属性名和绝对路径后面部分(自定义即可)存储起来。同时还需要创建一个用来存储输入输出流的类CopyIo,因为最终要将用户上传的文件写到发布路径里面,所以用请求创建输入流(建立客户端输入的渠道),并用绝对路径创建输出流(建立输出到发布路径的渠道),并将两个流封装到CopyIo中,以便直接使用。

接下来的思路和之前的一样,通过自定义的parseRequest()方法设置好ConcurrentHashmap和CopyIo对象。然后用自带的BeanUtils类的populate方法将数据封装到Student类中,利用这个类对象往数据库中查询,没有账号则可以注册,即先往数据库中添加数据,再用CopyIo对象中的输入输出流进行数据写入和发布输出;如果有账号则用请求存储错误信息,返回注册页面。注意:别忘了关闭资源(输入流、输出流)。

  public void parseRequest(HttpServletRequest request, Student student , CopyIO copyIO){
        ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
        DiskFileItemFactory factory = new DiskFileItemFactory();
        ServletFileUpload upload = new ServletFileUpload(factory);
        String username=null;

        try {
            List<FileItem> list = upload.parseRequest(request);
            for(FileItem fileItem:list){
                if(fileItem.isFormField()){
                    String fieldName = fileItem.getFieldName();
                    String value = URLEncoder.encode(fileItem.getString(),"UTF-8");
                    if("username".equals(fieldName)){
                        username=value;
                    }
                    String s = map.get(fieldName);
                    if(s==null){
                        //第一次添加
                        map.put(fieldName,value);
                    }
                    else{
                        //多选的情况
                        s = s+","+value;
                        map.put(fieldName,s);
                    }
                }else{

                    //二进制数据
                    String realPath = this.getServletContext().getRealPath("upload"+File.separator+"student"+File.separator+username);
                    File file = new File(realPath);
                    if(!file.exists()){
                        file.mkdirs();
                    }
                    String fileName = fileItem.getName();
                    String path = realPath + File.separator + fileName;
                    copyIO.setIn(fileItem.getInputStream());
                    copyIO.setOut(new FileOutputStream(path));
                    map.put(fileItem.getFieldName(),"upload"+File.separator+"student"+File.separator+username+File.separator+fileName);
                }
            }
            BeanUtils.populate(student,map);

        } catch (FileUploadException | IOException | InvocationTargetException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
public void method04(HttpServletRequest request,HttpServletResponse response) throws UnsupportedEncodingException {
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");

        Student student = new Student();
        CopyIO copyIO = new CopyIO();
        parseRequest(request,student,copyIO);
        System.out.println(student);
        try {
            Student sqlStu = DBUtil.commonQueryObj(Student.class, "select * from student where username=?", student.getUsername());
            System.out.println(sqlStu);
            if(sqlStu==null){

                //允许注册
                String sql = "insert into student(username,password,name,sex,age,hobbies,photo) values (?,?,?,?,?,?,?)";
                DBUtil.commonInsert(sql,student.getUsername(),student.getPassword(),student.getName(),student.getSex(),student.getAge(),student.getHobbies(),student.getPhoto());

                //上传头像
                InputStream in = copyIO.getIn();
                OutputStream out = copyIO.getOut();
                int len;
                byte[] bs = new byte[1024];
                while((len=in.read(bs))!=-1){
                    out.write(bs);
                }
                in.close();
                out.close();
            }else{
                //不允许注册
//                copyIO.getIn().close();
//                copyIO.getOut().close();
                request.setAttribute("msg","账号已存在,注册失败");
                request.getRequestDispatcher("register.jsp").forward(request,response);
            }
        } catch (SQLException | InstantiationException | IllegalAccessException | IOException | ServletException e) {
            throw new RuntimeException(e);
        }


    }
  

文件的下载

下载图片和压缩包

在web文件夹中设置一个download文件夹用来存放下载资源。

页面:超链接中href="download/xxx.zip"下载压缩包

href="download/xxx.jpg"下载图片

注:因为浏览器无法识别zip,所以可以直接下载,而jpg可以识别,所以会直接展示。如果想直接下载,要自己写一个servlet去实现图片下载功能。

更改发布路径,获取需要下载的文件路径,从发布路径中截取到文件名,设置编码格式,设置响应头信息即告诉浏览器以附件的形式下载而不是展示,用输入输出流读取和输出(输出流用响应获取)。

DownloadServlet:

package com.qf.servlet;

import org.apache.commons.io.IOUtils;

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("/DownloadServlet")
public class DownloadServlet extends HttpServlet {

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

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");

        //获取需要下载的文件路径
        String realPath = this.getServletContext().getRealPath("download\\aaa.jpg");
        //从下载路径中截取文件名
        String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);
        //设置编码格式
        fileName = URLEncoder.encode(fileName, "UTF-8");
        //设置响应头,使浏览器只能以附件形式下载
        response.setHeader("Content-disposition","attachment;fileName="+fileName);

        FileInputStream fileInputStream = new FileInputStream(realPath);
        ServletOutputStream outputStream = response.getOutputStream();
        IOUtils.copy(fileInputStream,outputStream);
        fileInputStream.close();
        fileInputStream.close();
    }
}

经验:下载图片用servlet,将下载的文件放在一个地方。

MVC设计模式

在这里插入图片描述

M-model-模型层:biz/service-服务层:做业务;dao/mapper -数据持久层:操作数据库。

V-view-视图层:页面(HTML、CSS、JS、JSP)。

C-Controller-控制器层:控制页面的跳转。

服务端分层思想:controller、biz、dao

优缺点:

缺点:使用MVC不能减少代码量, 增加系统结构和实现的复杂性

优点:整个项目结构清晰,业务逻辑清晰,降低了代码的耦合性,代码的重用性高

MVC编写顺序:先写模型层

以WEB-学生管理系统为例:

创建mapper包,

StudentMapper接口:

package com.qf.mapper;

import com.qf.pojo.Student;

import java.util.List;

public interface StudentMapper {
    public void add(String username,String password,String name,String sex,int age,String hobbies,String photo);
    public void delete(String username);
    public void update(String username,String password,String name,String sex,int age,String hobbies,String photo);
    public Student getStudent(String username);
    public List<Student> getStudent(int offset,int count);
    public int getAllCount();
}


在mapper中创建一个包impl,实现接口,StudentMapperImpl:

package com.qf.mapper.impl;

import com.qf.mapper.StudentMapper;
import com.qf.pojo.Student;
import com.qf.utils.DBUtil;

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

public class StudentMapperImpl implements StudentMapper {
    @Override
    public void add(String username, String password, String name, String sex, int age, String hobbies, String photo) {
        try {
            DBUtil.commonInsert("insert into student values (?,?,?,?,?,?,?)",username,password,name,sex,age,hobbies,photo);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void delete(String username) {
        try {
            DBUtil.commonUpdate("delete from student where username=?",username);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void update(String username, String password, String name, String sex, int age, String hobbies, String photo) {
        String sql = "update student set username=?,password=?,name=?,sex=?,age=?,hobbies=?,photo=?";
        try {
            DBUtil.commonUpdate(sql,username,password,name,sex,age,hobbies,photo);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Student getStudent(String username) {
        Student student = null;
        try {
            student = DBUtil.commonQueryObj(Student.class, "select * from student where username=?", username);

        } catch (SQLException | InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        return student;
    }

    @Override
    public List<Student> getStudent(int offset, int count) {
        List<Student> list = new ArrayList<>();
        try {
            list = DBUtil.commonQueryList(Student.class, "select * from student ?,limit?", offset, count);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }return list;

    }

    @Override
    public int getAllCount() {
        int allCount = DBUtil.getAllCount("select count(username) from student");
        return allCount;
    }
}

注:mapper层所有的参数是service层传进来的。

同理:mapper包中写一个TeacherMapper接口:

package com.qf.mapper;

import com.qf.pojo.Teacher;

public interface TeacherMapper {
    public void update(String username,String password,String name,int courseId,String photo);
    public Teacher getTeacher(String username);
    public Teacher getTeacher(String username,String password);


}

在impl中写TeacherMapperImpl

package com.qf.mapper.impl;
import com.qf.mapper.TeacherMapper;
import com.qf.pojo.Teacher;
import com.qf.utils.DBUtil;
import java.sql.SQLException;
public class TeacherMapperImpl implements TeacherMapper {

    @Override
    public void update(String username, String password, String name, int courseId, String photo) {
        String sql = "update set teacher username=?,password=?,name=?,courseId=?,photo=?";
        try {
            DBUtil.commonUpdate(sql,username,password,name,courseId,photo);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    @Override
    public Teacher getTeacher(String username) {
        Teacher teacher =null;
        try {
            teacher = DBUtil.commonQueryObj(Teacher.class, "select * from teacher where username=?", username);
        } catch (SQLException | InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        return teacher;
    }

    @Override
    public Teacher getTeacher(String username, String password) {
        Teacher teacher = null;
        try {
            teacher = DBUtil.commonQueryObj(Teacher.class, "select * from teacher where username=? and password=?", username, password);
        } catch (SQLException | InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        return teacher;
    }
}

相关推荐

  1. <span style='color:red;'>Day</span><span style='color:red;'>41</span>

    Day41

    2024-06-18 21:36:06      7 阅读
  2. Day41| 416 分割等和子集

    2024-06-18 21:36:06       21 阅读
  3. LEETCODE-DAY41

    2024-06-18 21:36:06       15 阅读
  4. Day41 HTTP编程

    2024-06-18 21:36:06       11 阅读
  5. 算法训练营Day41

    2024-06-18 21:36:06       36 阅读
  6. day 41 动归 04

    2024-06-18 21:36:06       19 阅读
  7. 【代码随想录】day41

    2024-06-18 21:36:06       16 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-06-18 21:36:06       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-18 21:36:06       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-18 21:36:06       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-18 21:36:06       20 阅读

热门阅读

  1. 深入探讨:Spring与MyBatis中的连接池与缓存机制

    2024-06-18 21:36:06       8 阅读
  2. token无感刷新

    2024-06-18 21:36:06       4 阅读
  3. 【HarmonyOS NEXT 】鸿蒙detectBarcode (图像识码)

    2024-06-18 21:36:06       5 阅读
  4. Flink 计数器Accumulator

    2024-06-18 21:36:06       6 阅读
  5. MySQL触发器基本结构

    2024-06-18 21:36:06       8 阅读
  6. Roboflow对YOLO数据集、标注、训练、下载

    2024-06-18 21:36:06       8 阅读
  7. Bean 的生命周期

    2024-06-18 21:36:06       5 阅读
  8. web前端开发哪个城市:探索最佳发展地

    2024-06-18 21:36:06       9 阅读
  9. Linux中的进程控制

    2024-06-18 21:36:06       7 阅读
  10. 高并发系统中面临的问题 及 解决方案

    2024-06-18 21:36:06       4 阅读
  11. Vue 3 的 setup 函数使用及避坑指南

    2024-06-18 21:36:06       6 阅读
  12. leetcode-11-二叉树前中后序遍历以及层次遍历

    2024-06-18 21:36:06       6 阅读