博客系统的前后端实现

news2024/9/24 7:22:41

前面的学习中, 我们基于 HTML, CSS, JavaScript 实现了一个简单的博客系统的页面.
接下来我们基于博客系统页面来实现一个带服务器版本的博客程序.

1.准备工作

1.创建项目

2.引入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>blog_system</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.15.2</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>

    </dependencies>

</project>

3.创建必要的目录
在这里插入图片描述

2.数据库设计

设计表结构,有几个表,每个表里面都有啥~~

要存储的主要是两个部分

1.博客数据

blog 博客id,标题,正文,userid,发布时间

2.用户存储

user userid,username,password[用户的头像,用户的github…暂时不搞]

--一般对于建表的SQL都会单独搞一个.sql文件来保存.
--后续程序可能需要在不同的主机上部署,部署的时候就需要在对应的主机上把数据库也给创建好.
--把建表sql保存好,方便在不同的机器上进行建库建表

create database if not exists blog_system;

use blog_system;

drop table if exists blog;
create table blog
(
    blogId   int primary key auto_increment, -- 博客 id
    title    varchar(128),                   -- 博客标题
    content  varchar(4096),                  -- 博客正文
    userId   int,                            -- 博客作者 id
    postTime datetime
);

drop table if exists user;
-- 一般都会在建表的时候删一下,删一下的目的是为了清空之前残留的数据~~
create table user(
    userId int primary key auto_increment,
    username varchar(128),
    password varchar(128)
);

insert into user values(null,'zhangsan','123'),(null,'lisi','123');

3.封装数据库操作

JDBC

1.封装数据库的连接操作

一个经典的 web 项目结构:MVC
M —> Model(和数据相关的部分)
V —> View(和界面相关的部分)
C —> COntroller(联系界面和数据之间的业务逻辑)

在这里插入图片描述
懒汉单例模式
真正使用的时候,才创建实例
如果不用,就不创建了

package model;

import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;

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

/**
 * @projectName: blog_system
 * @package: model
 * @className: DBUtil
 * @author: 王嘉辉
 * @description:
 * @date: 2023/10/23 14:46
 * @version: 1.0
 */
/*
* 通过这个类,封装数据库的连接操作
* */
public class DBUtil {
    //这个类中要提供DataSource,DataSource 对于一个项目来说,有一个就行了(单例)
    private static DataSource dataSource = null;

