SMBMS

本文详细介绍了SMBMS系统中的MVC架构应用,包括MVC概念、历史演变、各层职责划分(Model-View-Controller),以及如何实现用户登录、密码修改、权限控制和分页管理。涵盖了数据库配置、DAO与Service接口设计、JSP与Servlet的交互,以及文件上传和登录拦截器的实现。
摘要由CSDN通过智能技术生成

SMBMS

mysql -hlocalhost -uroot -p

set global time_zone = ‘+8:00’;

10、MVC三层架构

什么是MVC:Module View Controller 模型,视图,控制器

10.1、早些年

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ecklOf6u-1651655697760)(${images}/image-20210504092643736.png)]

用户直接访问控制层,控制层就可以直接操作数据库:

servlet---CRUD---->数据库
弊端:程序十分臃肿,不利于维护
servlet的代码中:处理请求,响应,视图跳转,处理JDBC,处理业务代码,处理逻辑代码

架构:没有什么是再加一层解决不了的
程序员调用JDBC
|
JDBC
|
MySQL Oracle sqlserver 。。。。

10.2、三层架构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1085VXdM-1651655697761)(${images}/image-20210504094912630.png)]

Module

  • 业务处理:业务逻辑(service)
    • service层叫服务层,被称为服务,肯定是相比之下比较高层次的一层结构,相当于将几种操作封装起来。
  • 数据持久层:CRUD(Dao)
    • dao层叫数据访问层,全称为data access object,属于一种比较底层,比较基础的操作,具体到对于某个表、某个实体的增删改查

面对接口编程

  • 在java中接口是多继承的,而类是单继承的,如果你需要一个类实现多个service,你用接口可以实现,用类定义service就没那么灵活
  • 要提供不同的数据库的服务时,我们只需要面对接口用不同的类实现即可,而不用重复地定义类
  • 编程规范问题,接口化的编程为的就是将实现封装起来,然调用者只关心接口不关心实现,也就是“高内聚,低耦合”的思想。

View

  • 展示数据
  • 提供链接发起servlet请求(a,from,img…)

Controller(Servlet)

  • 就收用户的请求:(request ,请求参数,session信息)
  • 交给业务层处理对应的代码
  • 控制视图的跳转
登录---->接受用户的登录请求---->处理用户的请求(获取用户登录的数据,username,password,)--->交给业务层处理登录业务(判断用户名密码是否正确,事务)---->Dao层查询用户名密码是否一样--->数据库

页面:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rsCot7bq-1651655697762)(${images}/image-20210505145503659.png)]

数据库:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2jfxnNKu-1651655697762)(${images}/image-20210505145847018.png)]

项目如何搭建

考虑不考虑使用maven?依赖jar

1.项目搭建准备工作

1.搭建一个maven web项目

  1. 配置pom.xml
  2. 配置web.xml
  3. 添加java和resoures文件夹

2.配置Tomcat

3.测试Tomcat是否能跑起来

4.导入jar包

  1. jsp
  2. servlet
  3. mysql
  4. 。。
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
<!--jsp,servelt,mysql-->
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>javax.servlet.jsp-api</artifactId>
      <version>2.3.3</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.5</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.11</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet.jsp.jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>

    <dependency>
      <groupId>taglibs</groupId>
      <artifactId>standard</artifactId>
      <version>1.1.2</version>
    </dependency>

  </dependencies>

int和Interger的区别

  • int是基本数据类型,Integer是引用数据类型;
  • int默认值是0,Integer默认值是null;
  • int类型直接存储数值,Integer需要实例化对象,指向对象的地址。

1、都是封装类,都是new出来的,肯定不相等。因为对象的内存地址不一样。
2、都是封装类,都不是new出来的,如果值在-128~127之间,那就相等,否则不相等。
3、如果是封装类和基本类型进行比较,只要数值相等那就相等,否则就不相等。因为封装类和基本数据类型进行比较的时候会有一个自动拆箱操作。
4、都是基本数据类型,如果数值相等,那就相等;否则不相等

5.创建项目包的结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ISn3wpGA-1651655697763)(${images}/image-20210505155712949.png)]

6.编写实体类

ORM映射:表——>类映射

7.编写基础公共类

1.数据库配置3文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A8RHJNvs-1651655697764)(${images}/image-20210505160158032.png)]

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/chenlong?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
username=root
password=123

2.编写数据库公共类

package com.kuang.dao;

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

//操作数据库的公共类
public class BaseDao {
    private  static String driver;
    private  static String url;
    private  static String username;
    private  static String passworld;
    //静态代码块,类加载的时候就加在了
    static {
        //读取配置文件
        //1.通过类加载器读取对应的资源
        InputStream inputStream = BaseDao.class.getClassLoader().getResourceAsStream("db.properties");
        //2.创建properties对象
        Properties properties = new Properties();

        try {
            properties.load(inputStream);
        } catch (IOException e) {

        }
        driver=properties.getProperty("driver");
        url=properties.getProperty("url");
        username=properties.getProperty("username");
        passworld=properties.getProperty("passworld");
    }

    //编写数据库连接对象的公共方法
    public static Connection getConnection(){
        Connection conn=null;
        try {
            //1.加载驱动
            Class.forName(driver);
            //2.获取连接对象
            conn = DriverManager.getConnection(url, username, passworld);
        } catch (Exception e) {
            e.printStackTrace();
        }
       return  conn;
    }


