博客项目(Spring Boot)

news2024/11/24 3:53:12

1.需求分析

  1. 注册功能(添加用户操纵)
  2. 登录功能(查询操作)
  3. 我的文章列表页(查询我的文章|文章修改|文章详情|文章删除)
  4. 博客编辑页(添加文章操作)
  5. 所有人博客列表(带分页功能)
  6. 文章详情页(多个查询功能和一个修改功能)

2.设计数据库

用户表:id(主键),登录名,密码,头像,状态

文章表:主键,标题,正文,发表时间,更新时间,作者id,阅读量,状态。

3.创建项目所有框架

所用技术:Spring Boot + Spring MVC + MyBatis + MySQL

将前端代码添加到项目中,前端代码详情请看博客连接。

创建后端并进行项目分层:

  • 控制层
  • 服务层
  • 数据持久层
  • 实体类层
  • 配置层
  • 工具层

4.后端代码

4.1实体类层

用户类:

package com.example.demo.model;

import lombok.Data;

import java.time.LocalDateTime;

/**
 * Describe:
 * User:lenovo
 * Date:2023-08-03
 * Time:18:02
 */
@Data
public class Userinfo {
    private int id;
    private String username;
    private String password;
    private String photo;
    private LocalDateTime createtime;
    private LocalDateTime updatetime;
    private int state;
}

文章类:

package com.example.demo.model;

import lombok.Data;

import java.time.LocalDateTime;

/**
 * Describe:
 * User:lenovo
 * Date:2023-08-03
 * Time:18:04
 */
@Data
public class Articleinfo {
    private int id;
    private String title;
    private String content;

    private LocalDateTime createtime;
    private LocalDateTime updatetime;
    private int uid;
    private int rcount;
    private int state;
}

4.2注册功能

在common层编写ResultAjax类,为统一的返回类。

@Data
public class ResultAjax {
    private int code;
    private String msg;
    private Object data;

    //为了方便使用我们构造一个成功方法
    public static ResultAjax succ(Object data) {
        ResultAjax resultAjax = new ResultAjax();
        resultAjax.setCode(200);
        resultAjax.setData(data);
        return resultAjax;
    }

    //为了方便处理构造一个成功的方法
    public static ResultAjax succ(int code, String msg, Object data) {
        ResultAjax resultAjax = new ResultAjax();
        resultAjax.setData(code);
        resultAjax.setMsg(msg);
        resultAjax.setData(data);
        return resultAjax;
    }

    //为了方便,我们构造一个失败的方法
    public static ResultAjax fail(int code, String msg) {
        ResultAjax resultAjax = new ResultAjax();
        resultAjax.setCode(code);
        resultAjax.setMsg(msg);
        resultAjax.setData(null);
        return resultAjax;
    }

    //为了方便,我们构造一个失败的方法
    public static ResultAjax fail(int code, String msg, Object data) {
        ResultAjax resultAjax = new ResultAjax();
        resultAjax.setCode(code);
        resultAjax.setMsg(msg);
        resultAjax.setData(data);
        return resultAjax;
    }
}

编写UserMapper类:

@Mapper
public interface UserMapper {

    @Insert("insert into userinfo(username, password) values(#{username}, #{password})")
    int reg(String username, String password);
}

编写UserService类:

@Service
public class UserService {
    @Autowired
    UserMapper userMapper;

    public int reg(String username, String password) {
        return userMapper.reg(username, password);
    }
}

编写UserController类:

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    @RequestMapping("/reg")
    public ResultAjax reg(Userinfo userinfo) {
        //1.参数校验
        if(userinfo.getUsername() == null || userinfo.getUsername().trim().equals("")) {
            return ResultAjax.fail(-1, "参数错误!");
        }
        if(userinfo.getPassword() == null || userinfo.getPassword().trim().equals("")) {
            return ResultAjax.fail(-1, "参数错误");
        }
        //2.处理数据
        int ret = userService.reg(userinfo.getUsername(), userinfo.getPassword());
        //3.返回结果
        return ResultAjax.succ(ret);
    }
}

4.3登录功能

VO实体对象类是实体对象类的一个扩展,主要包含数据表中不拥有,但是前后端可能会使用到的对象。比如登录的时候,我们需要验证码,这个验证码,必须后端接收,并且不存在于数据表中,我们可以使用扩展对象进行接收。

