用Java手写一个微型下载资源网站

手写一个微型下载资源网站【Java实现用户注册、登陆、下载功能】

一、技术栈

语言:Java

Web:html + css + js +bootstrap

数据库:MySQL8.2

服务器:Tomcat

开发环境:JDK–1.8 、Servlet–3.0 、ntelliJ IDEA、Navicat-11

所需知识:javase、servlet、http、request、response、cookie、session、jsp+el+jstl、filter、listener

知识点详细总结链接:

01-HTML+CSS+JS【常用总结+案例练习】

02-JavaWeb开发【Tomcat服务器&Servlet接口&HTTP协议&HttpServletRequest&BeanUtils工具包封装请求数据&HttpServletResponse】

03-JavaWeb开发【最详细的Cookie&Session分析】

04-JavaWeb开发【JSP&EL&JSTL&MVC&Bootstrap前端框架】

05-JavaWeb的三大组件:小程序Servlet+过滤器Filter+监听器Listener

二、流程分析图

注册

在这里插入图片描述

登陆

在这里插入图片描述

下载

在这里插入图片描述

三、案例实现效果

页面没有太多美化修饰,具体以后端业务实现为主哦!

首页

在这里插入图片描述

注册

在这里插入图片描述

登陆

在这里插入图片描述

下载网主页

在这里插入图片描述

壁纸下载

在这里插入图片描述

书籍下载

在这里插入图片描述

影视下载

在这里插入图片描述

四、案例代码实现

注意事项

可先看,这些处理概念。后面开发时需要处理的细节。

一、将下载的文件放到WEB-INF下面,更安全,用户不会通过浏览器对其访问。
    
    
-------------------------------------------------------------
    
    
二、关于乱码问题:
首先要处理接收请求中文乱码问题:
 request.setCharacterEncoding("utf-8");
然后解决响应给浏览器的乱码问题:
//解决响应体乱码问题
response.setContentType("text/html;charset=utf-8");
说明:上述只是解决响应体乱码问题,不能解决响应头乱码问题,由于告诉浏览器以附件形式下载,
    使用的文件名在响应头中响应给浏览器,所以上述不能解决乱码问题。所以我们采用另外一种解决方案。

方案:URLEncoder按照UTF-8的编码方式对filename文件名进行编码。
  //设置浏览器以附件形式下载
  //对文件名编码
  String encodeFilename = URLEncoder.encode(filename, "UTF-8");
  response.setHeader("Content-Disposition","attachment;filename="+encodeFilename);



--------------------------------------------------------------
    
    
    
三、关于下载编写Servlet处理文件下载逻辑
1. 获取当前项目中资源的真实路径;
String realPath = getServletContext().getRealPath("/WEB-INF/"+filename)2. 获取文件的mimeType;
String mimeType =  getServletContext().getMimeType(fileName);



--------------------------------------------------------------
    
    
四、关于设置文件为附件的形式 
对于response.setHeader("Content-Disposition","attachment;filename="+encodeFilename);
1.如果不加attachment关键字,则用户点击图片就可以直接在浏览器查看图片了,而我们的功能是下载。
所以要加attachment关键字,attachment表示附件的意思。
    
2.如果不书写 filename ,那么下载的文件名字就是当前servlet的路径名字。

3.【文件下载】两个响应头+流拷贝
    1. 设置响应头: Content-Disposition    // 告诉浏览器以附件的形式来处理文件;
    2. 设置响应头: response.setContentType(mimeType) // 告诉浏览器下载的文件的类型;
    3. 流拷贝:
       1. 将文件读取到流里边: FileInputStream fin = new FileInputStream(file)2. 将流写给浏览器:response.getOutputStream().write(arr,0,len);
1、数据库设计
create database user_managerment;
use user_managerment;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(20) NOT NULL,
  `password` varchar(32) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'zhangsan', '1234');
INSERT INTO `user` VALUES ('2', 'lisi', '1234');
INSERT INTO `user` VALUES ('3', 'wangwu', '1234');
INSERT INTO `user` VALUES ('4', 'zhaoliu', '1234');
2、项目结构
1)src

在这里插入图片描述

2)web

在这里插入图片描述

3)工具包

在这里插入图片描述

4)配置文件

在这里插入图片描述

jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/user_managerment\
  ?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false
jdbc.username=root
jdbc.password=1234
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--加载外部的配置文件-->
    <properties resource="jdbc.properties"/>
    <!--别名-->
    <typeAliases>
        <package name="cn.itcast.sh.login.pojo"/>
    </typeAliases>
    <!--mybatis环境的配置
        一个核心配置文件,可以配置多个运行环境,default默认使用哪个运行环境
    -->
    <environments default="development">
        <!--通常我们只需要配置一个就可以了, id是环境的名字 -->
        <environment id="development">
            <!--事务管理器:由JDBC来管理-->
            <!--
                事务管理器type的取值:
                1. JDBC:由JDBC进行事务的管理
                2. MANAGED:事务由容器来管理,后期学习Spring框架的时候,所有的事务由容器管理
            -->
            <transactionManager type="JDBC"/>
            <!--数据源的配置:mybatis自带的连接池-->
            <!--
                数据源:
                1. POOLED:使用mybatis创建的连接池
                2. UNPOOLED:不使用连接池,每次自己创建连接
                3. JNDI:由服务器提供连接池的资源,我们通过JNDI指定的名字去访问服务器中资源。
            -->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>

    </environments>
    <!--映射器-->
    <mappers>
        <!--加载其它的映射文件 注:注解开发是点号-->
        <!--<package name="com.itheima.sh.dao"></package>-->
        <!--
            加载其它的映射文件 xml形式
                包扫描方式加载mapper映射文件,说明:
                1. 要求mapper映射文件,与mapper接口要放在同一个目录
                2. 要求mapper映射文件的名称,与mapper接口的名称要一致
            -->
        <package name="cn.itcast.sh.login.dao"/>
    </mappers>