    private static DataSource getDataSource() {
        if (dataSource == null) {
            synchronized (DBUtil.class) {
                if (dataSource == null) {
                    dataSource = new MysqlDataSource();
                    ((MysqlDataSource) dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/blog_system?characterEncoding=utf8&useSSL=false");
                    ((MysqlDataSource) dataSource).setUser("root");
                    ((MysqlDataSource) dataSource).setPassword("0126");
                }
            }
        }
        return dataSource;
    }

    public static Connection getConnection() throws SQLException {
        return getDataSource().getConnection();
    }
    public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet) {
        try {
            if (resultSet != null) {
                resultSet.close();
            }
            if (statement != null) {
                statement.close();
            }
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

2.创建两个表对应的实体类

package model;

/**
 * @projectName: blog_system
 * @package: model
 * @className: User
 * @author: 王嘉辉
 * @description:
 * @date: 2023/10/23 15:05
 * @version: 1.0
 */
/*
 * 这个类表示数据库中User表的内容
 * 每个 User 对象,就对应 User 表中的一条记录
 * */
public class User {
    private int userId;
    private String username;
    private String password;

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    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;
    }
}

package model;

import java.sql.Timestamp;

/**
 * @projectName: blog_system
 * @package: model
 * @className: Blog
 * @author: 王嘉辉
 * @description:
 * @date: 2023/10/23 15:05
 * @version: 1.0
 */

/*
* 这个类表示数据库中blog表的内容
* 每个 blog 对象,就对应 blog 表中的一条记录
* */
public class Blog {
    private int blogId;
    private String title;
    private String content;
    private int userId;
    private Timestamp postTime;

    public int getBlogId() {
        return blogId;
    }

    public void setBlogId(int blogId) {
        this.blogId = blogId;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public Timestamp getPostTime() {
        return postTime;
    }

    public void setPostTime(Timestamp postTime) {
        this.postTime = postTime;
    }
}

3.封装一些必要的增删改查操作

在这里插入图片描述

在这里插入图片描述
DAO —> Data Access Object
通过这样的对象来访问数据

package model;


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

/**
 * @projectName: blog_system
 * @package: model
 * @className: BlogDao
 * @author: 王嘉辉
 * @description:
 * @date: 2023/10/23 15:14
 * @version: 1.0
 */
public class BlogDao {
    // 把一个博客对象插入到 blog 表中
    public void insert(Blog blog) {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            //建立连接
            connection = DBUtil.getConnection();
            //构造sql语句
            String sql = "insert into blog values(null, ?, ?, ?, ?)";
            statement = connection.prepareStatement(sql);
            statement.setString(1, blog.getTitle());
            statement.setString(2, blog.getContent());
            statement.setInt(3, blog.getUserId());
            statement.setTimestamp(4, blog.getPostTime());
            //执行sql
            statement.executeUpdate();
        }catch (SQLException e){
            e.printStackTrace();
        }finally {
            //关闭
            DBUtil.close(connection, statement, null);
        }
    }
    // 从 blog 表中查找到所有的 blog 对象
    public List<Blog> selectAll() {
        List<Blog> blogs = new ArrayList<>();
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
       try {
           connection = DBUtil.getConnection();
           String sql = "select * from blog";
           statement = connection.prepareStatement(sql);

           while (resultSet.next()) {
               Blog blog = new Blog();
               blog.setBlogId(resultSet.getInt("blogId"));
               blog.setTitle(resultSet.getString("title"));
               blog.setContent(resultSet.getString("content"));
               blog.setUserId(resultSet.getInt("userId"));
               blog.setPostTime(resultSet.getTimestamp("postTime"));
               blogs.add(blog);
           }
       } catch (SQLException e) {
           e.printStackTrace();
       }finally {
           DBUtil.close(connection, statement, resultSet);
       }
       return blogs;
    }
    // 从 blog 表中查找到指定的 blog 对象
    public Blog selectOne(int blogId) {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            connection = DBUtil.getConnection();
            String sql = "select * from blog where blogId = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1, blogId);
            resultSet = statement.executeQuery();

            if (resultSet.next()) {
                Blog blog = new Blog();
                blog.setBlogId(resultSet.getInt("blogId"));
                blog.setTitle(resultSet.getString("title"));
                blog.setContent(resultSet.getString("content"));
                blog.setUserId(resultSet.getInt("userId"));
                blog.setPostTime(resultSet.getTimestamp("postTime"));
                return blog;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection, statement, resultSet);
        }
        return null;
    }
    // 删除指定的 blog 对象
    public void delete(int blogId) {
        Connection connection = null;
        PreparedStatement statement = null;
        try {
            connection = DBUtil.getConnection();
            String sql = "delete from blog where blogId = ?";
            statement.setInt(1, blogId);
            statement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DBUtil.close(connection, statement, null);
        }
    }
}

package model;


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

/**
 * @projectName: blog_system
 * @package: model
 * @className: BlogDao
 * @author: 王嘉辉
 * @description:
 * @date: 2023/10/23 15:14
 * @version: 1.0
 */
public class BlogDao {
    // 把一个博客对象插入到 blog 表中
    public void insert(Blog blog) {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            //建立连接
            connection = DBUtil.getConnection();
            //构造sql语句
            String sql = "insert into blog values(null, ?, ?, ?, ?)";
            statement = connection.prepareStatement(sql);
            statement.setString(1, blog.getTitle());
            statement.setString(2, blog.getContent());
            statement.setInt(3, blog.getUserId());
            statement.setTimestamp(4, blog.getPostTime());
            //执行sql
            statement.executeUpdate();
        }catch (SQLException e){
            e.printStackTrace();
        }finally {
            //关闭
            DBUtil.close(connection, statement, null);
        }
    }
    // 从 blog 表中查找到所有的 blog 对象
    public List<Blog> selectAll() {
        List<Blog> blogs = new ArrayList<>();
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
       try {
           connection = DBUtil.getConnection();
           String sql = "select * from blog";
           statement = connection.prepareStatement(sql);
           resultSet = statement.executeQuery();
           while (resultSet.next()) {
               Blog blog = new Blog();
               blog.setBlogId(resultSet.getInt("blogId"));
               blog.setTitle(resultSet.getString("title"));
               blog.setContent(resultSet.getString("content"));
               blog.setUserId(resultSet.getInt("userId"));
               blog.setPostTime(resultSet.getTimestamp("postTime"));
               blogs.add(blog);
           }
       } catch (SQLException e) {
           e.printStackTrace();
       }finally {
           DBUtil.close(connection, statement, resultSet);
       }
       return blogs;
    }
    // 从 blog 表中查找到指定的 blog 对象
    public Blog selectOne(int blogId) {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            connection = DBUtil.getConnection();
            String sql = "select * from blog where blogId = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1, blogId);
            resultSet = statement.executeQuery();

            if (resultSet.next()) {
                Blog blog = new Blog();
                blog.setBlogId(resultSet.getInt("blogId"));
                blog.setTitle(resultSet.getString("title"));
                blog.setContent(resultSet.getString("content"));
                blog.setUserId(resultSet.getInt("userId"));
                blog.setPostTime(resultSet.getTimestamp("postTime"));
                return blog;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection, statement, resultSet);
        }
        return null;
    }
    // 删除指定的 blog 对象
    public void delete(int blogId) {
        Connection connection = null;
        PreparedStatement statement = null;
        try {
            connection = DBUtil.getConnection();
            String sql = "delete from blog where blogId = ?";
            statement.setInt(1, blogId);
            statement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DBUtil.close(connection, statement, null);
        }
    }
}

4.前后端交互逻辑的实现

1.博客列表页

展示博客列表

1.约定前后端交互接口

请求:
GET/blog

响应:

在这里插入图片描述

2.写后端代码

创建一个Servlet来处理对应的请求

package api;

import com.fasterxml.jackson.databind.ObjectMapper;
import model.Blog;
import model.BlogDao;

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

/**
 * @projectName: blog_system
 * @package: api
 * @className: BlogServlet
 * @author: 王嘉辉
 * @description:
 * @date: 2023/10/24 11:10
 * @version: 1.0
 */
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        BlogDao blogDao = new BlogDao();
        List<Blog> blogs = blogDao.selectAll();
        String respString = objectMapper.writeValueAsString(blogs);
        resp.setContentType("application/json;charset=utf8");
        resp.getWriter().write(respString);
    }
}

3.写前端代码

构造ajax请求,按照上述约定,发送HTTP请求
此时得修改前端代码.(先把前端代码拷贝到咱们的项目中)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>博客列表页</title>

    <link rel="stylesheet" href="css/common.css">
    <link rel="stylesheet" href="css/list.css">

    <!--引入jquery-->
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>