    public static ResultSet executeQuery(String sql, Object [] params ,Connection conn, PreparedStatement pstm, ResultSet rs){

        try {
            //预编译
            pstm=conn.prepareStatement(sql);
            //循环遍历参数数组,并将参数设入SQL中
            for (int i = 1; i < params.length; i++) {
                pstm.setObject(i,params[i-1]);//数组的index从0开始,而PreparedStatement中设置占位符的值的index从1开始
            }
            rs=pstm.executeQuery();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return rs;
    }
    //3、编写修改公共方法
    /**
     * 用于修改数据的公共方法,注意:使用发送SQL语句的对象为PreparedStatement
     * @param sql:修改数据的sql语句模板
     * @param params:模板中占位符对应的值
     *
     * =====下面两个对象需要调用出传入也是为了统一管理和关闭资源=====
     * @param conn:调用出使用BaseDao.getConnection()获取到数据库连接对象传入
     * @param pstmt:调用出只是传入null的引用。这个对象真正的实例化放在这个方法里面
     *
     * @return 返回受影响的行数
     */
    public static int executeUpdate(String sql,Object[] params,Connection conn,PreparedStatement pstmt){
        int result = 0;
        try {
            pstmt = conn.prepareStatement(sql);
            for (int i=1;i< params.length;i++){//循环遍历参数数组,并将参数设入SQL中
                pstmt.setObject(i,params[i-1]);//注意:数组的index从0开始,而PreparedStatement中设置占位符的值的index从1开始
            }
            result = pstmt.executeUpdate();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return  result;
    }
    
    //4、编写关闭资源公共方法
    /**
     * 关闭资源
     * @param conn:调用出使用BaseDao.getConnection()获取到数据库连接对象传入
     * @param pstmt:调用出只是传入null的引用。这个对象真正的实例化放在这个方法里面
     * @param rs:返回的结果集,和pstmt只是传入null的引用。这个对象真正的实例化放在这个方法里面
     * @return:返回关闭资源的结果
     *
     * 注意:关闭资源的时候要倒着关
     */
    public static boolean close(Connection conn,PreparedStatement pstmt,ResultSet rs){
        boolean flag = true;
        if (rs!=null){
            try {
                rs.close();
                rs = null;//让这个变量为null,gc就会自动对其进行回收
            } catch (SQLException throwables) {
                throwables.printStackTrace();
                flag = false;//关闭失败就将flag设置false
            }
        }
        if (pstmt!=null){
            try {
                pstmt.close();
                pstmt = null;
            } catch (SQLException throwables) {
                throwables.printStackTrace();
                flag = false;
            }
        }
        if (conn!=null){
            try {
                conn.close();
                conn = null;
            } catch (SQLException throwables) {
                throwables.printStackTrace();
                flag = false;
            }
        }
        return flag;//返回关闭结果
    }
}


}

3.编写字符集编码过滤器

8.导入静态资源

2.登录功能实现

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iOYGgmix-1651655697764)(${images}/image-20210506231905429.png)]

1.编写前端页面

编写前端页面时要放入web目录下

前端的代码可以直接使用已有的,我们主要进行网页背后的执行力代码

记得是login.jsp

2.设置欢迎页面

在web-inf中配置web.xml

<!--设置欢迎页面-->
<welcome-file-list>
    <welcome-file>login.jsp</welcome-file>
</welcome-file-list>

3.编写Dao层用户的登录的接口

  • 创建一个UserDao接口,然后我们按照“面向接口编程”的原则去实现UserDaolmpl中实现这个接口的方法
    • 使用面向接口编程的的好处就是我们在接口中只需要定义方法,而不需要实现方法,整个结构和思路都很清晰
    • 其次就是将设计和实现分离,保证设计专注于设计,实现专注于实习。
package com.kuang.dao.user;

import com.kuang.pojo.User;

import java.sql.Connection;

public interface UserDao {
    /*
    得到要进行登录的用户
    input:conn数据库连接对象,
          usercode:通过用户id查询用户的数据
     output;返回一个user对象,表示查到的用户
     */
    public User GetLoginUserInfo(Connection conn, String usercode);

}

4.编写Dao接口的实现类

package com.kuang.dao.user;

import com.kuang.dao.BaseDao;
import com.kuang.pojo.User;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class UserDaoImpl implements UserDao {
    @Override
    public User GetLoginUserInfo(Connection conn, String usercode) {
        PreparedStatement pstm=null;
        ResultSet rs=null;
        User user=null;
        if (conn!=null){
            String sql="select * from smbms_user where userCode = ?" ;
            Object [] param={usercode};
         try{
             rs= BaseDao.execute(sql,param,conn,pstm,rs);//调用项目搭建阶段准备的查询语句
             if (rs.next()){
                 user= new User();
                 user.setId(rs.getInt("id"));
                 user.setUserCode(rs.getString("userCode"));
                 user.setUserName(rs.getString("userName"));
                 user.setUserPassword(rs.getString("userPassword"));
                 user.setGender(rs.getInt("gender"));
                 user.setBirthday(rs.getDate("birthday"));
                 user.setPhone(rs.getString("phone"));
                 user.setAddress(rs.getString("address"));
                 user.setUserRole(rs.getInt("userRole"));
                 user.setCraeateBy(rs.getInt("createdBy"));
                 user.setCreateDate(rs.getTimestamp("creationDate"));
                 user.setModifyBy(rs.getInt("modifyBy"));
                 user.setModifyDate(rs.getTimestamp("modifyDate"));user.setId(rs.getInt("id"));
             }
             //关闭资源
             BaseDao.close(null,pstm,rs);
             //因为数据库的连接可能不只是这一个操作,所以我们不应该做完一件事就把数据库连接对象销毁,所以conn处传的null
         } catch (Exception e) {
             e.printStackTrace();
         }
        }
        return user;
    }
}