</configuration>
log4j.properties
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n

### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=D:/IdeaProjects/mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n

### set log levels - for more verbose logging change 'info' to 'debug' ###

log4j.rootLogger=debug, stdout,file

5)web页面
index.jsp
<!DOCTYPE html>
<html lang="en">
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <!-- 指定网页的字符集为utf-8-->
    <meta charset="utf-8">
    <!--使用微软最新的浏览器Edge的内核来解析当前的HTML文件,最新的浏览器都支持 -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!--
    响应式布局参数:
     viewport: 视口的参数
     width=device-width:默认的网页宽度是设备的宽度
     initial-scale=1: 初始的缩放比1:1
     -->
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->

    <!-- 1. 导入bootstrap的全局css样式  -->
    <link href="css/bootstrap.min.css" rel="stylesheet">
    <title>Beauty下载网</title>
</head>
<body>
<!--<div align="left">-->
<!--    <a href="register.jsp" class="btn btn-success">用户注册</a>-->
<!--    <a href="login.jsp" class="btn btn-primary">用户登陆</a>-->
<!--</div>-->

<h3 font-siz:200px align="center">Beauty下载网</h3>
<hr>

<div class="container">
    <!-- 默认是白色,navbar-inverse  表示黑色-->
    <nav class="navbar navbar-inverse">
        <!-- container-fluid 表示占容器nav的100%-->
        <div class="container-fluid">
            <!-- navbar-header表示指定商标和开关的样式,让它在手机上更好的显示-->
            <div class="navbar-header">
                <!--开关按钮-->
                <!--data-toggle="collapse" 表示点击按钮时可以折叠
                   data-target 点击开关按钮时,可以找到id是bs-example-navbar-collapse-1下拉菜单
                   而我们发现下面的下拉菜单的id正好是 bs-example-navbar-collapse-1
-->
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                        data-target="#bs-example-navbar-collapse-1">
                    <!--开关按钮中的白线-->
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <!--navbar-brand 表示商标 类似于苹果网站的苹果标志,无论设备窗口怎么变化,商标都会存在-->
                <a class="navbar-brand" href="#">Beauty</a>
            </div>

            <!-- 下拉菜单,表单等 navbar-collapse 表示可以折叠的所有的项-->
            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                <ul class="nav navbar-nav">
                    <li class="active">
                        <span class="sr-only">(current)</span></a>
                        <a href="/register.jsp" class="dropdown-toggle" data-toggle="dropdown"
                           role="button" aria-haspopup="true" aria-expanded="false">注册
                            <!--向下箭头-->
                        </a>
                    </li>
                    <li class="dropdown">
                        <a href="/login.jsp" class="dropdown-toggle" data-toggle="dropdown"
                           role="button" aria-haspopup="true" aria-expanded="false">登陆
                            <!--向下箭头-->
                        </a>
                    </li>
                    <li class="dropdown">
                        <a href="/images.jsp" class="dropdown-toggle" data-toggle="dropdown"
                           role="button" aria-haspopup="true" aria-expanded="false">壁纸
                            <!--向下箭头-->
                            <span class="caret"></span></a>
                        <!--下拉项-->
                        <ul class="dropdown-menu">
                            <li><a href="#">风景</a></li>
                            <li><a href="#">美女</a></li>
                            <!--divider表示一条线-->
                        </ul>
                    </li>
                    <li class="dropdown">
                        <a href="/books.jsp" class="dropdown-toggle" data-toggle="dropdown"
                           role="button" aria-haspopup="true" aria-expanded="false">书籍
                            <!--向下箭头-->
                            <span class="caret"></span></a>
                        <!--下拉项-->
                        <ul class="dropdown-menu">
                            <li><a href="#">编程</a></li>
                            <li><a href="#">名著</a></li>
                            <!--divider表示一条线-->
                        </ul>
                    </li>

                    </li>
                    <!--dropdown表示下拉菜单-->
                    <li class="dropdown">
                        <a href="/movies.jsp" class="dropdown-toggle" data-toggle="dropdown"
                           role="button" aria-haspopup="true" aria-expanded="false">影视
                            <!--向下箭头-->
                            <span class="caret"></span></a>
                        <!--下拉项-->
                        <ul class="dropdown-menu">
                            <li><a href="#">电影</a></li>
                            <li><a href="#">动漫</a></li>
                            <!--divider表示一条线-->
                        </ul>
                    </li>


                </ul>
                <!--navbar-right表示form表单位于右侧-->
                <form class="navbar-form navbar-right">
                    <div class="form-group">
                        <input type="search" class="form-control" placeholder="关键字">
                    </div>
                    <button type="submit" class="btn btn-default">搜索</button>
                </form>
            </div><!-- /.navbar-collapse -->
        </div><!-- /.container-fluid -->
    </nav>