</head>
<body>
    <!--导航栏,nav是导航栏的缩写-->
    <div class="nav">
        <!--logo-->
        <img src="image/1.jpg" alt="">
        <!---->
        <div class="title" >我的博客系统</div>
        <!--只是一个空白,用来把后面的链接挤过去(这是一个简单粗暴地写法)-->
        <div class="spacer"></div>
        <a href="list.html">主页</a>
        <a href="edit.html">写博客</a>
        <!--注销 这里的地址后面提到-->
        <a href="">注销</a>
    </div>


    <!--页面主体部分-->
    <div class="container">
        <!--左侧信息-->
        <div class="container-left">
            <!--这个div标识整个用户信息的区域-->
            <div class = "card">
                <!--用户头像-->
                <img src = "image/3.jpg" alt = "">
                <!--用户名-->
                <h3>南北</h3>
                <!-- Gitee 地址-->
                <a href="https://gitee.com/D2814413094">Gitee 地址</a>
                <!--统计信息-->
                <div class="counter">
                    <span>文章</span>
                    <span>分类</span>
                </div>
                <div class="counter">
                    <span>3</span>
                    <span>1</span>
                </div>

            </div>
        </div>
        <!--右侧信息-->
        <div class="container-right">
            <!--<div class="blog">
                <div class="title">我的第一篇博客</div>
                <div class="data">2022-1-26 20:07:11</div>
                <div class="desc">
                    清夜无尘,月色如银。酒斟时、须满十分。浮名浮利,虚苦劳神。叹隙中驹,石中火,梦中身。虽抱文章,开口谁亲。且陶陶、乐尽天真。几时归去,作个闲人。对一张琴,一壶酒,一溪云。
                </div>
                <a href="detail.html?blog=1">查看全文 &gt;&gt;</a>
            </div>-->
        </div>
    </div>

    <script>
        //通过ajax给服务器发请求,获取到所有的博客数据,并且构造到页面上
        function getBlogs() {
            $.ajax({
                type:'get',
                url:'blog',
                success:function (body) {
                    //根据返回的数据,构造出页面中对应的元素
                    let containerRight = document.querySelector('.container-right');
                    for (let blog of body) {
                        let blogDiv = document.createElement("div");
                        blogDiv.className = 'blog';
                        let titleDiv = document.createElement("div")
                        titleDiv.className = 'title';
                        titleDiv.innerHTML = blog.title;
                        let dateDiv = document.createElement("div");
                        dateDiv.className = 'data';
                        dateDiv.innerHTML = blog.postTime;
                        let descDiv = document.createElement("div");
                        descDiv.className = 'desc';
                        descDiv.innerHTML = blog.content;
                        let a = document.createElement("a");
                        a.href = 'detail.html?blogId=' + blog.blogId;
                        a.innerHTML = '查看全文 &gt;&gt;';

                        //把上述标签构造好了之后,还需要组合起来
                        blogDiv.appendChild(titleDiv);
                        blogDiv.appendChild(dateDiv);
                        blogDiv.appendChild(descDiv);
                        blogDiv.appendChild(a);
                        containerRight.appendChild(blogDiv);
                    }
                }
            });
        }
        //要记得在定义完函数以后调用函数.
        getBlogs();
    </script>
</body>
</html>

前端代码要做的事情,就是根据咱们当前这个页面的结构,来构造出内容~~

页面结构是啥,这里的内容就怎么构造~

我们在数据库中插入数据后发现当前发现有一些问题.

1.显示的时间不对

针对web程序的问题,首先要做的就是先明确问题是由前端还是后端引起的

通过抓包就可以知道问题是哪边的!

如果抓包结果返回的数据就是时间戳,不是格式化时间,说明是后端问题.
如果抓包的结果是格式化时间,说明是前端问题.

在这里插入图片描述

当前可以看到后端返回的就是时间戳了!!

在这里插入图片描述
后端返回的数据,是Jackson把blogs转成json字符串
直接返回的
jackson会遍历当前的blogs List,取到每个blog对象
去调用blog对象的每个getter方法,拿到对应的属性,构造json字符串

在这里插入图片描述
由于这个方法返回的是时间戳
导致最终构造json字符串的时候往里构造的内容就是时间戳了~

如果此处可以得到一个格式化时间,后续的json字符串里,也就是格式化时间了~~

具体如何生成格式化时间?
要用到SimpleDateFormat这个类~

在这里插入图片描述
咱们要生成格式化时间,这个时间具体是啥样的格式?需要描述一下~~

所以Blog里的方法要改为

 public String getPostTime() {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        return format.format(postTime);
    }

同时BlogDao里的数据库操作也要改成

statement.setString(4, blog.getPostTime());

2.博客文章的顺序应该是新的文章在上面

只需把BlogDao里的操作改为

String sql = "select * from blog order by postTime desc";

3.博客列表页,显示的是正文的一段摘要

我们就需要更改BlogDao中的Content

String content = resultSet.getString("content");
if (content.length() > 50) {
	content = content.substring(0,50) + "...";
}
blog.setContent(content);

我们在数据库中在插入数据然后观察:

在这里插入图片描述

2.博客详情页

1.约定前后端交互接口

请求:
GET/blog?blogId=1

响应

{
    blogId:1,
    title:"第一篇博客",
    content:"这是正文",
    //这里要获取到完整的博客正文
    userId:1,
    postTime:"2023-01-26 12:00:00"
}

2.编写后端代码