5.编写service接口

用户登录

public interface UserService {
    /**
     * 用户登录身份验证
     * @param userCode :用户账号
     * @param passworld;密码
     *                 注意,密码判断在我们service层进行,在Dao层只是简单的此操作数据库,没有其他逻辑代码
     *                 在servlet层只是接受和转发请求以及控制视图跳转
     *                 而对于业务层(service)就适合用来实现业务逻辑代码的
     * @return  User用户对象
     */
    //用户登录
    public User login(String userCode, String passworld);
}

6.业务层实现类

package com.kuang.service.User;

import com.kuang.dao.BaseDao;
import com.kuang.dao.user.UserDao;
import com.kuang.dao.user.UserDaoImpl;
import com.kuang.pojo.User;
import org.junit.Test;

import java.sql.Connection;

public class UserServiceImpl implements UserService
{

    //业务层需要使用Dao,所以直接将Dao作为一个成员变量来使用
    private UserDao userDao;
    public UserServiceImpl(){
    userDao = new UserDaoImpl();//在业务层实例化的时候,就让它得到dao对象,后面就可以直接取用
    }
    @Override
    public User login(String userCode, String passworld) {
        Connection  conn=null;
        User user=null;
        try{
            conn= BaseDao.getConnection();//获取数据库连接对象
            //通过业务层调dao层
            user=userDao.GetLoginUserInfo(conn,userCode);//调用userdao中的获取信息的方法
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            BaseDao.close(conn,null,null);
        }
        return user;
    }
@Test
public void test(){
    UserServiceImpl userService= new UserServiceImpl();
    User admin = userService.login("admin", "adwada");
    System.out.println(admin.getUserPassword());
}
}

7.编写servlet

package com.kuang.servlet.user;

import com.kuang.pojo.User;
import com.kuang.service.User.UserService;
import com.kuang.service.User.UserServiceImpl;
import com.kuang.util.Constant;

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

public class LoginServlet extends HttpServlet {
    //Servlet控制层调用业务层代码
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        System.out.println("进入LoginServlet");
        //获取用户名和密码
        String userCode = req.getParameter("userCode");
        String userPassword = req.getParameter("userPassword");
        //和数据库中的密码进行对比,调用业务层
        UserService userService=new UserServiceImpl();
        User user = userService.login("userCode", "userPassword");//这里已经把登录的人查出来了
        if (user!=null){//查有此人,可以登录
            //将用户信息放在session中
            req.getSession().setAttribute(Constant.USER_SESSION,user);
            //跳转到主页
            resp.sendRedirect("jsp/frame.jsp");
        }else {//查无此人
            //转发登录页面,顺带提示用户名密码错误
            req.setAttribute("error","用户名密码不正确");
            req.getRequestDispatcher("login.jsp").forward(req,resp);
        }


    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

8.注册servlet

<servlet>
    <servlet-name>LoginServlet</servlet-name>
    <servlet-class>com.kuang.servlet.user.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>LoginServlet</servlet-name>
    <url-pattern>/login.do</url-pattern>
</servlet-mapping>

9.测试功能,保证成功运行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6dHuN7LM-1651655697765)(${images}/image-20210509155303558.png)]

3.登录功能优化

注销功能:

思路:移出session,返回登录页面

public class LogoutServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //移出用户的session
        req.getSession().removeAttribute(Constant.USER_SESSION);
    resp.sendRedirect(req.getContextPath()+"/login.isp");//返回登录页面
    }

注册mxl

<servlet>
    <servlet-name>LogoutServlet</servlet-name>
    <servlet-class>com.kuang.servlet.user.LogoutServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>LogoutServlet</servlet-name>
    <url-pattern>/jsp/logout.do</url-pattern>
</servlet-mapping>

登录拦截器

编写登录拦截Filter

package com.kuang.filter;

import com.kuang.pojo.User;
import com.kuang.util.Constant;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class SysFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;

        //过滤器,从session中获取用户
            User user = (User) request.getSession().getAttribute(Constant.USER_SESSION);
        if(user==null){//用户已经被移除,或者未登录
            response.sendRedirect("/smbms/error.jsp");
        }else {
            filterChain.doFilter(req,resp);
        }

    }

    @Override
    public void destroy() {

    }
}

注册过滤器

<!--    用户登录过滤器-->
    <filter>
        <filter-name>SysFilter</filter-name>
        <filter-class>com.kuang.filter.SysFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>SysFilter</filter-name>
        <url-pattern>/jsp/*</url-pattern>
    </filter-mapping>

测试,成功!!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wWM3IGvw-1651655697765)(${images}/image-20210509222338075.png)]


至此登录,注销,权限都已完成,还剩密码修改,ajax验证旧密码,用户底层管理,用户管理分页,smbms架构分析即学习方法,

文件传输原理,文件上传,邮件发送,网站注册发送邮件实现,后话

4.密码修改

1.导入前端素材

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-27lGM2ES-1651655697766)(${images}/image-20210509223532649.png)]

2.写项目,建议从底层向上层写

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-myBUoKAK-1651655697766)(${images}/image-20210509230447268.png)]

3.UserDao接口

///修改当前用户的密码
public  int UpdatePwd(Connection con,int id , int passworld) throws SQLException;
/*
返回修改的条数,pwd是String?
 */