</div>

<br><br><br><br><br><br>

<div align="center" style="font-size: 17px">
    <figure>
        <div><a href="/login.jsp" target="_blank">
            <span class="face">登陆</span>
            <span class="face">Login</span></a>
        </div>
    </figure>
    <br>

    <figure>
        <div><a href="register.jsp" target="_blank">
            <span class="face">注册</span>
            <span class="face">Register</span></a>
        </div>
    </figure>
    <br>

    <figure>
        <div><a href="https://music.163.com/#/discover/toplist" target="_blank">
            <span class="face">在线音乐</span>
            <span class="face">Music</span></a>
        </div>
    </figure>
    <br>

    <figure>
        <div><a href="#" target="_blank">
            <span class="face">软件下载</span>
            <span class="face">Software</span></a>
        </div>
    </figure>
    <br>

    <figure>
        <div><a href="success.html" target="_blank">
            <span class="face">网站主页</span>
            <span class="face">Homepage</span></a>
        </div>
    </figure>
    <br>

    <div style="color: green">
        <span class="face">网站当前在线</span>
        <span class="face">${applicationScope.loginNumber}人</span></a>
    </div>


</div>


<br><br>
<br><br>
<br><br>
<br><br>
<br><br><br><br><br>
<div class="footer" align="center" style="font-size: 16px">
    <div class="foot_link">
        <a href="#">关于作者</a>
        <span>|</span>
        <a href="#">联系作者</a>
        <span>|</span>
        <a href="#">招聘合作</a>
        <span>|</span>
        <a href="#">友情链接</a>
    </div>
    <p>CopyRight © 2021 上海菜鸟程序员研学中心 All Rights Reserved</p>
    <p>电话:010-5211314 沪ICP备666888号</p>
</div>

<script src="js/canvas-nest.js"></script>
<!-- 2. 导入 jQuery 框架 -->
<script src="js/jquery-3.2.1.min.js"></script>
<!-- 3. 导入bootstrap框架 -->
<script src="js/bootstrap.min.js"></script>
</body>
</html>
register.jsp
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>注册页面</title>

    <link href="css/bootstrap.min.css" rel="stylesheet">
    <link href="css/login.css" rel="stylesheet">
    <script src="js/jquery.js"></script>
    <script src="js/bootstrap.js"></script>
</head><%--
  Created by IntelliJ IDEA.
  User: cfl
  Date: 2021/05/01
  Time: 15:32
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>注册账户</title>

    <link href="css/bootstrap.min.css" rel="stylesheet">
    <link href="css/login.css" rel="stylesheet">
    <script src="js/jquery.js"></script>
    <script src="js/bootstrap.js"></script>
</head>
<body>
<div align="left">
    <a href="index.jsp" class="btn btn-success">首页</a>
</div>
<div class="container text-center">
    <form class="form-signin" action="/registerServlet" method="post">
        <h3 class="form-signin-heading">注册</h3>
        <input type="text"  name="username" class="form-control" placeholder="用户名" required autofocus>
        <br>
        <input type="password"  name="password" class="form-control" placeholder="密码" required>
        <br>

        <button class="btn btn-lg btn-primary btn-block" type="submit">注册</button>
    </form>

    <font color="red" size="2px">${requestScope.megRegister}</font>
</div>
</body>
</html>


<div class="container text-center">
    <form class="form-signin" action="/registerServlet" method="post">
        <h2 class="form-signin-heading">注册页面</h2>
        <input type="text"  name="username" class="form-control" placeholder="用户名" required autofocus>
        <br>
        <input type="password"  name="password" class="form-control" placeholder="密码" required>
        <br>

        <br>
        <button class="btn btn-lg btn-primary btn-block" type="submit">注册</button>
    </form>
</div>

</body>
</html>

login.jsp
<%--
  Created by IntelliJ IDEA.
  User: cfl
  Date: 2021/04/27
  Time: 23:24
  To change this template use File | Settings | File Templates.
--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>登录账户</title>

    <link href="css/bootstrap.min.css" rel="stylesheet">
    <link href="css/login.css" rel="stylesheet">
    <script src="js/jquery.js"></script>
    <script src="js/bootstrap.js"></script>
    <style type="text/css">
        #cookie {
            font-size: 2px;
        }

        #code {
            border-collapse: collapse;
        }

        #code #codeImg {
            padding: 1px 8px;
        }
    </style>

</head>
<body>
<div align="left">
    <a href="index.jsp" class="btn btn-success">首页</a>
    <a href="register.jsp" class="btn btn-info">注册</a>