每个Servlet类都是关联了一个路径的.
如果路径确定了,对应的Servlet也就明确了.

所以我们还是只需调整BlogServlet即可!

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        
        //从 query string 中查询一下是否有blogId,如果有,就查询指定博客;如果没有就是查询所有博客
        BlogDao blogDao = new BlogDao();
        String blogId = req.getParameter("blogId");
        if(blogId == null) {
            List<Blog> blogs = blogDao.selectAll();
            String respString = objectMapper.writeValueAsString(blogs);
            resp.setContentType("application/json;charset=utf8");
            resp.getWriter().write(respString);
        }else {
            Blog blog = blogDao.selectOne(Integer.parseInt(blogId));
            String respString = objectMapper.writeValueAsString(blog);
            resp.setContentType("application/json;charset=utf8");
            resp.getWriter().write(respString);
        }
    }

此处的关键就是根据 blogId 这个query string是否存在,来分两个情况讨论~

3.编写前端代码

在这里插入图片描述
我们预期的url:blog?blogId=1
?blogId=1这一段就是从location,search来的
location是一个全局标量
相当于Document一样

在这里插入图片描述
由于当前响应的Content-Type 是 application/json
此时jquery就会直接把这个响应数据,转成js对象

blog.blogId
body.title

设置正文,正文内容应该是 markdown 格式的数据
此处要显示的应该是渲染过的markdown的内容,而不是markdown的原始字符串.
第一个参数,是一个html元素的id,接下来渲染的结果会放到对应的元素中.

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>博客详情页</title>
  <link rel = "stylesheet" href="css/common.css">
  <link rel = "stylesheet" href="css/detail.css">

  <!--引入jquery-->
  <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
  <!-- 引入 editor.md 的依赖 -->
  <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
  <script src="js/jquery.min.js"></script>
  <script src="editor.md/lib/marked.min.js"></script>
  <script src="editor.md/lib/prettify.min.js"></script>
  <script src="editor.md/editormd.js"></script>


</head>
<body>
<!--导航栏,nav是导航栏的缩写-->
<div class="nav">
  <!--logo-->
  <img src="image/1.jpg" alt="">
  <!---->
  <div class="title" >我的博客系统</div>
  <!--只是一个空白,用来把后面的链接挤过去(这是一个简单粗暴地写法)-->
  <div class="spacer"></div>
  <a href="list.html">主页</a>
  <a href="edit.html">写博客</a>
  <!--注销 这里的地址后面提到-->
  <a href="">注销</a>
</div>

<!--页面主体部分-->
<div class="container">
  <!--左侧信息-->
  <div class="container-left">
    <!--这个div标识整个用户信息的区域-->
    <div class = "card">
      <!--用户头像-->
      <img src = "image/3.jpg" alt = "">
      <!--用户名-->
      <h3>南北</h3>
      <!-- Gitee 地址-->
      <a href="https://gitee.com/D2814413094">Gitee 地址</a>
      <!--统计信息-->
      <div class="counter">
        <span>文章</span>
        <span>分类</span>
      </div>
      <div class="counter">
        <span>3</span>
        <span>1</span>
      </div>

    </div>
  </div>
  <!--右侧信息-->
  <div class="container-right">
  <h3></h3>
    <div class="data"></div>
    <div id="content">

    </div>
  </div>
  <script>
    function getBlog() {
      $.ajax({
        type:'get',
        url:'blog'+location.search,
        success:function (body) {
          //设置博客标题
          let h3 = document.querySelector('.container-right h3');
          h3.innerHTML = body.title;
          //设置发布时间
          let dataDiv = document.querySelector('.container-right .data');
          dataDiv.innerHTML = body.postTime;
          //设置正文,正文内容应该是 markdown 格式的数据
          //此处要显示的应该是渲染过的markdown的内容,而不是markdown的原始字符串.
          //第一个参数,是一个html元素的id,接下来渲染的结果会放到对应的元素中.
          editormd.markdownToHTML('content',{markdown:body.content});
        }
      });
    }
    getBlog();
  </script>
</div>

</body>
</html>

在这里插入图片描述

当我们修改完代码以后,点击博客详情,如果发现内容还是之前的内容,就需要使用 Ctrl + f5 强制刷新,避免浏览器读取缓存~~

3.博客登录页

在这里插入图片描述
输入用户名,密码
点击登录,就会触发登录操作~~

期望,点击的时候,给后端发起一个http请求
后端做一些登录相关的逻辑(验证用户名密码之类的)

1.约定前后端交互接口

此处提交用户名密码,可以使用form也可以使用ajax
form是更简单的做法~

请求
POST/login
Content-Type:application/x-www-form-urlencoded
专属于form表单的类型

username=zhangsan&password=123
query string 就是这个格式,在服务器直接使用getParameter就能获取到内容

响应:
HTTP/1.1 302
Location:list.html
登陆成功,直接跳转主页

如果是通过302跳转
前端必须使用form,不能使用ajax
ajax的话,收到302响应,不会触发页面跳转~~
(必须使用js来跳转)

2.编写后端代码

需要创建一个Servlet来处理上述login的请求!!
在这里插入图片描述

如果前端传过来的用户名带有中文~~,此时这个代码拿到的username可能是乱码
需要明显的告诉Servlet按照啥样的编码方式来理解请求参数!!

在这里插入图片描述

会话,让服务器保存当前用户的一些数据!

在这里插入图片描述

会话可以理解成一个hash表~
每个用户(每个客户端)都会对应到这个表里的一个键值对
其中键是一个字符串,sessionId(唯一的字符串)
其中值是HttpSession对象了
这个对象,也可以按照键值对的方式来存储其他数据(getAttribute,setAttribute)