编写UserVO类(在此文章中作用不大,但是方便以后的更新):

/**
 * Describe: userinfo的扩展类
 * User:lenovo
 * Date:2023-08-04
 * Time:19:03
 */
@Data
public class UserinfoVO extends Userinfo {
    //这里可以放入验证码
    private String checkCode;
}

编写UserMapper类:

@Select("select * from userinfo where username=#{username}")
    Userinfo getUserByName(@Param("username")String username);

编写UserService类:

public Userinfo getUserByName(String username) {
        return userMapper.getUserByName(username);
    }

编写UserController类:

//登录接口:根据名字查找用户
    @RequestMapping("/login")
    public ResultAjax getUserByName(UserinfoVO userinfoVO, HttpServletRequest request) {
        // 1.校验参数
        if(userinfoVO == null || !StringUtils.hasLength(userinfoVO.getUsername()) ||
                !StringUtils.hasLength(userinfoVO.getPassword())) { //这里我们使用StringUtils.hasLength判断它是否为null或为空
            return ResultAjax.fail(-1, "非法参数!");
        }
        // 2.根据用户名查找对象
        Userinfo userinfo = userService.getUserByName(userinfoVO.getUsername());
        if(!userinfoVO.getPassword().equals(userinfo.getPassword())) {
            //密码错误
            return ResultAjax.fail(-2, "账号或密码错误!");
        }
        // 3.将对象存储到session中
        HttpSession session = request.getSession();
        session.setAttribute(AppVariable.SESSION_USERINFO_KEY, userinfo);
        // 4.将结果返回给前端
        return ResultAjax.succ(1);
    }

这里我们使用cookie-session才储存用户信息。

编写公共类(通过这个类,我们知道session通过什么关键字,来获取到用户的关键信息):

/**
 * Describe: 全局变量
 * User:lenovo
 * Date:2023-08-04
 * Time:19:12
 */
public class AppVariable {
    //用户的session key
    public static final String SESSION_USERINFO_KEY = "SESSION_USERINFO";
}

4.4登录校验/统一异常处理/统一结果返回

登录校验:

/**
 * Describe:拦截器
 * User:lenovo
 * Date:2023-08-09
 * Time:19:06
 */
public class LoginIntercept implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        HttpSession session = request.getSession(false);
        if(session == null || session.getAttribute(AppVariable.SESSION_USERINFO_KEY) == null) {
            response.sendRedirect("/login.html");
            return false;
        }
        return true;
    }
}
/**
 * Describe:系统配置文件
 * User:lenovo
 * Date:2023-08-09
 * Time:19:15
 */
@Configuration
public class MyConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginIntercept())
                .addPathPatterns("/**")
                .excludePathPatterns("/user/login")
                .excludePathPatterns("/user/reg")
                .excludePathPatterns("/art/getlistbypage")
                .excludePathPatterns("/art/detail")
                .excludePathPatterns("/editor.md/*")
                .excludePathPatterns("/img/*")
                .excludePathPatterns("/js/*")
                .excludePathPatterns("/css/*")
                .excludePathPatterns("/blog_list.html")
                .excludePathPatterns("/blog_content.html")
                .excludePathPatterns("/reg.html")
                .excludePathPatterns("/login.html");
    }
}

统一结果返回:

/**
 * Describe:保底统一的返回值
 * User:lenovo
 * Date:2023-08-05
 * Time:10:00
 */
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {

    @Autowired
    private ObjectMapper objectMapper;
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }


    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        //判断类是否为标准的返回类型
        if(body instanceof ResultAjax) {
            return body;
        }
        if(body instanceof String) {
            ResultAjax resultAjax = ResultAjax.succ(body);
            try {
                return objectMapper.writeValueAsString(resultAjax);//如果是String类型,我们需要手动的转化为json对象
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }

        }
        return  ResultAjax.succ(body);
    }
}

统一异常的处理:

/**
 * Describe:统一异常的处理
 * User:lenovo
 * Date:2023-08-05
 * Time:10:33
 */
//@RestControllerAdvice  //为了发现后续编程过程中的问题,我们可以先注释掉
public class ExceptionAdvice {
    @ExceptionHandler(Exception.class)
    public ResultAjax doException(Exception e) {
        return ResultAjax.fail(-1, e.getMessage());
    }
}

4.5查看我的文章列表页

Mapper:

@Mapper
public interface ArticleMapper {

    @Select("select * from articleinfo where uid = #{uid} order by id desc")
    List<Articleinfo> getListByUid(@Param("uid") int uid);
}

service:

@Service
public class ArticleService {
    @Autowired
    private ArticleMapper articleMapper;

    public List<Articleinfo> getListByUid(@Param("uid") int uid) {
        return articleMapper.getListByUid(uid);
    }
}

controller: 

    //得到当前登录用户的文章列表
    @RequestMapping("/mylist")
    public ResultAjax myList(HttpServletRequest request) {
        // 1.等到当前登录的用户
        Userinfo userinfo = SessionUtils.getUser(request);
        if(userinfo == null) {
            return ResultAjax.fail(-1, "请先登录!");
        }
        // 2.根据用户的id查询你次用户发表的所有文章
        List<Articleinfo> list = articleService.getListByUid(userinfo.getId());
        //处理list->将文章正文变成简介
        if(list != null && list.size() > 0) {
            //并发的处理文章的内容,然后处理集合中每一个元素
            list.stream().parallel().forEach((art) -> {
                if(art.getContent().length() > _DESC_LENGTH) {
                    //这是简介内容,我们取前120个字符
                    art.setContent(art.getContent().substring(0, _DESC_LENGTH));
                }
            });
        }
        return ResultAjax.succ(list);
    }

4.6添加博客

Mapper:

@Select("select * from userinfo where username=#{username}")
    Userinfo getUserByName(@Param("username")String username);

service:

 public int add(Articleinfo articleinfo) {
        return articleMapper.add(articleinfo);
    }

controller:

//添加文章
    @RequestMapping("/add")
    public ResultAjax add(Articleinfo articleinfo, HttpServletRequest request) {
        // 1.校验参数
        if(articleinfo == null || !StringUtils.hasLength(articleinfo.getTitle()) ||
                !StringUtils.hasLength(articleinfo.getContent())) {
            return ResultAjax.fail(-1, "非法参数!");
        }
        // 2.组装数据
        Userinfo userinfo = SessionUtils.getUser(request);
        if(userinfo == null) {
            return ResultAjax.fail(-2, "请先登录");
        }
        articleinfo.setUid(userinfo.getId());
        int ret = articleService.add(articleinfo);
        // 3.将结果返回给前端
        return ResultAjax.succ(ret);

    }

4.7删除博客

Mapper:

@Delete("delete from articleinfo where id = #{aid} and uid = uid")
    int del(@Param("aid") Integer aid, int uid);

Service:

public int del(Integer aid, int uid) {
        return articleMapper.del(aid, uid);
    }

Controller:

//删除文章
    @RequestMapping("/del")
    public ResultAjax del(Integer aid, HttpServletRequest request) {
        // 1.参数校验
        if(aid == null || aid <= 0) {
            return ResultAjax.fail(-1, "参数错误");
        }
        // 2.获取登录的用户
        Userinfo userinfo = SessionUtils.getUser(request);
        if(userinfo == null) {
            return ResultAjax.fail(-1, "请先登录");
        }
        // 3.执行删除操作
        // 执行删除操作,我们有很多的方式,这里采用 aid和uid 的方法
        int ret = articleService.del(aid, userinfo.getId());
        if(ret == 0) {
            return ResultAjax.fail(-1, "你不是文章的归属人");
        }
        return ResultAjax.succ(ret);
    }

4.8查看文章详情

查看文章主要包含三个功能:

  • 查询文章的内容
  • 查询相应的作者信息
  • 查询文章总数

最后两点我们可以使用多线程的方式来实现,提高查询的效率,同时三个功能使用一个HTTP请求,以减少多次连接和断开的次数,减少资源的消耗.

ArticleMapper:

    @Select("select * from articleinfo where id = #{aid}")
    Articleinfo getDetailById(@Param("aid") int aid);

    @Select("select count(*) from articleinfo where uid = #{uid}")
    int getArtCountByUid(@Param("uid") int uid);

UserMapper:

    @Select("select * from userinfo where id = #{uid}")
    UserinfoVO getUseById(@Param("uid") int uid);

ArticleService:

    public Articleinfo getDetail(int aid) {
        return articleMapper.getDetailById(aid);
    }

    public int getArtCountByUid(int uid) {
        return articleMapper.getArtCountByUid(uid);
    }

UserService:

public UserinfoVO getUserById(int uid) {
        return userMapper.getUseById(uid);
    }

ArticleController:

//获取文章详情
    @RequestMapping("/detail")
    public ResultAjax detail(Integer aid) throws ExecutionException, InterruptedException {
        // 1.参数校验
        if(aid == null || aid <= 0) {
            return ResultAjax.fail(-1, "参数错误!");
        }
        // 2.查看文章详情
        Articleinfo articleinfo = articleService.getDetail(aid);
        if(articleinfo == null) {
            return ResultAjax.fail(-2, "查询文章不存在");
        }
        // 3.根据uid查询作者的详情信息
        FutureTask<UserinfoVO> userTask = new FutureTask<>(() -> {
            return userService.getUserById(articleinfo.getUid());
        });
        // 4.查询uid查询用户发表的文章总数
        FutureTask<Integer> artCountTask = new FutureTask<>(() -> {
            return articleService.getArtCountByUid(articleinfo.getUid());
        });
        taskExecutor.submit(artCountTask);
        taskExecutor.submit(userTask);
        // 5.组装数据
        UserinfoVO userinfoVO = userTask.get(); //等待任务执行完成
        int artCount = artCountTask.get(); //等待任务执行完成
        userinfoVO.setArtCount(artCount);
        HashMap<String, Object> result = new HashMap<>();
        result.put("user", userinfoVO);
        result.put("art", articleinfo);
        // 6.返回结果给前端
        return ResultAjax.succ(result);
    }

增加阅读量:

Mapper:

 @Update("update articleinfo set rcount = rcount + 1 where id = #{aid}")
    int incrementRCount(@Param("aid") int aid);

Service:

public int incrementRCount(int aid) {
        return articleMapper.incrementRCount(aid);
    }

Controller:

//文章阅读量加1
    @RequestMapping("/increment_rcount")
    public ResultAjax incrementRCount(Integer aid) {
        // 1.参数校验
        if(aid == null || aid <= 0) {
            return ResultAjax.fail(-1, "参数错误");
        }
        // 2.更改数据库
        int result = articleService.incrementRCount(aid);
        // 3.返回结果
        return ResultAjax.succ(result);
    }

4.9修改文章

Mapper:

    @Select("select * from articleinfo where id = #{aid} and uid = #{uid}")
    Articleinfo getArticleByIdAndUid(int aid, int uid);

    @Update("update articleinfo set title=#{title}, content=#{content} where id = #{id} and uid = #{uid}")
    int update(Articleinfo articleinfo);

service:

    public Articleinfo getArticleByIdAndUid(int aid, int uid) {
        return articleMapper.getArticleByIdAndUid(aid, uid);
    }

    public int update(Articleinfo articleinfo) {
        return articleMapper.update(articleinfo);
    }

controller:

//修改文章
    //更新文章
    @RequestMapping("/update")
    public ResultAjax update(Articleinfo articleinfo, HttpServletRequest request) {
        // 1.参数校验
        if(articleinfo == null || articleinfo.getId() <= 0 ||
        !StringUtils.hasLength(articleinfo.getTitle()) ||
        !StringUtils.hasLength(articleinfo.getContent())) {
            return ResultAjax.fail(-1, "参数错误!");
        }
        // 2.获取登录的用户
        Userinfo userinfo = SessionUtils.getUser(request);
        if(userinfo == null) {
            return ResultAjax.fail(-2, "请登录!");
        }
        // 3.操作数据库
        articleinfo.setUid(userinfo.getId());
        int ret = articleService.update(articleinfo);
        return ResultAjax.succ(ret);
    }

    //修改文章
    //获取文章
    @RequestMapping("/update_init")
    public ResultAjax updateInit(Integer aid, HttpServletRequest request) {
        // 1.参数校验
        if(aid == null || aid <= 0) {
            return ResultAjax.fail(-1, "参数错误!");
        }
        // 2.操作数据库
        Userinfo userinfo = SessionUtils.getUser(request);
        if(userinfo == null) {
            return ResultAjax.fail(-2, "请登录");
        }
        //我们利用where id = #{aid} and uid = #{uid}的操作校验文章所有者
        Articleinfo articleinfo = articleService.getArticleByIdAndUid(aid, userinfo.getId());
        // 3.返回结果
        return ResultAjax.succ(articleinfo);

    }

4.10文章列表页