</div>
<div class="container text-center">
    <form class="form-signin" action="${pageContext.request.contextPath}/loginServlet" method="post">
        <h3 class="form-signin-heading">登录</h3>
        <input type="text" name="username" value="${cookie.username.value}" class="form-control" placeholder="用户名"
               required autofocus>
        <br>
        <input type="password" name="password" value="${cookie.password.value}" class="form-control" placeholder="密码"
               required>
        <table id="code" align="left">
            <tr>
                <td>
                    <input type="text" name="checkCode"
                           class="form-control" placeholder="验证码" required>
                </td>
                <td id="codeImg">
                    <!--页面一加载就向checkCodeServlet发送请求-->
                    <img src="/checkCodeServlet" alt="验证码"
                         style="cursor: pointer" onclick="changeCheckCode(this);">
                </td>
            </tr>
        </table>
        <div align="left">
            <table>
                <tr>
                    <td>
                        <span id="cookie"><b>记住用户名密码:</b></span>
                    </td>
                    <td>
                        <input type="checkbox" name="checkbox">
                    </td>
                </tr>
            </table>
        </div>
        <br>
        <button class="btn btn-lg btn-primary btn-block" type="submit">登录</button>
    </form>

    <font color="red" size="2px">${requestScope.messageUser}</font>
    <font color="red" size="2px">${requestScope.messageCode}</font>
</div>

<script type="text/javascript">
    function changeCheckCode(obj) {
        obj.src = "/checkCodeServlet?" + new Date().getTime();
    }
</script>

</body>
</html>

success.html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
    <meta name="description" content="">
    <meta name="author" content="">
<!--    nk rel="icon" href="../../favicon.ico">-->

    <title>Beauty下载网</title>
    <!-- Bootstrap core CSS -->
    <link href="css/bootstrap.min.css" rel="stylesheet">
    <link href="css/dashboard.css" rel="stylesheet">
</head>

<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
    <div class="container-fluid">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#">Beauty下载网</a>
        </div>
        <div id="navbar" class="navbar-collapse collapse">
            <ul class="nav navbar-nav navbar-right">
                <li class="active"><a href="#">收藏<span class="sr-only">(current)</span></a></li>
                <li><a href="#">设置</a></li>
                <li><a href="#">帮助</a></li>
                <li><a href="/LogoutServlet">退出</a></li>
            </ul>
            <form class="navbar-form navbar-right">
                <input type="text" class="form-control" placeholder="Search...">
            </form>
        </div>
    </div>
</nav>

<div class="container-fluid">
    <div class="row">
        <div class="col-sm-3 col-md-2 sidebar">
            <ul class="nav nav-sidebar">
                <li class="active"><a href="#">用户中心<span class="sr-only">(current)</span></a></li>
                <li><a href="/images.jsp">壁纸下载</a></li>
                <li><a href="/queryBooksServlet">书籍下载</a></li>
                <li><a href="/movies.jsp">影视下载</a></li>
            </ul>
        </div>

        <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">

            <h3 class="page-header">个人信息</h3>

            <div class="row placeholders">
                <div class="col-xs-6 col-sm-3 placeholder">
                    <img src="img/demo.jpg" width="100" height="100" class="img-responsive" alt="Generic placeholder thumbnail">
                    <h5>username</h5>
<!--                    <span class="text-muted">Something else</span>-->
                </div>
            </div>

<!--            <h2 class="sub-header">Section title</h2>-->
        </div>
    </div>
</div>

<script src="js/jquery.js"></script>
<script>window.jQuery || document.write('<script src="js/jquery.js"><\/script>')</script>
<script src="js/bootstrap.js"></script>

</body>
</html>

books.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--
  Created by IntelliJ IDEA.
  User: cfl
  Date: 2021/04/30
  Time: 16:29
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <!--使用微软最新的浏览器Edge的内核来解析当前的HTML文件,最新的浏览器都支持 -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!--
    响应式布局参数:
     viewport: 视口的参数
     width=device-width:默认的网页宽度是设备的宽度
     initial-scale=1: 初始的缩放比1:1
     -->
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->

    <!-- 1. 导入bootstrap的全局css样式  -->
    <link href="css/bootstrap.min.css" rel="stylesheet">
    <title>书籍网</title>
    <style>
        tr{
            height: 40px;
            /*行文本居中*/
            text-align: center;
        }
        table{
            /*table表格在页面居中显示*/
            margin: auto;
            /*
                border-collapse 属性设置表格的边框是否被合并为一个单一的边框
                属性值是collapse:如果可能,边框会合并为一个单一的边框。会忽略 border-spacing 属性
            */
            border-collapse: collapse;
        }
    </style>
</head>
<body>
<div align="left">
    <a href="success.html" class="btn btn-success">首页</a>
</div>

<table border="1px" cellspacing="0" width="600">
    <caption><h2>书籍列表</h2></caption>
    <tr>
        <th>编号</th>
        <th>书籍名称</th>
        <th>作者</th>
        <th>价格</th>
    </tr>
    <%--使用jstl结合EL从后台queryAllServlet的reques域对象中取出数据--%>
    <c:forEach items="${requestScope.bookList}" var="book">
        <tr>
          <td>${book.id}</td>
          <td>${book.name}</td>
          <td>${book.author}</td>
          <td>${book.price}</td>
        </tr>
    </c:forEach>