package api;

import model.User;
import model.UserDao;

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

/**
 * @projectName: test-2023-10-27
 * @package: api
 * @className: LoginServlet
 * @author: 王嘉辉
 * @description:
 * @date: 2023/10/27 17:36
 * @version: 1.0
 */
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf8");
        //1.从请求中获取到用户名和密码
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        if (username == null || username.equals("") || password == null || password.equals("")) {
            String html = "<h3> 登录失败!缺少用户名或者密码!</h3>";
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write(html);
            return;
        }
        //2.读取数据库,看这里的用户名密码是否匹配
        UserDao userDao = new UserDao();
        User user = userDao.selectUserByName(username);
        if(user == null) {
            String html = "<h3> 用户名或密码错误!</h3>";
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write(html);
            return;
        }
        if(!password.equals(user.getPassword())) {
            String html = "<h3> 用户名或密码错误!</h3>";
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write(html);
            return;
        }
        //3.需要设置会话
        HttpSession session = req.getSession(true);
        //此处就把用户对象存储到session中了,下次用户访问其他页面,就可以直接拿到会话,进一步拿到之前的user对象了.
        session.setAttribute("user",user);
        //4.返回一个重定向相应,能够跳转到博客列表页
        resp.sendRedirect("list.html");
    }
}

3,编写前端代码

在这里插入图片描述

id属性,只是针对html生效,只是用来方便获取到该元素.
name属性则是针对form表单构造http请求的~

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>博客登录页</title>
    <link rel="stylesheet" href="css/common.css">
    <link rel="stylesheet" href="css/login.css">
</head>
<body>
    <!--导航栏,nav是导航栏的缩写-->
    <div class="nav">
    <!--logo-->
    <img src="image/1.jpg" alt="">
    <!---->
    <div class="title" >我的博客系统</div>
    <!--只是一个空白,用来把后面的链接挤过去(这是一个简单粗暴地写法)-->
    <div class="spacer"></div>
    <a href="list.html">主页</a>
    <a href="edit.html">写博客</a>

</div>

    <!--登录页的版心-->
    <div class="login-container">
        <!--登录对话框-->
        <div class="login-dialog">
            <h3>登录</h3>
            <!--使用form包装一下下列内容,便于给后续服务器提交数据-->
            <form action="login" method="post">
                <div class="row">
                    <span>用户名</span>
                    <input type="text" id="username"name="username">
                </div>
                <div class="row">
                    <span>密码</span>
                    <input type="password" id="password"name="password">
                </div>
                <div class="row">
                    <input type="submit" id="submit" value="登录">
                </div>
            </form>
        </div>
    </div>
</body>
</html>

在这里插入图片描述

此时,输入正确的账号密码,就会跳转到博客列表页~

在这里插入图片描述
输入错误

在这里插入图片描述

4.实现强制要求登录

做出以下设定:
当用户访问博客列表页/详情页/编辑页的时候,必须是登录状态,如果未登录,则直接跳转到登录页要求用户登录

在博客列表页/详情页/登录页,页面加载的时候,发起一个ajax请求,通过这个请求,访问服务器,获取到当前的登录状态.
如果当前未登录,则跳转到登录页面
如果已登录,则不做任何操作~~

1.约定前后端交互接口

请求:
GET/login

响应:
登录成功,就返回200这样的响应
登录失败(未登录状态),返回403这样的响应

2.编写后端代码

在loginServlet写一个doGet判定当前的登录状态.

    //通过这个方法,判定用户的登录状态,已登录,返回200,未登录,返回403
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //看当前请求是否存在会话,并且当前的会话是否包含user对象
        //getSession(boolean create)意思是返回当前reqeust中的HttpSession ,如果当前reqeust中的HttpSession 为null,当create为true,就创建一个新的Session,否则返回null;
        HttpSession session = req.getSession(false);
        if (session == null) {
            resp.setStatus(403);
            return;
        }
        User user = (User) session.getAttribute("user");
        if (user == null) {
            //虽然会话对象存在,但是用户对象没有,未登录状态
            resp.setStatus(403);
            return;
        }
        //已经登陆了
        //200 是默认的状态码,此处不写200也是可以的
        resp.setStatus(200);
    }
}

3.编写前端代码

需要在博客列表页/详情页加入

  <script>
        function getLoginStatus() {
            $.ajax({
                type:'get',
                url:'login',
                success:function (body) {
                    //响应200的时候,执行success回调
                    //用户已经登录啥都不用干
                    console.log("用户已经登录了.");
                },
                error:function (body) {
                    //响应403的时候,执行error回调(只要返回不是2开头的,都会触发error)
                    //跳转到login.html主页
                    location.assign("login.html");
                }
            });
        }
        getLoginStatus();
</script>

5.显示用户信息

在博客列表页,此处显现的是登录的用户信息!!

在博客详情页,显示的是文章作者的信息!!

针对这两个部分的数据,就需要分别进行获取~~

1.约定前后端交互接口

在博客列表页,要获取到登录用户的信息~~
可以直接复用刚刚的那个 getLoginStatus 方法.

让这个方法在登录成功之后,就把当前用户信息,给返回回来~~

请求:
GET/login

响应:
HTTP/1.1 200
Content-Type:application/json;

{
	userId:1
	username:zhangsan,
}

页面拿到这个数据就可以显示到界面上了~~

2.开发后端代码