文章列表页,主要包含两部分:

  1. 文章的获取
  2. 分页功能

mapper:

    @Select("select * from articleinfo order by id desc limit #{psize} offset #{offset}")
    List<Articleinfo> getListByPage(@Param("psize") int psize, @Param("offset") int offset);

    @Select("select count(*) from articleinfo")
    int getCount();

service:

    public List<Articleinfo> getListByPage(int psize, int offset) {
        return articleMapper.getListByPage(psize, offset);
    }

    public int getCount() {
        return articleMapper.getCount();
    }

controller:

//查询分页功能
    @RequestMapping("/getlistbypage")
    public ResultAjax getListByPage(Integer pindex, Integer psize) throws ExecutionException, InterruptedException {
        // 1.参数校验
        if(pindex == null) {
            pindex = 1;
        }
        if(psize == null) {
            psize = 2;
        }
        // 2.并发执行文章列表和总页数的查询
        int finalOffset = psize * (pindex - 1); // 分页公式
        int finalPSize = psize;
        FutureTask<List<Articleinfo>> listTask = new FutureTask<>(() -> {
            return articleService.getListByPage(finalPSize, finalOffset);
        });
        FutureTask<Integer> sizeTask = new FutureTask<Integer>(() -> {
            int totalCount = articleService.getCount();
            if(totalCount % finalPSize != 0) {
                return totalCount / finalPSize + 1;
            }
            return totalCount/ finalPSize;
        });
        taskExecutor.submit(listTask);
        taskExecutor.submit(sizeTask);
        // 3.组装数据
        List<Articleinfo> list = listTask.get();
        int size = sizeTask.get();
        HashMap<String, Object> map = new HashMap<>();
        map.put("list", list);
        map.put("size", size);
        // 4.将结果返回给前端
        return ResultAjax.succ(map);
    }

4.11注销功能

Controller:

//注销功能
    @RequestMapping("/logout")
    public ResultAjax logout(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if(session != null && session.getAttribute(AppVariable.SESSION_USERINFO_KEY) != null) {
            session.removeAttribute(AppVariable.SESSION_USERINFO_KEY);
        }
        return ResultAjax.succ(1);
    }

5.加盐算法

/**
 * Describe: 密码工具类
 * User:lenovo
 * Date:2023-08-10
 * Time:10:45
 */
public class PasswordUtils {
    /**
     * 加盐加密
     */
    public static String encrypt(String password) {
        // 1.盐值
        String salt = UUID.randomUUID().toString().replace("-", "");
        // 2.将盐值+密码进行md5得到最终密码
        String finalPassword = DigestUtils.md5DigestAsHex((salt + password).getBytes(StandardCharsets.UTF_8));
        // 3.将盐值和最终密码进行返回
        return salt + "$" + finalPassword;
    }

    /**
     * 加盐验证
     */
    public static boolean decrypt(String password, String dbpassword) {
        if(!StringUtils.hasLength(password) || !StringUtils.hasLength(dbpassword) ||
            dbpassword.length() != 65) {
            return false;
        }
        // 1.得到盐值
        String[] dbPasswordArray = dbpassword.split("\\$");
        if(dbPasswordArray.length != 2) {
            return false;
        }
        // 盐值
        String salt = dbPasswordArray[0];
        // 最终密码
        String dbFinalPassword = dbPasswordArray[1];
        // 2.加密待验证的密码
        String finalPassword = DigestUtils.md5DigestAsHex((salt + password).getBytes(StandardCharsets.UTF_8));
        // 3.对比
        return finalPassword.equals(dbFinalPassword);
    }
}

修改原来的UserController:

//注册功能:添加用户
    @RequestMapping("/reg")
    public ResultAjax reg(Userinfo userinfo) {
        //1.参数校验
        if(userinfo.getUsername() == null || userinfo.getUsername().trim().equals("")) {
            return ResultAjax.fail(-1, "参数错误!");
        }
        if(userinfo.getPassword() == null || userinfo.getPassword().trim().equals("")) {
            return ResultAjax.fail(-1, "参数错误");
        }
        //2.处理数据
        int ret = userService.reg(userinfo.getUsername(), PasswordUtils.encrypt(userinfo.getPassword()));
        //3.返回结果
        return ResultAjax.succ(ret);
    }

    //登录接口:根据名字查找用户
    @RequestMapping("/login")
    public ResultAjax getUserByName(UserinfoVO userinfoVO, HttpServletRequest request) {
        // 1.校验参数
        if(userinfoVO == null || !StringUtils.hasLength(userinfoVO.getUsername()) ||
                !StringUtils.hasLength(userinfoVO.getPassword())) { //这里我们使用StringUtils.hasLength判断它是否为null或为空
            return ResultAjax.fail(-1, "非法参数!");
        }
        // 2.根据用户名查找对象
        Userinfo userinfo = userService.getUserByName(userinfoVO.getUsername());
        if(!PasswordUtils.decrypt(userinfoVO.getPassword(), userinfo.getPassword())) {
            //密码错误
            return ResultAjax.fail(-2, "账号或密码错误!");
        }
        // 3.将对象存储到session中
        HttpSession session = request.getSession();
        session.setAttribute(AppVariable.SESSION_USERINFO_KEY, userinfo);
        // 4.将结果返回给前端
        return ResultAjax.succ(1);
    }

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

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

相关文章

Claude 2、ChatGPT、Google Bard优劣势比较

​Claude 2&#xff1a; 优势&#xff1a;Claude 2能够一次性处理多达10万个tokens&#xff08;约7.5万个单词&#xff09;。 tokens数量反映了模型可以处理的文本长度和上下文数量。tokens越多&#xff0c;模型理解语义的能力就越强&#xff09;。它在法律、数学和编码等多个…

【EI/SCOPUS检索】第三届新媒体发展与现代化教育国际学术会议(NMDME 2023)

第三届新媒体发展与现代化教育国际学术会议&#xff08;NMDME 2023&#xff09; The 3rd International Conference on New Media Development and Modernized Education 第三届新媒体发展与现代化教育国际学术会议(NMDME 2023)将于2023年10月13-15日于西安召开。会议旨在为新…

【实测有效】朋友圈截图生成,制作朋友圈网页教程

使用教程可以自己看工具的使用手册。 Windows电脑版&#xff1a; https://imageio.jscs.top/zip/wxchat-moment-windows Mac电脑版&#xff1a; https://imageio.jscs.top/zip/wxchat-moment-mac 比如&#xff0c;你可以使用朋友圈评论生成器制作一段搞笑的评论回复&#…

【雕爷学编程】Arduino动手做(01)---干簧管传感器模块3

37款传感器与模块的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&#x…

Winrar右键没有压缩选项,怎么找回?

我们安装了WinRAR之后想要压缩文件&#xff0c;但是右键点击文件之后发现并没有WinRAR压缩选项&#xff0c;这应该如何设置才能出现右键带有压缩选项呢&#xff1f;方法如下&#xff1a; 首先打开WinRAR&#xff0c;在上面功能中点击选项 – 设置 然后我们在设置界面中切换到集…

openLayers实战(四):设置地图中心点、修改地图中心点

截至到目前阶段的功能&#xff0c;我自己实现最完整的代码 import "ol/ol.css"; import Map from "ol/Map"; import Feature from "ol/Feature"; import VectorSource from "ol/source/Vector"; import Overlay from "ol/Overlay…

2.4g无线芯片G350规格书详细介绍

G350是一款高度集成的2.4GHz无线收发芯片&#xff0c;旨在为各种应用提供低成本、高性能的无线通信解决方案。该芯片通过降低功耗&#xff0c;在保持寄存器值条件下&#xff0c;实现最低电流为5μA&#xff0c;从而显著提高了电池寿命。它内置了发射接收FIFO寄存器&#xff0c;…

【vue+el-table+el-backtop】表格结合返回顶部使用,loading局部加载

效果图: 一. 表格结合返回顶部 二. 局部loading 解决方法: 一 返回顶部 target绑定滚动dom的父元素类名就可以了. 1.如果你的表格是 固定表头 的,那滚动dom的父元素类名就是 el-table__body-wrapper <el-backtop target".el-table__body-wrapper" :visibility…

python练手项目百度网盘,python练手经典100例项目

大家好&#xff0c;小编来为大家解答以下问题&#xff0c;python练手项目 源代码 百度网盘&#xff0c;python练手项目码源百度网盘&#xff0c;现在让我们一起来看看吧&#xff01; 前言 Python 是一种面向对象、解释型、弱类型的脚本语言&#xff0c;它也是一种功能强大而完善…