</table>
<div align="center">
    <h3>书籍下载</h3>
    <a href="/downloadBookServlet?filename=红楼梦.txt">红楼梦</a><br>
    <a href="/downloadBookServlet?filename=西游记.txt">西游记</a><br>
    <a href="/downloadBookServlet?filename=JavaEE-SSH实战开发.txt">JavaEE-SSH实战开发</a><br>
    <a href="/downloadBookServlet?filename=三国演义.txt">三国演义</a><br>
</div>


<!-- 2. 导入 jQuery 框架 -->
<script src="js/jquery-3.2.1.min.js"></script>
<!-- 3. 导入bootstrap框架 -->
<script src="js/bootstrap.min.js"></script>
</body>
</html>

images.jsp
<%--
  Created by IntelliJ IDEA.
  User: cfl
  Date: 2021/05/02
  Time: 19:43
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <!--使用微软最新的浏览器Edge的内核来解析当前的HTML文件,最新的浏览器都支持 -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!--
    响应式布局参数:
     viewport: 视口的参数
     width=device-width:默认的网页宽度是设备的宽度
     initial-scale=1: 初始的缩放比1:1
     -->
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->

    <!-- 1. 导入bootstrap的全局css样式  -->
    <link href="css/bootstrap.min.css" rel="stylesheet">
    <title>壁纸下载</title>
</head>
<body>

<div align="left">
    <a href="success.html" class="btn btn-success">首页</a>
</div>

<br>

<h4 class="sub-header">壁纸下载</h4>
<hr>
<div align="center">
    <a href="/downloadImageServlet?filename=刘亦菲.jpg">刘亦菲.jpg</a><br>
    <a href="/downloadImageServlet?filename=壁纸.jpg">壁纸.jpg</a><br>
    <a href="/downloadImageServlet?filename=小兔子.jpg">小兔子.jpg</a><br>
    <a href="/downloadImageServlet?filename=01.jpg">01.jpg</a><br>
    <a href="/downloadImageServlet?filename=02.txt">02.txt</a><br>
    <a href="/downloadImageServlet?filename=03.zip">03.zip</a><br>
</div>

<!-- 2. 导入 jQuery 框架 -->
<script src="js/jquery-3.2.1.min.js"></script>
<!-- 3. 导入bootstrap框架 -->
<script src="js/bootstrap.min.js"></script>
</body>
</html>

movies.jsp

<%--
  Created by IntelliJ IDEA.
  User: cfl
  Date: 2021/05/01
  Time: 18:22
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>影视</title>
</head>
<body>
<a href="success.html">首页</a>
<h2>尚在开发中。。。。。</h2>
</body>
</html>
3、后端业务代码实现
1)test测试连接数据库
public static void main(String[] args) {
    //获取session
    SqlSession sqlSession = SessionFactoryUtil.getSqlSession();
    //获取接口对象
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    //调用方法查询数据
    List<User> list = mapper.queryTest();
    list.forEach(System.out::println);
    sqlSession.close();
}
2)util工具
SessionFactoryUtil 工具类
/**
 *  会话工厂工具类
        1. 通过静态方法得到一个工厂对象
        2. 通过静态方法得到会话对象
 *
 */
public class SessionFactoryUtil {
    //声明一个工厂对象
    private static SqlSessionFactory factory;
    //在静态代码块中创建会话工厂
    static {
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //得到输入流
        try(InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");) {
            factory = builder.build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     静态方法得到会话工厂
     */
    public static SqlSessionFactory getSessionFactory() {
        return factory;
    }
    /**
     得到会话对象
     */
    public static SqlSession getSqlSession() {
        return factory.openSession();
    }

}
Filter过滤器未登录拦截访问

LoginFilter

@WebFilter({"/success.html", "/books.jsp", "/movies.jsp", "/images.jsp"})
public class LoginFilter implements Filter {
    @Override
    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        System.out.println("-----过滤登陆访问-----");
        User user = (User) request.getSession().getAttribute("user");

        if (user == null) {
         /*   request.getRequestDispatcher("/login.jsp")
                    .forward(request, response);*/
            response.sendRedirect("/login.jsp");
        }
        chain.doFilter(request, response);
    }

    @Override
    public void init(FilterConfig config) throws ServletException {
    }
}

Listener监听器统计网站在线人数

InitNumberListener

@WebListener
public class InitNumberListener implements ServletContextListener,
        HttpSessionListener, HttpSessionAttributeListener {

    // Public constructor is required by servlet spec
    public InitNumberListener() {
    }

    // -------------------------------------------------------
    // ServletContextListener implementation
    // -------------------------------------------------------
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("-----初始化上下文对象-----");
        ServletContext servletContext = sce.getServletContext();
        servletContext.setAttribute("loginNumber", 0);
    }

    public void contextDestroyed(ServletContextEvent sce) {

    }

    // -------------------------------------------------------
    // HttpSessionListener implementation
    // -------------------------------------------------------
    public void sessionCreated(HttpSessionEvent se) {
        /* Session is created. */
    }

    public void sessionDestroyed(HttpSessionEvent se) {
        /* Session is destroyed. */
    }