4.UserDaoImpl接口实现类

///修改当前用户的密码
@Override
public int UpdatePwd(Connection con, int id, int passworld) throws SQLException {
    int execute=0;//提升作用域
    PreparedStatement pstm=null;
    Object[] params={passworld,id};
    String sql="UPDATE `smbms_user` SET   `userPassword`=?  WHERE id=?";
    //提高安全性,防止没有连接就可以修改
    if(con!=null){
        execute= BaseDao.execute(sql, params, con, pstm);
        BaseDao.close(null,pstm,null);
    }
    return execute;
}

5.service层

5.1userService接口

//用户修改密码
public boolean updatePwd(int id,int pwd);

5.2userService接口实现

@Override
public boolean updatePwd(int id, int pwd) {
    Connection conn=null;
    conn= BaseDao.getConnection();
    boolean flag=false;
    try {
        //业务层需要使用Dao,所以直接将Dao作为一个成员变量来使用
        /*
        userdao已经在前面生成了,可以直接使用
         */
        if (userDao.UpdatePwd(conn,id,pwd)>0){
            flag=true;
        }
    }catch (SQLException e){
        e.printStackTrace();

    }finally {
        BaseDao.close(conn,null,null);
    }
    return flag;

}

6.编写servlet

package com.thhh.servlet.user;

import com.mysql.jdbc.StringUtils;
import com.thhh.pojo.User;
import com.thhh.service.user.UserService;
import com.thhh.service.user.UserServiceImpl;
import com.thhh.utils.Constants;

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

//实现servlet复用
public class UserServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        boolean flag = false;

        Object user = req.getSession().getAttribute(Constants.USER_SESSION);
        String newpassword = req.getParameter("newpassword");

        if (user!=null && !StringUtils.isNullOrEmpty(newpassword)){//获取到了这个用户对象且获取到的新密码不为空
            UserService userService = new UserServiceImpl();
            flag = userService.updatePwd(newpassword,((User)user).getId());//servlet调用业务层
            if (flag){//修改成功
                req.setAttribute("message","密码修改成功!请使用新密码重新登陆");
                //移除用户的session,利用过滤器阻止用户再进行操作,直接跳转error.jsp页面
                req.getSession().removeAttribute(Constants.USER_SESSION);
            }else{
                req.setAttribute("message","密码修改失败");
            }
        }else {
            //用户可以进行密码修改,则user一定不是null,所以跳入这个分支的原因一定是newpassword = NULL
            req.setAttribute("message","密码设置有误,请重新输入!");
        }
        //无论是修改成功还是失败,都重定向到密码修改页面,就是在刷新页面,否则我们设置在req中的message属性不会被前端读到
        req.getRequestDispatcher("pwdmodify.jsp").forward(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

7.注册servlet

<servlet>
    <servlet-name>UserServlet</servlet-name>
    <servlet-class>com.thhh.servlet.user.UserServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>UserServlet</servlet-name>
    <url-pattern>/jsp/user.do</url-pattern>
</servlet-mapping>

8.实现复用

实现复用需要提取出方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9WfpDwB6-1651655697766)(${images}/image-20210511172215534.png)]

package com.kuang.servlet.user;

import com.kuang.pojo.User;
import com.kuang.service.User.UserService;
import com.kuang.service.User.UserServiceImpl;
import com.kuang.util.Constant;
import com.mysql.cj.util.StringUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.Connection;
//实现servlet复用
public class UserServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getParameter("method");
        if(method!=null&&method.equals("savepwd")){
            this.updatePwd(req,resp);
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }

    public  void  updatePwd(HttpServletRequest req, HttpServletResponse resp){
        //获取用户的session中获取id
        Object o = req.getSession().getAttribute(Constant.USER_SESSION);
        String newpassword = req.getParameter("newpassword");
        boolean flag=false;
        if(o!=null&& (!StringUtils.isNullOrEmpty(newpassword))){
            UserService userService = new UserServiceImpl();
            flag = userService.updatePwd(((User)o).getId(), newpassword);
            if (flag){
                req.setAttribute("message","succsess");
                //密码修改成功移出session
                req.getSession().removeAttribute(Constant.USER_SESSION);
            }else {
                req.setAttribute("message","fail");
            }
        }else {
            req.setAttribute("message","pwd is error");
            try {
                req.getRequestDispatcher("pwdmodify.jsp").forward(req,resp);
            } catch (ServletException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }
}

5.ajax验证旧密码

什么是ajax

1.什么是Ajax

  1. AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)
  2. AJAX 不是新的编程语言,而是一种使用现有标准的新方法
  3. AJAX 是在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页的艺术
    • 通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新
    • 这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新
  4. 传统的网页(不使用 AJAX)如果需要更新内容,必需重载整个网页面

2.怎么实现旧密码验证?

直接的做法就是去数据库中查找,但是这样的画我们又要写一条线:Dao开始–>service–>servlet–>JSP

我们可以采取一种很简单的方式进行:在用户登陆的时候我们将整个用户对象都存储到session中了,所以我们可以利用Ajax的异步请求+session中的user对象实现旧密码的验证