基于现有代码进行修改

        //200 是默认的状态码,此处不写200也是可以的
        resp.setStatus(200);
        resp.setContentType("application/json;charset=utf8");
        user.setPassword("");//避免把密码也返回给前端,减少密码泄露的风险
        String respJson = objectMapper.writeValueAsString(user);
        resp.getWriter().write(respJson);

3.开发前端代码

基于现有代码进行修改

三个页面都在使用 getLoginStatus
实际上,只是需要在博客列表页,做出这样的调整,另外两个页面不变!!

<script>
        function getLoginStatus() {
            $.ajax({
                type:'get',
                url:'login',
                success:function (body) {
                    //响应200的时候,执行success回调
                    //用户已经登录啥都不用干
                    console.log("用户已经登录了.");
                    //把返回的用户名,设置到页面中
                    let h3 = document.querySelector('.card h3');
                    //body 已经是一个 js 对象了,就是前面服务器返回的 json 格式的 user 对象
                    h3.innerHTML = body.username;
                },
                error:function (body) {
                    //响应403的时候,执行error回调(只要返回不是2开头的,都会触发error)
                    //跳转到login.html主页
                    location.assign("login.html");
                }
            });
        }
</script>

在这里插入图片描述

在这里插入图片描述

在针对详情页,进行处理.
详情页这里.显示的是当前文章的作者信息.

先根据blogId查询到文章对象,进一步拿到文章的作者的id
再根据作者id查询对应作者名字,显示到页面上~

1.约定前后端交互接口

请求:
GET/user?blogId = 1

响应:
HTTP/1.1 200
Content-Type:application/json;

{
	userId:1
	username:zhangsan,
}

2.编写后端代码

创建一个新的Servlet处理上述请求.

package api;

import com.fasterxml.jackson.databind.ObjectMapper;
import model.Blog;
import model.BlogDao;
import model.User;
import model.UserDao;

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

/**
 * @projectName: test-2023-10-30
 * @package: api
 * @className: UserServlet
 * @author: 王嘉辉
 * @description:
 * @date: 2023/10/30 16:35
 * @version: 1.0
 */
@WebServlet("/user")
public class UserServlet extends HttpServlet {
    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("application/json;charset=utf8");
        //1.先读取出blogId
        String blogId = req.getParameter("blogId");
        if (blogId == null || blogId.equals("")) {
            //直接返回一个userId为0的对象,因为最终返回的是一个json数据
            //此处也是返回 json 格式比较好,如果返回一个html,前端处理就会很麻烦
            String respJson = objectMapper.writeValueAsString(new User());
            resp.getWriter().write(respJson);
            System.out.println("参数给定的 blogId 为空!!");
            return;
        }
        //2.查询数据库,查询对应的Blog对象
        BlogDao blogDao = new BlogDao();
        Blog blog = blogDao.selectOne(Integer.parseInt(blogId));
        if (blog == null) {
            String respJson = objectMapper.writeValueAsString(new User());
            resp.getWriter().write(respJson);
            System.out.println("参数给定的 blogId 不存在!!");
            return;
        }
        //根据 blog 中的 userId,查询作者信息
        UserDao userDao = new UserDao();
        User user = userDao.selectUserById(blog.getUserId());
        if(user == null) {
            String respJson = objectMapper.writeValueAsString(new User());
            resp.getWriter().write(respJson);
            System.out.println("该博客对应的作者不存在!!");
            return;
        }
        //4.把user对象返回给页面
        String respJson = objectMapper.writeValueAsString(user);
        resp.getWriter().write(respJson);
    }
}

3.编写前端代码

 <script>
    function getAuthor() {
      $.ajax({
        type:'get',
        url:'user' + location.search,
        success:function (body) {
          //把响应中的得到的user对象的数据,给构造到页面上
          if (body.userId == 0) {
            //服务器没有找到匹配的用户
            alert("当前未找到作者信息!")
            return;
          }
          //body 是一个合法的 user 对象
          let h3 = document.querySelector('.card h3');
          h3.innerHTML = body.username;
        }
      });
    }
    getAuthor();
  </script>

在这里插入图片描述

6.用户退出登录(注销)

在这里插入图片描述

这个注销,是一个a标签
这个a标签在点击的时候,就会触发一个GET请求
服务器收到这个get请求,就可以把当前用户的会话中的user对象给删掉即可!!

1.约定前后端交互接口

请求:
GET/logout

响应:
HTTP/1.1 302
Location:login.html

2.编写后端代码

创建新的Servlet

package api;

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

/**
 * @projectName: test-2023-10-30
 * @package: api
 * @className: LogoutServlet
 * @author: 王嘉辉
 * @description:
 * @date: 2023/10/30 17:14
 * @version: 1.0
 */
@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession(false);
        if (session == null) {
            //用户本来就没登录,直接跳转到登录页.
            resp.sendRedirect("login.html");
            return;
        }
        //user 存在,就删除了,不存在,也不会有副作用
        session.removeAttribute("user");
        resp.sendRedirect("login.html");
    }
}

3.编写前端代码

只需给a标签写个href的值即可!

在这里插入图片描述

在这里插入图片描述

登录后

在这里插入图片描述

点击注销

在这里插入图片描述

7.实现发布博客

在博客编辑页进行相关操作~~

用户写的博客标题和正文,就可以随着点击按钮而进行上传
服务器就可以保存上述数据到数据库中
接下来,后续就可以在博客列表页和博客详情页中看到了刚才新的博客了~

1.约定前后端交互接口

使用form提交博客~~

请求:
POST/blog
Content-Type:application/x-www-form-urlencoded