    // -------------------------------------------------------
    // HttpSessionAttributeListener implementation
    // -------------------------------------------------------

    public void attributeAdded(HttpSessionBindingEvent sbe) {

    }

    public void attributeRemoved(HttpSessionBindingEvent sbe) {

    }

    public void attributeReplaced(HttpSessionBindingEvent sbe) {
      /* This method is invoked when an attribute
         is replaced in a session.
      */
    }
}

LoginNumChangeListener

package cn.itcast.sh.login.listener;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.*;

@WebListener
public class LoginNumChangeListener implements ServletContextListener,
        HttpSessionListener, HttpSessionAttributeListener {

    // Public constructor is required by servlet spec
    public LoginNumChangeListener() {
    }

    // -------------------------------------------------------
    // ServletContextListener implementation
    // -------------------------------------------------------
    public void contextInitialized(ServletContextEvent sce) {


    }

    public void contextDestroyed(ServletContextEvent sce) {

    }

    // -------------------------------------------------------
    // HttpSessionListener implementation
    // -------------------------------------------------------
    public void sessionCreated(HttpSessionEvent se) {
        /* Session is created. */
        HttpSession session = se.getSession();

        ServletContext servletContext = session.getServletContext();
        Integer loginNumber = (Integer) servletContext.getAttribute("loginNumber");

        servletContext.setAttribute("loginNumber", loginNumber+1);


    }

    public void sessionDestroyed(HttpSessionEvent se) {
        /* Session is destroyed. */
        HttpSession session = se.getSession();

        ServletContext servletContext = session.getServletContext();
        Integer loginNumber = (Integer) servletContext.getAttribute("loginNumber");

        servletContext.setAttribute("loginNumber",loginNumber-1);
    }

    // -------------------------------------------------------
    // HttpSessionAttributeListener implementation
    // -------------------------------------------------------

    public void attributeAdded(HttpSessionBindingEvent sbe) {

    }

    public void attributeRemoved(HttpSessionBindingEvent sbe) {

    }

    public void attributeReplaced(HttpSessionBindingEvent sbe) {
      /* This method is invoked when an attribute
         is replaced in a session.
      */
    }
}

3)pojo实体类
User类
public class User {
    private Integer id;
    private String username;
    private String password;

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
Book类
public class Book {
    private int id;    //编号
    private String name;  //书名
    private String author;  //作者
    private double price;  //价格
    //setter/getter

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", author='" + author + '\'' +
                ", price=" + price +
                '}';
    }
}

4)dao层
UserMapper映射接口
public interface UserMapper {
    @Select("select * from user")
    List<User> queryTest();

    @Select("select * from user where username=#{username} and password=#{password}")
    User queryUserByUserNameAndPassword(User user);

    @Insert("insert into user values(null,#{username},#{password})")
    void register(User user);

}
BookMapper映射接口
public interface BookMapper {

    @Select("select * from book")
    List<Book> queryAllBooks();
}
5)service层
UserService业务处理
public class UserService {

    // 注册业务逻辑处理
    public boolean register(User user){
        SqlSession sqlSession = null;
        try {
            sqlSession = SessionFactoryUtils.getSqlSession();
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            User us = userMapper.queryUserByUserNameAndPassword(user);
            if(us == null){
                userMapper.register(user);
                sqlSession.commit();
                return true;
            }else {
                return false;
            }
        }finally {
            assert sqlSession != null;
            sqlSession.close();
        }

    }


    // 登陆业务逻辑处理
    public boolean login(User user) {
        //处理业务逻辑
        //4. 通过会话工厂得到会话对象
        SqlSession session = SessionFactoryUtils.getSqlSession();
        //5. 会话对象得到UserMapper接口的代理对象
        UserMapper userMapper = session.getMapper(UserMapper.class);
        //接口对象调用方法
        User loginUser = userMapper.queryUserByUserNameAndPassword(user);

        session.close();
        // 方式一
/*        if (null == loginUser) {
            return  false;
        }else {
            return  true;
        }*/
        // 方式二
        // return loginUser == null ? false : true;

        return  loginUser != null;
    }


}

BookService业务处理
public class BookService {