做法就是在后端复用的servlet中再定义一个方法,这个方法专门负责对比Ajax传递给后端的旧密码是否和session中User对象的密码一致,并通过对比的情况和结果来返回JSON数据给前端

3.编写servlet

oldpassword.on("blur",function(){
   $.ajax({
      type:"GET",
      url:path+"/jsp/user.do",//Ajax提交的URL,可见和我们修改密码的JSP页面的表单提交的URL一样,所以我们应该复用servlet
      data:{method:"pwdmodify",oldpassword:oldpassword.val()},//提交的参数
      //上面两句话等价于 = path+/jsp/user.do ? method=pwdmodify & oldpassword=oldpassword.val()

           dataType:"json",
      success:function(data){
         if(data.result == "true"){//旧密码正确
            validateTip(oldpassword.next(),{"color":"green"},imgYes,true);
         }else if(data.result == "false"){//旧密码输入不正确
            validateTip(oldpassword.next(),{"color":"red"},imgNo + " 原密码输入不正确",false);
         }else if(data.result == "sessionerror"){//当前用户session过期,请重新登录
            validateTip(oldpassword.next(),{"color":"red"},imgNo + " 当前用户session过期,请重新登录",false);
         }else if(data.result == "error"){//旧密码输入为空
            validateTip(oldpassword.next(),{"color":"red"},imgNo + " 请输入旧密码",false);
         }
      },
      error:function(data){
         //请求出错
         validateTip(oldpassword.next(),{"color":"red"},imgNo + " 请求错误",false);
      }
   });

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hmn0nYnv-1651655697767)(${images}/1747479-20200909202727331-1672399838.png)]

4.验证旧密码

 //2.验证旧密码
    //直接与session中的user对象的密码进行对比即可,不用再去查数据库
    public void pwdModify(HttpServletRequest req, HttpServletResponse resp){
        Object user = req.getSession().getAttribute(Constants.USER_SESSION);
        String oldpassword = req.getParameter("oldpassword");

        //使用Map集合存储返回给前端的数据
        Map<String,String> resultMap = new HashMap<String, String>();

        if (user==null){//session中的user对象失效了
            resultMap.put("result","sessionerror");
        }else if (StringUtils.isNullOrEmpty(oldpassword)){//输入的密码为空
            resultMap.put("result","error");
        }else {
            String userPassword = ((User)user).getUserPassword();
            if (userPassword.equals(oldpassword)){//输入的密码匹配
                resultMap.put("result","true");
            }else {//输入的密码不匹配
                resultMap.put("result","false");
            }
        }

        //将Map集合中的数据转为JSON格式传输给前端
        try {
            resp.setContentType("application/JSON");//设置返回数据是一个JSON,这样浏览器拿到这个数据之后就会按照JSON的数据格式来解析它
            PrintWriter writer = resp.getWriter();//获取输出流
            writer.write(JSONArray.toJSONString(resultMap));//使用阿里巴巴提供的一个JSON工具类,直接将其他数据格式的数据转为JSON数据格式,然后直接将其写出去
            writer.flush();//刷新缓冲区
            writer.close();//关闭资源
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

5、测试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N6ZSKEj6-1651655697767)(${images}/1747479-20200909202751837-335071298.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3WE25KhO-1651655697768)(${images}/1747479-20200909202812308-1507354166.png)]

6.用户管理功能实现

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IgIoADry-1651655697768)(${images}/1747479-20200909202936680-574336968.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n1Qqun0r-1651655697769)(${images}/1747479-20200909202943395-25047443.png)]

1.导入分页工具类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wFX68NLN-1651655697769)(${images}/1747479-20200909202959922-1612350389.png)]

查看一下这个工具类的源码[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q5cySxW5-1651655697770)(${images}/1747479-20200909203010937-1786980443.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wyZCtJmH-1651655697770)(${images}/1747479-20200909203016663-107565294.png)]

OOP的3大特性:封装、继承、多态,其中封装 = 属性私有+属性的get/set() + 在set中限制一些不安全的赋值操作(这一步可以留到service层再做,但是在封装的时候做更好,这样减少了service层的代码,且体现了封装的特性)

2.用户列表页面导入

首先是HTML文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ruDmtZVW-1651655697771)(${images}/1747479-20200909203034599-1462656386.png)]
分析:

  • Dao层使用聚合函数COUNT(*)查询用户表一共多少条记录
  • service层用于统计总共有多少条数据
  • servlet层用于向前端返回总共的数据条数

3.获取用户数量

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2uI8dCz8-1651655697771)(${images}/1747479-20200909203104060-1507838416.png)]

1、UserDao接口

因为是获取用户的数量,所以和用户表有关,那这个接口方法就放在前面已经创建好的UserDao中

方法定义的时候需要哪些参数?

查看前端素材:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-82fay8pl-1651655697771)(${images}/1747479-20200909203120578-1278957015.png)]
​ 可见,查询界面我们需要实现按照用户名查询、按照角色名称查询和整表查询

再去看看数据库中的数据表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B3apb1HT-1651655697772)(${images}/1747479-20200909203133549-1455463515.png)]

联表查询的基本SQL语句

SELECT COUNT(1)
FROM smbms_user u,smbms_role r
WHERE u.userRole = r.id;

这是MYSQL的方言版内连接查询,内连接两张表的地位平等,只要我们加了WHERE过滤笛卡尔积,那么输出的结果就只包含有主外键关联的字段