班级事务管理系统设计与实现

班级事务管理系统 后端采用Spring Boot开发。 cloud分支版本正在开发中&#xff0c;后端采用Spring Cloud进行改造。 系统架构 项目采用B/S架构&#xff0c;前后端通讯采用RESTful API&#xff0c;数据格式使用Json&#xff0c;认证Token格式采用JWT。 身份认证使用Spring …

第五章 Opencv图像处理框架实战 5-8直方图与傅里叶变化

1、直方图定义 import cv2 #opencv读取的格式是BGR import numpy as np import matplotlib.pyplot as plt#Matplotlib是RGB %matplotlib inline def cv_show(img,name):cv2.imshow(name,img)cv2.waitKey()cv2.destroyAllWindows() 直方图 cv2.calcHist(images,channels,mask…

【音视频、chatGpt】h5页面最小化后,再激活后视频停住问题的解决

目录 现象 观察 解决 现象 页面有时候要切换&#xff0c;要最小化&#xff1b;短时间或者几个小时内切换回来&#xff0c;视频可以正常续上&#xff1b;而放置较长时间&#xff0c;几个小时或者一晚上&#xff0c;切换回来后&#xff0c;视频可能卡死 观察 切换页面&#x…

如何给Linux开启swap虚拟内存

查看系统内存资源 free -h 创建swap分区 dd if/dev/zero of/swapfile bs1024 count4194304dev/zero&#xff1a;是Linux的一种特殊字符设备(输入设备)&#xff0c;可以用来创建一个指定长度用于初始化的空文件&#xff0c;如临时交换文件&#xff0c;该设备无穷尽地提供0&…

工程监测仪器振弦传感器信号转换器应用于隧洞监测

工程监测仪器振弦传感器信号转换器应用于隧洞监测 隧洞建设是重大工程项目&#xff0c;监测隧洞结构和环境的变化对确保隧洞安全和运行管理至关重要。工程监测仪器是实现隧洞监测的关键设备&#xff0c;其中振弦传感器和信号转换器是非常重要的组成部分。 振弦传感器是一种专门…

AI自动驾驶

AI自动驾驶 一、自动驾驶的原理二、自动驾驶的分类三、自动驾驶的挑战四、自动驾驶的前景五、关键技术六、自动驾驶的安全问题七、AI数据与自动驾驶八、自动驾驶的AI算法总结 自动驾驶技术是近年来备受关注的热门话题。它代表了人工智能和机器学习在汽车行业的重要应用。本文将…

SAP Range 表

Range表装的一些个复杂的可以选择的值。有时候单选的值不够用的&#xff0c;用Range表。 数据结构就是Select option一样的。当你在选择屏幕定义一个selection-option的时候&#xff0c;系统自动定义个range表。那我们自己想定义个来用用咋搞&#xff1f; Range表有四列&…

在rviz中实时显示车辆轨迹

在工作空间中创建包 cd ~/catkin_ws/src catkin_create_pkg trajectory_display_example roscpp nav_msgs sensor_msgs在src文件夹下创建一个C源文件 #include <ros/ros.h> #include <nav_msgs/Odometry.h> #include <nav_msgs/Path.h> #include <senso…

roi感兴趣区域像素值统计,求roi感兴趣区域内像素值的最小值、最大值、均值、标准差(标准方差)

文章目录 1、求roi感兴趣区域内像素值的最小值、最大值minMaxLoc() 函数原型&#xff1a;&#xff08;1&#xff09;原型一&#xff1a;&#xff08;2&#xff09;原型二&#xff1a;&#xff08;3&#xff09;另外与 minMaxLoc()函数原型一&#xff0c;用法相同的函数 minMaxI…

mac ssh连接另一台window虚拟机vm

vmware配置端口映射 编辑(E) > 虚拟网络编辑器(N)... > NAT设置(S)... window防火墙&#xff0c;入站规则添加5555端口 控制面板 > 系统和安全 > Windows 防火墙>高级设置>入站规则>新建规则... tips windows查看端口命令&#xff1a;netstat -ano | f…

html实现商品图片放大镜,html图片放大镜预览

效果 实现 复制粘贴&#xff0c;修改图片路径即可使用 <!DOCTYPE html> <html><head><meta charset"UTF-8"><title>商品图片放大镜</title></head><style>body {margin: 0;padding: 0;}#app {padding: 10px;posit…