title=标题&content=…

响应:
HTTP/1.1 302
Location:list.html

发布成功,直接跳转到列表页~

2.编写后端代码

在这里插入图片描述

package api;

import com.fasterxml.jackson.databind.ObjectMapper;
import model.Blog;
import model.BlogDao;
import model.User;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.List;

/**
 * @projectName: blog_system
 * @package: api
 * @className: BlogServlet
 * @author: 王嘉辉
 * @description:
 * @date: 2023/10/24 11:10
 * @version: 1.0
 */
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
    private ObjectMapper objectMapper = new ObjectMapper();

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

        //从 query string 中查询一下是否有blogId,如果有,就查询指定博客;如果没有就是查询所有博客
        BlogDao blogDao = new BlogDao();
        String blogId = req.getParameter("blogId");
        if(blogId == null) {
            List<Blog> blogs = blogDao.selectAll();
            String respString = objectMapper.writeValueAsString(blogs);
            resp.setContentType("application/json;charset=utf8");
            resp.getWriter().write(respString);
        }else {
            Blog blog = blogDao.selectOne(Integer.parseInt(blogId));
            String respString = objectMapper.writeValueAsString(blog);
            resp.setContentType("application/json;charset=utf8");
            resp.getWriter().write(respString);
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf8");
        //1.先从请求中拿到标题和正文
        String title = req.getParameter("title");
        String content = req.getParameter("content");
        if(title == null || title.equals("") || content == null || content.equals("")) {
            String html = "<h3>title 或者 content 为空!! 新增博客失败! </h3>";
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write(html);
            return;
        }
        //2.从会话中拿到作者id
        HttpSession session = req.getSession(false);
        if (session == null) {
            String html = "<h3>当前用户未登录 新增博客失败! </h3>";
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write(html);
            return;
        }
        User user = (User) session.getAttribute("user");
        if (user == null) {
            String html = "<h3>当前用户未登录 新增博客失败! </h3>";
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write(html);
            return;
        }
        //3.构造blog对象
        Blog blog = new Blog();
        blog.setUserId(user.getUserId());
        blog.setTitle(title);
        blog.setContent(content);
        blog.setPostTime(new Timestamp(System.currentTimeMillis()));
        //4.插入 Blog 对象到数据库中
        BlogDao blogDao  = new BlogDao();
        blogDao.insert(blog);
        //5.跳转到博客列表页
        resp.sendRedirect("list.html");
    }
}

3.编写前端代码

修改页面,加上form表单,同时让页面提交的时候按照咱们的约定,构造出HTTP请求.

在这里插入图片描述

在这里插入图片描述
这里是md编辑器
这个div内部的内容,其实是被editor.md做出了各种修改~~

如何才能把编辑框内部内容
作为form的一部份,提交给服务器?

editor.md的官方文档的Demo里给了答案~

(1)先给#editor div 中放一个隐藏的 textarea.后续编辑器输入框的内容就会被自动放到这个 textarea 中.

(2)在editor.md的初始化代码中,需要新增一个选项saveHTMLToTextarea:true

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1152310.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

简单聊聊远程协同运维定义以及优势-行云管家

很多新人小伙伴对于远程协同运维不是很了解&#xff0c;今天我们就来简单聊聊远程协同运维定义以及优势。 远程协同运维定义 远程协同运维其实非常容易理解&#xff0c;主要是指计算机系统技术服务工程相关的人员通过局域网或者是其他网络对于它来进行连接&#xff0c;共同远…

【PyQt学习篇 · ⑦】:QWidget - 父子关系扩充和层级控制

文章目录 父子关系扩充常用API案例 层级控制案例 父子关系扩充 常用API childAt(x, y) 函数可以根据传入的坐标参数获取该QWidget中位于该坐标位置的子QWidget&#xff0c;如果该位置没有子QWidget则返回None。 parentWidget() 函数可以获取当前QWidget的父QWidget&#xff0…

k8s之pod

目录 一、Pod基础概念 1.1 在Kubrenetes集群中Pod有如下两种使用方式 1.2 pod的种类 二、容器的分类 &#xff08;1&#xff09;基础容器&#xff08;pause&#xff09; &#xff08;2&#xff09;初始化容器&#xff08;initcontainers&#xff09; &#xff08;3&#…

数字经济之于城市碳排放:“加速器”抑或“减速带”?

数据简介&#xff1a;数字经济是我国经济高质量发展的核心驱动力&#xff0c;在提升碳福利绩效过程中发挥重要作用&#xff0c;其在许多方面都能提供减少碳排放的机会。通过数字化和物联网技术&#xff0c;能源系统、交通运输、城市规划等领域可以实现智能化管理和优化&#xf…

VPN与IP代理用哪个好?有何区别?

当谈到网络安全和IP变更时&#xff0c;人们会想到VPN和IP代理服务器。很多人很困惑&#xff0c;它们之间有什么区别&#xff0c;应该选择哪一个呢&#xff1f;这取决于您的需求来决定哪一个更好。 一、什么是VPN与IP代理&#xff1f; VPN 是虚拟专用网络 (Virtual Private Net…

软件测试进阶篇----接口测试

接口测试 一、接口的概述 接口是什么&#xff1f; 在系统与系统之间、子系统与子系统之间数据交互的功能就是接口。 接口就是一个特定功能的函数&#xff08;方法&#xff09;&#xff0c;有参数&#xff0c;有返回值&#xff0c;调用者需要通过某种方式&#xff08;网络协议&…