但是从前面的需求我们可以看出,前端素材提供了按照姓名查询、按照职位查询和整表查询,所以我们需要在上面联表查询的基础上再添加一些筛选条件,添加的手段就是使用"AND 条件"来实现

在Java中实现我们可以使用StringBuffer来实现sql语句的拼接

package com.thhh.dao.user;

import com.thhh.pojo.User;

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

public interface UserDao {
    /**
     * 1、得到要进行登陆的用户
     * @param conn:数据库连接对象
     * @param userCode:通过用户的用户名userCode查询用户数据
     * @return
     */
    public User getLoginUserInfo(Connection conn,String userCode);

    /**
     * 2、修改用户密码
     * @param conn:数据库连接对象
     * @param id:修改密码的用户的ID
     * @param newPwd:新密码
     * @return:影响行数
     */
    public int updatePwd(Connection conn,String newPwd,int id);

    /**
     * 3、用于获取数据库中用户总数
     * @param conn:数据库连接对象
     * @param userName:用户名,这个参数主要用于按照用户名称查询用户信息
     * @param userRole:用户角色,这个参数主要用于按照用户角色查询用户信息
     * @return :查询到的行数
     */
    public int getUserCount(Connection conn, String userName, int userRole) throws SQLException;
}
2、UserDaoImpl接口实现
//3、根据用户名/角色名获取用户总数
@Override
public int getUserCount(Connection conn, String userName, int userRole) throws SQLException {
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    int count = 0;
    if (conn!=null){
        StringBuffer sql = new StringBuffer();//使用字符串缓冲区,这样就可以动态的在sql后面追加AND条件了
        sql.append("SELECT COUNT(1) COUNT FROM smbms_user u,smbms_role r WHERE u.userRole = r.id");//基本的联表查询SQL语句
        List<Object> list = new ArrayList<Object>();//创建一个list来存储我们要拼接的筛选条件,由于我们不能限制传入的参数的数据类型,所以泛型指定为object
        if (!StringUtils.isNullOrEmpty(userName)){//判断传入的用户名是不是空,如果不是空表明前端指定了按照姓名查询的参数
            sql.append("AND userName like ?");
            list.add("%"+userName+"%");
        }
        if (userRole>0){
            sql.append("AND userRole = ?");
            list.add(userRole);
        }
        Object[] params = list.toArray();//获得BaseDao中为pstmt对象设置参数的参数列表
        rs = BaseDao.executeQuery(sql.toString(), params, conn, pstmt, rs);//调用查询定义好的查询方法
        if (rs.next()){//获取查询结果
            count = rs.getInt("COUNT");//COUNT是在SQL语句中为查询结果取的别名
        }
        BaseDao.close(null,pstmt,rs);//关闭资源
    }
    return count;
}

注意回顾知识点:

  • StringBuffer类:一个专门用来弥补String内容不可修改的类,调用这个类对象的append()可以实现在字符串的末尾加上新字符串且不会产生新的字符串对象(String对象的拼接操作其实在底层就是产生了新的String对象)
  • 使用List集合来存储参数,这样做的好处就在于我们可以动态的向集合中添加参数,而不是像前面使用数组那样固定了数组长度;其实集合也是用来弥补数组长度不可修改的缺陷而出现的,使用集合存储数据即使数据很多也不会产生新的集合对象,而数组不一样,数组一经创建长度就固定了;而针对上面要实现的需求我们不知道到底前端要进行哪一种操作,所以我们不能定义一个定长的数组,即使按照最大需求量定义数组,虽然可以满足要求,但是在不能充分使用的时候就是资源的浪费
  • 上面使用的动态的拼接SQL语句的做法很明智:既使用了StringBuffer对象控制SQL语句在提交之前可变,又使用了List集合来存储参数,在提交的时候才将其转为数组
3、UserService接口
package com.thhh.service.user;

import com.thhh.pojo.User;

public interface UserService {
    /**
     * 1、获取登陆用户对象,对用户登陆身份进行验证
     * @param userCode:用户账号
     * @param userPassword:用户密码,注意,密码判断我们在service层进行;
     *                      在Dao层只是简单的操作数据库,没有其他的逻辑代码;在servlet层中只是接收和转发请求以及控制视图跳转
     *                      而对于业务层(service)就是用来实现业务逻辑代码的
     * @return
     */
    public User login(String userCode,String userPassword);

    /**
     * 2、根据用户ID修改用户密码
     * @param newPwd:新密码
     * @param id:用户ID
     * @return
     */
    public boolean updatePwd(String newPwd, int id);

    /**
     * 3、获取用户总数
     * @param userName:按照用户姓名查,查到的用户总数
     * @param userRole:按照用户角色查,查到的用户总数
     * @return:返沪对应查询条件查到的用户总数
     */
    public int getUserCount(String userName, int userRole) ;
}

只看方法3

4、UserServiceImpl接口实现
/**
 * 3、按照条件查询符合条件的用户总数
 * @param userName:按照用户姓名查,查到的用户总数
 * @param userRole:按照用户角色查,查到的用户总数
 * @return
 */
@Override
public int getUserCount(String userName, int userRole) {
    Connection conn = null;
    int rs = 0;

    try {
        conn = BaseDao.getConnection();//获取数据库连接对象
        rs = userDao.getUserCount(conn,userName,userRole);//业务层调用Dao层获取业务结果
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    }finally {
        BaseDao.close(conn,null,null);//关闭资源
    }
    return rs;//业务层将业务结果返回给servlet
}
5、测试