    public List<Book> queryAllBooks(){

        SqlSession sqlSession = SessionFactoryUtils.getSqlSession();
        BookMapper mapper = sqlSession.getMapper(BookMapper.class);

        List<Book> booksList = mapper.queryAllBooks();
        sqlSession.close();
        return booksList;
    }
}
6)web层
CheckCodeServlet(生成验证码)
@WebServlet("/checkCodeServlet")
public class CheckCodeServlet 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 {
        System.out.println("----请求验证码----");
        /*
    说明:验证码是一张图片,而这个图片中的内容是使用代码生成的。
    分析和步骤:
    1)创建一个可以存放图片的缓冲区BufferedImage作为画布;
    2)通过画布获取到针对这个画布的画笔;
    3)修改画布的背景颜色为白色;
    4)设置画布的边框,画边框的时候需要注意下,如果这里写画布的宽width和高height ,就会超出画布就会看不见,所以width和height 分别-1;
    5)创建一个获取随机数的对象;
    6)给画布上写数据;
    7)给画布上画干扰线;
    8)需要把画布中的内容响应给浏览器;ImageIO.write(bi,"JPG",response.getOutputStream());
 */
        //定义画布的宽和高
        int width=120;
        int height=30;
        //创建一个可以存放图片的缓冲区,作为画布
        //BufferedImage.TYPE_INT_RGB 表示生成图片的类型
        BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        //通过画布获取到针对这个画布的画笔
        Graphics g = bi.getGraphics();
        //修改画布的背景颜色  每次使用画笔的时候都得给画笔指定颜色
        g.setColor(Color.WHITE);
        //填充画布
        g.fillRect(0, 0, width, height);
        //设置画布的边框
        //给画笔指定颜色
        g.setColor(Color.RED);
        //给画布画边框 如果这里写width height 就会超过画布,因为边框也会占一个像素,所以这里宽和高都需要-1
        g.drawRect(0, 0, width-1, height-1);
        //创建一个获取随机数的对象
        Random r = new Random();
        //给画布上画干扰线
        //循环控制画多条线
        for(int i=1;i<=3;i++)
        {
            //设置画笔的颜色
            g.setColor(new Color(r.nextInt(255),r.nextInt(255),r.nextInt(255)));
            //向画布上画干扰线
            //drawLine(x1, y1, x2, y2) 这里四个参数是因为两个点画成一条线
            g.drawLine(r.nextInt(width), r.nextInt(height), r.nextInt(width), r.nextInt(height));
        }
        //定义数据准备向画布中写数据
        String data="abcdefghigklmnpqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ123456789";
        //创建字符串缓冲区
        StringBuilder sb = new StringBuilder();

        //循环控制画四个字符
        for (int i = 1; i <=4; i++) {
            //设置画笔的颜色 Color.BLUE这里的颜色固定了,只能是蓝色,我们可以让颜色随机变化
//			g.setColor(Color.BLUE);
            g.setColor(new Color(r.nextInt(255),r.nextInt(255),r.nextInt(255)));
            //设置字体  Font.ITALIC表示斜体
            g.setFont(new Font("宋体", Font.ITALIC, 20));
            //给画布上写内容 20表示从x轴的位置开始书写 25表示y轴位置开始书写
//			g.drawString("哈哈哈哈", 20, 25);
            /*
             * data.charAt()表示根据函数的参数进行查找字符
             * data.length()表示字符串的长度
             * r.nextInt()表示生成随机数,但是随机数的范围在0~data字符串的长度
             */
            String str = data.charAt(r.nextInt(data.length())) + "";
            g.drawString(str, 20*i, 25);
            //将验证码内容拼接到字符串缓冲区中
            sb.append(str);
        }

        //  验证码保存到session中
        request.getSession().setAttribute("serverCheckCode",sb.toString());
        //将生成的验证码图片响应给浏览器
        ImageIO.write(bi,"JPG",response.getOutputStream());
    }

}
RegisterServlet实现类
@WebServlet("/registerServlet")
public class RegisterServlet 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 {
        System.out.println("---开始注册---");
        //解决请求乱码
        request.setCharacterEncoding("UTF-8");
        //获取用户名
        String username = request.getParameter("username");
        System.out.println(username);
        //获取密码
        String password = request.getParameter("password");
        System.out.println(password);
        //创建业务层对象
        UserService userService = new UserService();
        //使用业务层对象调用方法注册
        User user = new User();
        user.setUsername(username);
        user.setPassword(password);
        boolean boo = userService.register(user);
        //对boo结果进行判断
        if (boo) {
            //注册成功,跳转到登录页面,不需要携带数据
            response.sendRedirect("/login.html");
        } else {
            //注册失败,跳转到注册失败页面或者输出一句话
//            System.out.println("注册失败");
            response.sendRedirect("/register.html");
        }
    }
}

LoginServlet实现类
@WebServlet("/loginServlet")
public class LoginServlet 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 {
        System.out.println("登陆校验请求来了-------");
        System.out.println(request.getMethod());
        // 解码请求数据
        request.setCharacterEncoding("utf-8");
        // 获取用户输入的 用户名和密码
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        if(username == null && password == null){
            response.setContentType("text/html;charset=utf-8");
            response.getWriter().println("<h2>请登录后再进入本网站!<h2>");
        }

        // 进行验证码校验
        String checkCode = request.getParameter("checkCode");  // 获取用户输入的验证码
        System.out.println(checkCode);
        String serCode = (String) request.getSession().getAttribute("serverCheckCode"); // 获取服务器随机生成的验证码
        System.out.println(serCode);
        // 判断浏览器 验证码 与 服务器生成的验证码是否 相匹配 -》true 进行 用户验证
        if(checkCode.equalsIgnoreCase(serCode)){
            // 创建用户实体类
            User user = new User();
            user.setUsername(username);
            user.setPassword(password);
            // 调用用户名密码校验服务 进行校验
            UserService userService = new UserService();
            boolean loginResult = userService.login(user);
            String resultStr = loginResult ? "用户登陆成功!" : "用户登陆失败!";
            request.setAttribute("message", resultStr);
            // 结果为true 用户存在 判断cookie 进入网站首页
            // 否则 用户不存在 进入注册页面
            if(loginResult){
                // 判断用户是否记住cookie
                String check = request.getParameter("checkbox");
                System.out.println(check);
                if("on".equals(check)){
                    Cookie cookieUserName = new Cookie("username", username);
                    Cookie cookiePassword = new Cookie("password", password);
                    response.addCookie(cookieUserName);
                    response.addCookie(cookiePassword);
                }
                /*request.getRequestDispatcher("/success.html")
                        .forward(request,response);*/
                response.sendRedirect("/success.html");
            }else {
                /* request.getRequestDispatcher("/error.html")
                        .forward(request,response);*/
                System.out.println(request.getAttribute("message"));
                request.getRequestDispatcher("/loginErrorServlet")
                        .forward(request,response);
            }
        }else {
            request.getRequestDispatcher("/login.html")
                    .forward(request,response);
        }
    }
}