CVE-2021-21234 spring-boot-actuator-logview目录遍历漏洞

0x01 影响版本 Spring-Boot-Actuator-logview < 0.2.13 0x02 漏洞分析 源码中对filename进行了校验但并未对路径进行校验 校验函数如下&#xff1a; 0x03 漏洞复现 首先开vulhub的镜像 点击下载&#xff0c;原数据包如下 送入repeater打入payload&#xff0c;复现…

【机器学习】一、机器学习概述与模型的评估、选择

机器学习简介 由来 阿瑟.萨缪尔Arthur Samuel,1952年研制了一个具有自学习能力的西洋跳棋程序&#xff0c;1956年应约翰.麦卡锡John McCarthy&#xff08;人工智能之父&#xff09;之邀&#xff0c;在标志着人工智能学科诞生的达特茅斯会议上介绍这项工作。他发明了“机器学习…

视频增强修复软件Topaz Video AI mac中文版支持功能

Topaz Video AI mac是一款使用人工智能技术对视频进行增强和修复的软件。它可以自动降噪、去除锐化、减少压缩失真、提高清晰度等等。Topaz Video AI可以处理各种类型的视频&#xff0c;包括低分辨率视频、老旧影片、手机录制的视频等等。 使用Topaz Video AI非常简单&#xff…

一文搞懂隐私计算

隐私计算 1. 安全多方计算2. 联邦学习3. 可信执行环境4. 隐私计算三类技术比较 隐私计算&#xff08;Privacy computing&#xff09;是指在保证数据不对外泄露的前提下&#xff0c;由两个或多个参与方联合完成数据分析计算相关技术的统称。 隐私计算作为跨学科技术&#xff0c…

如何优雅地单元测试 Kotlin/Java 中的 private 方法?

翻译自 https://medium.com/mindorks/how-to-unit-test-private-methods-in-java-and-kotlin-d3cae49dccd ❓如何单元测试 Kotlin/Java 中的 private 方法❓ 首先&#xff0c;开发者应该测试代码里的 private 私有方法吗&#xff1f; 直接信任这些私有方法&#xff0c;测试到…

Leetcode—169.多数元素【简单】

2023每日刷题&#xff08;十四&#xff09; Leetcode—169.多数元素 算法思想 由于nums中一定存在多数元素&#xff0c;所以将nums数组元素递增排序&#xff0c;取出位置的元素即可 实现代码 class Solution { public:int majorityElement(vector<int>& nums) {s…

视频转gif表情怎么操作?一键提取gif动画

通过电影、电视剧等视频转换gif动画&#xff0c;效果非常的吸引人。但是很多视频转换gif的工具要么是需要下载要么就是操作起来很复杂。其实&#xff0c;大家只需要使用gif动画图片&#xff08;https://www.gif.cn/&#xff09;制作工具&#xff0c;不用下载任何软件&#xff0…

前端基础---跳转相关的功能

后端给链接地址并且给token进行跳转 如果点击有key4&#xff0c;说明要跳转到相应的页面 auth是通过后端获取的地址&#xff0c; jdk是后端获取的相应的token&#xff0c; 然后进行拼接&#xff0c;进行window.open&#xff08;&#xff09;进行跳转 if (key 4) {var testUrl …

JavaEE入门介绍,HTTP协议介绍,常用状态码及含义,服务器介绍(软件服务器、云服务器)

一、JavaEE入门 JavaEE&#xff08;Java Enterprise Edition&#xff09;&#xff0c;Java企业版&#xff0c;是一个用于企业级web开发&#xff08;不需要使用控制台&#xff09;平台。最早由Sun公司定制并发布&#xff0c;后由Oracle负责维护。 JavaEE平台规范了在开发企业级w…

开发者看亚马逊云科技1024【文末有福利~】

1024&#xff0c;2023年的1024&#xff0c;注定是不平凡的1024&#xff0c;AIGC已经成为了整个年度的主题&#xff0c;亚马逊云科技在这个开发者每年最重要的日子&#xff0c;举办了生成式AI构建者大会&#xff0c;让我们一起再次了解本次生成式AI构建者大会&#xff0c;回顾会…

【c++|opencv】一、基础操作---2.图像信息获取

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 图像信息获取&#xff0c;roi 1. 图像信息获取 // 获取图像信息#include <iostream> #include <opencv2/opencv.hpp>using namespace cv; …

浅谈智能卡远程费控电能表的设计与实现

叶根胜 安科瑞电气股份有限公司 上海嘉定201801 摘要:本文分析了国内外远程费用控制电能表的研究现状&#xff0c;并根据功能要求和参数要求设计研究了远程费用控制电能表。采用模块化设计的电能表硬件电路系统&#xff0c;并研究了电能表的功能程序软件&#xff0c;实现了测…

虚拟内存之页面置换算法

内存空间不够&#xff0c;OS将内存中暂时用不到的信息换出到外存。但页面的换入/出需要磁盘I/O&#xff0c;系统开销较大。页面置换算法要更少的缺页率。 一、最佳置换(OPT)-optimal 1.思想 每次选择淘汰以后永不使用、在最长时间内不再被访问的页面&#xff0c;以此保证最低的…

3 tensorflow构建模型详解

上一篇&#xff1a;2 用TensorFlow构建一个简单的神经网络-CSDN博客 1、神经网络概念 接上一篇&#xff0c;用tensorflow写了一个猜测西瓜价格的简单模型&#xff0c;理解代码前先了解下什么是神经网络。 下面是百度AI对神经网络的解释&#xff1a; 神经网络是一种运算模型&…