bug1:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GtIuByXB-1651655697773)(${images}/1747479-20200909203209066-625874060.png)]

4.获取用户列表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yZoodXGk-1651655697773)(${images}/1747479-20200909203224671-30813080.png)]

1、UserDao接口
/**
 * 4、用户获取用户列表
 * @param conn:数据库连接对象
 * ===========后面两个参数用于条件查询用户数据
 * @param userName:按照用户名查找
 * @param userRole:按照角色名称查找
 * ===========后面两个参数用于对按照上面条件查询出来的结果进行分页处理
 * @param currentPageNo:翻到第多少页
 * @param pageSize:每一页多少条数据
 * @return:返回满足条件的user对象集合
 */
public List<User> getUserList(Connection conn, String userName, int userRole, int currentPageNo, int pageSize) throws SQLException;
2、UserDaoImpl接口实现
    //4、获取满足条件的用户对象集合
    @Override
    public List<User> getUserList(Connection conn, String userName, int userRole, int currentPageNo, int pageSize) throws SQLException {
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        List<User> userList = null;
        if (conn!=null){
            userList = new ArrayList<User>();
            StringBuffer sql = new StringBuffer();
            sql.append("SELECT u.*,r.roleName as userRoleName FROM smbms_user u,smbms_role r WHERE u.userRole = r.id");

            List<Object> temp = new ArrayList<Object>();
            if (userName!=null){
                sql.append(" AND u.userName LIKE ?");
                temp.add("%"+userName+"%");
            }
            if (userRole>0){
                sql.append(" AND u.userRole = ?");
                temp.add(userRole);
            }

            sql.append(" ORDER BY u.creationDate DESC LIMIT ?,?");//在sql最后追加一个排序和分页
            //5
            //1     5
            //2     10
            //3     15
            currentPageNo = (currentPageNo-1)*pageSize;//减一的原因就是MYSQL分页的index从0开始

            temp.add(currentPageNo);//从哪一个下标开始
            temp.add(pageSize);//从currentPageNo连续取几个

            Object[] params = temp.toArray();
            rs = BaseDao.executeQuery(sql.toString(),params,conn,pstmt,rs);
            while (rs.next()){
                User _user = new User();
                _user.setId(rs.getInt("id"));
                _user.setUserCode(rs.getString("userCode"));
                _user.setUserName(rs.getString("userName"));
                _user.setGender(rs.getInt("gender"));
                _user.setBirthday(rs.getDate("birthday"));
                _user.setPhone(rs.getString("phone"));
                _user.setUserRole(rs.getInt("userRole"));
                _user.setUserRoleName(rs.getString("userRoleName"));//这个属性是在POJO中新加入的,数据表中没有
                userList.add(_user);//将查到的这个对象分装为对象并存入List集合中
            }
            BaseDao.close(null,pstmt,rs);
        }
        return userList;
    }
3、UserService接口
/**
 * 4、根据用户名/用户角色名称来查询数据,返回一个User对象集合,而currentPageNo+pageSize用于前端做分页操作
 * @param userName
 * @param userRole
 * @param currentPageNo
 * @param pageSize
 * @return:满足条件+limit的User对象集合
 */
public List<User> getUserList(String userName, int userRole, int currentPageNo, int pageSize);
4、Userservice接口实现
/**
 * 4、根据用户名/用户角色名称来查询数据,返回一个User对象集合,而currentPageNo+pageSize用于前端做分页操作
 * @param userName
 * @param userRole
 * @param currentPageNo
 * @param pageSize
 * @return:满足条件+limit的User对象集合
 */
@Override
public List<User> getUserList(String userName, int userRole, int currentPageNo, int pageSize) {
    Connection conn = null;
    List<User> userList = null;

    try {
        conn = BaseDao.getConnection();//获取连接
        userList = userDao.getUserList(conn,userName,userRole,currentPageNo,pageSize);//业务层调用Dao层获取业务结果
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    }finally {
        BaseDao.close(conn,null, null);//关闭资源
    }
    return userList;//业务层将业务结果返回给servlet
}
5、测试
@Test
public void test(){
    List userList = new UserServiceImpl().getUserList(null,0,2,5);
    //(3-1)*5 = 10,所以展示的是10~14条数据,但是一共只有12条,注意:MYSQL中结果index从0开始
    for (Object o : userList) {
        System.out.println(((User)o).getUserName());
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8XnCAW1O-1651655697774)(${images}/1747479-20200909203248681-1355249713.png)]
测试完成!

5.获取角色列表

Dao

RoleDao

package com.kuang.dao.role;

import com.kuang.pojo.Role;

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

public interface RoleDao  {
    //获取角色列表
    public List<Role> getRoleList(Connection conn) throws SQLException;
}

RoleDaoImpl

package com.kuang.dao.role;

import com.kuang.dao.BaseDao;
import com.kuang.pojo.Role;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class RoleDaoImpl implements RoleDao {
    @Override
    public List<Role> getRoleList(Connection conn) throws SQLException {
        PreparedStatement pstm = null;
        ResultSet rs = null;
        ArrayList<Role> rolelist=new ArrayList<>();
        if (conn != null) {
            String sql = "select * from smbms_role";
            Object param[] = {};

            rs = BaseDao.execute(sql, param, conn, pstm, rs);

            while (rs!=null){
                Role _role=new Role();
                _role.setRoleName(rs.getString("id"));
                _role.setRoleCode(rs.getString("roleCode"));
                _role.setId(rs.getInt("id"));
                rolelist.add(_role);
            }
            BaseDao.close(conn,pstm,rs);
        }
        return rolelist;

    }
}