LogoutServlet实现类
@WebServlet("/LogoutServlet")
public class LogoutServlet 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 {
        System.out.println("销毁session对象。。。。");
        HttpSession session = request.getSession();

        Cookie cookieUsername = new Cookie("username", "");
        Cookie cookiePwd = new Cookie("password", "");
        //2.设置cookie的存活时间是0
        cookieUsername.setMaxAge(0);
        cookiePwd.setMaxAge(0);
        //3.设置cookie的有效路径和原来的有效路径一致
        cookieUsername.setPath("/");
        cookiePwd.setPath("/");
        //4.响应给浏览器
        response.addCookie(cookieUsername);
        response.addCookie(cookiePwd);

        // 销毁session
        String username = request.getParameter("username");
        session.invalidate();
        System.out.println(username+"用户退出。。。。");

        response.sendRedirect("/index.jsp");
    }
}

DownloadImageServlet实现类

@WebServlet("/downloadImageServlet")
public class DownloadImageServlet 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 {
        /*
        * String getRealPath(String path)
        * 返回资源文件在服务器文件系统上的真实路径(文件的绝对路径)
        * getMimeType(fileName)获取服务器中文件类型
        *
        ServletContext servletContext = getServletContext();
        System.out.println(servletContext);

        String mimeType = servletContext.getMimeType("壁纸.jpg");
        System.out.println(mimeType); // image/jpeg
        String realPath = servletContext.getRealPath("WEB-INF/壁纸.jpg");*/

        System.out.println("---------开始接收客户端【下载文件】--------------");
        // 1. 获取客户端下载的文件名
        String filename = request.getParameter("filename");
        System.out.println("filename:"+filename);

        // 2. 获取servletContext上文对象
        ServletContext servletContext = getServletContext();

        // 3. 获取文件的真实路径
        String realPath = servletContext.getRealPath("WEB-INF/"+filename);
        System.out.println("realPath:"+realPath);

        // 4. 创建下载文件的File对象
        File file = new File(realPath);
        System.out.println("文件对象:"+file);

        // 5. 判断文件是否存在
        if(file.exists()){
            // 6. 存在,开始下载  把项目文件写入到浏览器通道 获取2个头 + 2 个流 【核心】
            // 2个响应头 信息
            // 获取 响应的 【文件类型】
            String mimeType = servletContext.getMimeType(filename);
            System.out.println("文件传输类型mimeType:"+mimeType); // image/jpeg
            // 设置 响应的【浏览器文件类型】
            response.setContentType(mimeType);

            // 设置 浏览器以 【附件形式进行下载】, 如果文件名包含中文字符,首先进行编码
            String encodeFilename = URLEncoder.encode(filename, "UTF-8");
            response.setHeader("Content-Disposition","attachment;filename="+encodeFilename);

            // 2个流【本地输入流】 【响应输出流】
            // 创建字节输入流对象 -> 关联服务器文件 读取文件
            FileInputStream fis = new FileInputStream(file);
            // 获取响应对象的 输出流,向浏览器写入
            ServletOutputStream os = response.getOutputStream();

            // 7. 读取并写入文件数据 -》 给浏览器做出响应
            byte[] buf = new byte[1024];
            int len = 0;
            while ((len=fis.read(buf))!= -1){
                os.write(buf, 0, len);
            }

            // 8. 释放资源
            fis.close();
            os.close();
            System.out.println("传送资源结束");
        }else {
            System.out.println(filename+"不存在");
        }

    }
}

QueryBooksServlet实现类


@WebServlet("/queryBooksServlet")
public class QueryBooksServlet 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 {
        BookService bookService = new BookService();

        List<Book> books = bookService.queryAllBooks();

        request.setAttribute("bookList", books);

        request.getRequestDispatcher("/books.jsp")
                .forward(request,response);
    }
}

下面两个请求处理可自行设计呀!

DownloadBookServlet
@WebServlet("/downloadBookServlet")
public class DownloadBookServlet 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 {

    }
}

DownloadMovieServlet
@WebServlet("/downloadMovieServlet")
public class DownloadMovieServlet 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 {

    }
}

读到此处的小哥哥小姐姐们留个赞吧!加油
ღ( ´・ᴗ・` )比心

在这里插入图片描述

  • 8
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

编程小栈

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

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

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

打赏作者

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

抵扣说明:

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

余额充值