Service

RoleService

package com.kuang.service.Role;

import com.kuang.pojo.Role;

import java.util.List;

public interface RoleService {
    //在dao层已经查到了角色表,并返回了一个RoleList
    //在service层应该干啥呢?就是调用dao层的代码,没有任何其余操作,只不过改善一下逻辑
    public List<Role> getRoleList();
}

RoleService

package com.kuang.service.Role;

import com.kuang.dao.BaseDao;
import com.kuang.dao.role.RoleDao;
import com.kuang.dao.role.RoleDaoImpl;
import com.kuang.pojo.Role;

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

public class RoleServiceImpl implements RoleService {
    //引入dao方便调用
    RoleDao roleDao;

    public RoleServiceImpl( ) {
        roleDao =new RoleDaoImpl();
    }

    @Override
    public List<Role> getRoleList() {
        Connection connection=null;
        List<Role> RoleList=null;
        try {
            connection = BaseDao.getConnection();
            RoleList= roleDao.getRoleList(connection);
        }catch (SQLException e){
            e.printStackTrace();
        }finally {
            BaseDao.close(connection,null,null);
        }
        return  RoleList;
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5QVj2uYk-1651655697774)(${images}/image-20210515211510851.png)]

7.smbms用户分页管理

为了我们职责统一,可以把角色的操作单独放在一个包里,和pojo对应

8、项目总结

1.可以参照正式项目,将其拖入Tomcat总运行

扩展功能文件上传和下载

1、文件上传

1.准备工作

1.创建空java项目的方法

关于项目结构的使用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fysWh1xY-1651655697774)(${images}/image-20210516093246464.png)]

  1. 选择空的,然后创建file
  2. [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uASmLzuS-1651655697774)(${images}/image-20210515221933987.png)]
  3. 导包,从官网下载,创建lib目录,添加为资源,在把包拖进去

4.修复

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3lO2YrrN-1651655697775)(${images}/image-20210516093142770.png)]

image-20210516093159242

2.注意事项

【文件上传的注意事项】
1.为保证服务器安全,上传文件应该放在外界无法直接访问的目录下,比如放于WBNF目录下
2为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名

!!1.时间戳

2.uuid

3.md5加密

4.位运算

3.要限制上传文件的最大值

4.可以限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法

【需要用到的类详解】
ServletFileUpload负责处理上传的文件数据并将表单中每个输入项封装成一个 Fileltem对象,在使用 ServletFileUpload对象解析请求时需要Dis kFileltem Factory对象。所以,我们需要在进行解析工作前构造好

DiskFileltem Facto对象,通过 ServletFileUpload对象的构造方法或 setFileltem Factory(方法设置 ServletFileUpload对象的 fileltem Factory属性Fileltem类

FileItem类

在HTML页面nput必须有 name

 <input type="file”  name="filename">

表单如果包含一个文件上传输入项的话,这个表单的 enctype属性就必须设置为 multipart/form-data

on connection=null;
List RoleList=null;
try {
connection = BaseDao.getConnection();
RoleList= roleDao.getRoleList(connection);
}catch (SQLException e){
e.printStackTrace();
}finally {
BaseDao.close(connection,null,null);
}
return RoleList;
}
}


[外链图片转存中...(img-5QVj2uYk-1651655697774)]



# 7.smbms用户分页管理

==为了我们职责统一,可以把角色的操作单独放在一个包里,和pojo对应==





# ==8、项目总结==

1.可以参照正式项目,将其拖入Tomcat总运行

2.













# 扩展功能文件上传和下载

## 1、文件上传

### 1.准备工作

1.创建空java项目的方法

> 关于项目结构的使用

[外链图片转存中...(img-fysWh1xY-1651655697774)]

1. 选择空的,然后创建file
2. [外链图片转存中...(img-uASmLzuS-1651655697774)]
3. 导包,从官网下载,创建lib目录,添加为资源,在把包拖进去

4.修复

[外链图片转存中...(img-3lO2YrrN-1651655697775)]

[外链图片转存中...(img-NBRoawfh-1651655697775)]

### 2.注意事项

**【文件上传的注意事项】**
1.为保证服务器安全,上传文件应该放在外界无法直接访问的目录下,比如放于==WBNF目录下==。
2为防止文件覆盖的现象发生,要为上传文件**产生一个唯一的文件名**

**!!1.时间戳**

**2.uuid**

**3.md5加密**

**4.位运算**

**3.要限制上传文件的最大值**

4.可以限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法

**【需要用到的类详解】**
ServletFileUpload负责处理上传的文件数据并将表单中每个输入项封装成一个 Fileltem对象,在使用 ServletFileUpload对象解析请求时需要Dis kFileltem Factory对象。所以,我们需要在进行解析工作前构造好 

DiskFileltem Facto对象,通过 ServletFileUpload对象的构造方法或 setFileltem Factory(方法设置 ServletFileUpload对象的 fileltem Factory属性Fileltem类



### **FileItem类**

在HTML页面nput必须有 name

```html
 <input type="file”  name="filename">

表单如果包含一个文件上传输入项的话,这个表单的 enctype属性就必须设置为 multipart/form-data

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奥丁之眼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值