五、云对象存储以及课程分类管理模块

news2024/10/7 14:25:18

本项目的存储图片和音频文件都存储在腾讯云的服务器中,本章先介绍存储图片的方式。

存储桶文件创建:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qsr9yZMb-1683855752856)(/upload/2022/08/image-1659492454069.png)]

存储桶项目存储内容:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kEmo3xUC-1683855752857)(/upload/2022/08/image-1659492571905.png)]

APPID和密钥管理:

APPID和密钥是整个云存储最为重要的验证依据,在配置文件中应用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1JLE20RT-1683855752857)(/upload/2022/08/image-1659492762080.png)]

现在简要介绍整个云存储的配置过程(只能指定路径上传存储,密钥隐藏):

package com.lxl;

import com.alibaba.fastjson.JSON;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.http.HttpProtocol;
import com.qcloud.cos.model.PutObjectRequest;
import com.qcloud.cos.model.PutObjectResult;
import com.qcloud.cos.region.Region;

import java.io.File;

public class TestCos {

    public static void main(String[] args) {
        // 1 初始化用户身份信息(secretId, secretKey)。
        String secretId = "AKIDQjskRxPgs1HPNl0LSM3c2GF3jGbkiXCh";
        String secretKey = "###############################";
        COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
        // 2 设置 bucket 的地域, COS 地域的简称请参照 https://cloud.tencent.com/document/product/436/6224
        Region region = new Region("ap-beijing");
        ClientConfig clientConfig = new ClientConfig(region);
        // 这里建议设置使用 https 协议
        clientConfig.setHttpProtocol(HttpProtocol.https);
        // 3 生成 cos 客户端。
        COSClient cosClient = new COSClient(cred, clientConfig);

        // 指定要上传的文件
        File localFile = new File("/Users/xiaolianglu/01.png");
        // 指定文件将要存放的存储桶
        String bucketName = "ggkt-1312247070";
// 指定文件上传到 COS 上的路径,即对象键。例如对象键为folder/picture.jpg,则表示将文件 picture.jpg 上传到 folder 路径下
        String key = "/2022/07/01.png";
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, localFile);
        PutObjectResult putObjectResult = cosClient.putObject(putObjectRequest);
        System.out.println(JSON.toJSONString(putObjectResult));
    }
}

配置application.properties方便后续服务器数据调用(密钥隐藏):

spring.servlet.multipart.max-file-size=1024MB
spring.servlet.multipart.max-request-size=1024MB

tencent.cos.file.region=ap-beijing
tencent.cos.file.secretid=AKIDQjskRxPgs1HPNl0LSM3c2GF3jGbkiXCh
tencent.cos.file.secretkey=############################

tencent.cos.file.bucketname=ggkt-1312247070

创建工具类(将个人数据库数据做转换,作为接口传输数据):

package com.lxl.ggkt.vod.utils;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;

@Component
public class ConstantPropertiesUtil implements InitializingBean {

    @Value("${tencent.cos.file.region}")
    private String region;

    @Value("${tencent.cos.file.secretid}")
    private String secretId;

    @Value("${tencent.cos.file.secretkey}")
    private String secretKey;

    @Value("${tencent.cos.file.bucketname}")
    private String bucketName;
    

    public static String END_POINT;
    public static String ACCESS_KEY_ID;
    public static String ACCESS_KEY_SECRET;
    public static String BUCKET_NAME;

    @Override
    public void afterPropertiesSet() throws Exception {
        END_POINT = region;
        ACCESS_KEY_ID = secretId;
        ACCESS_KEY_SECRET = secretKey;
        BUCKET_NAME = bucketName;
    }
}

确定上传流之后,查看对应文档提供的事例,修改默认的KEY_ID和KEY_SECRECT,存储桶APP_ID,修改上传文件路径:

package com.lxl.ggkt.vod.service.impl;

import com.lxl.ggkt.vod.service.FileService;
import com.lxl.ggkt.vod.utils.ConstantPropertiesUtil;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.http.HttpProtocol;
import com.qcloud.cos.model.ObjectMetadata;
import com.qcloud.cos.model.PutObjectRequest;
import com.qcloud.cos.model.PutObjectResult;
import com.qcloud.cos.region.Region;
import org.joda.time.DateTime;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.UUID;

@Service
public class FileServiceImpl implements FileService {

    //文件上传
    @Override
    public String upload(MultipartFile file) {
        // 1 初始化用户身份信息(secretId, secretKey)。
        String secretId = ConstantPropertiesUtil.ACCESS_KEY_ID;
        String secretKey = ConstantPropertiesUtil.ACCESS_KEY_SECRET;
        COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
        // 2 设置 bucket 的地域, COS 地域的简称请参照 https://cloud.tencent.com/document/product/436/6224
        Region region = new Region(ConstantPropertiesUtil.END_POINT);
        ClientConfig clientConfig = new ClientConfig(region);
        // 这里建议设置使用 https 协议
        clientConfig.setHttpProtocol(HttpProtocol.https);
        // 3 生成 cos 客户端。
        COSClient cosClient = new COSClient(cred, clientConfig);

        // 存储桶的命名格式为 BucketName-APPID,此处填写的存储桶名称必须为此格式
        String bucketName = ConstantPropertiesUtil.BUCKET_NAME;
        // 对象键(Key)是对象在存储桶中的唯一标识。  998u-09iu-09i-333
        //在文件名称前面添加uuid值
        String key = UUID.randomUUID().toString().replaceAll("-","")
                +file.getOriginalFilename();
        //对上传文件分组,根据当前日期  /2022/11/11
        String dateTime = new DateTime().toString("yyyy/MM/dd");
        key = dateTime+"/"+key;
        try {
            //获取上传文件输入流
            InputStream inputStream = file.getInputStream();
            ObjectMetadata objectMetadata = new ObjectMetadata();
            PutObjectRequest putObjectRequest = new PutObjectRequest(
                    bucketName,
                    key,
                    inputStream,
                    objectMetadata);
            // 高级接口会返回一个异步结果Upload
            PutObjectResult putObjectResult = cosClient.putObject(putObjectRequest);
            //返回上传文件路径
            //https://ggkt-atguigu-1310644373.cos.ap-beijing.myqcloud.com/01.jpg
            String url = "https://"+bucketName+"."+"cos"+"."+ConstantPropertiesUtil.END_POINT+".myqcloud.com"+"/"+key;
            return url;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

完成对应的FileUploadController.java(上传接口):

package com.lxl.ggkt.vod.controller;

import com.lxl.ggkt.result.Result;
import com.lxl.ggkt.vod.service.FileService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

@Api(tags = "文件上传接口")
@RestController
@RequestMapping("/admin/vod/file")
//@CrossOrigin
public class FileUploadController {

    @Autowired
    private FileService fileService;

    @ApiOperation("文件上传")
    @PostMapping("upload")
    public Result uploadFile(
            @ApiParam(name = "file", value = "文件", required = true)
            @RequestParam("file") MultipartFile file){

        String url = fileService.upload(file);
        return Result.ok(url).message("上传文件成功");
    }
}

课程后端相关设计(列表展示,数据导入,数据导出):

package com.lxl.ggkt.vod.controller;


import com.lxl.ggkt.model.vod.Subject;
import com.lxl.ggkt.result.Result;
import com.lxl.ggkt.vod.service.SubjectService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

/**
 * <p>
 * 课程科目 前端控制器
 * </p>
 *
 * @author lxl
 * @since 2022-07-19
 */
@RestController
@RequestMapping("/admin/vod/subject")
//@CrossOrigin
public class SubjectController {

    @Autowired
    private SubjectService subjectService;

    @ApiOperation("课程分类列表")
    @GetMapping("getChildSubject/{id}")
    public Result getChildSubject(@PathVariable Long id){
        List<Subject> list = subjectService.selectSubjectList(id);
        return Result.ok(list);
    }

    //课程分类导出
    @ApiOperation("课程分类导出")
    @GetMapping("exportData")
    public void exportData(HttpServletResponse response){
        subjectService.exportData(response);
    }

    //课程分类导入
    @ApiOperation("课程分类导入")
    @PostMapping("importData")
    public Result importData(MultipartFile file){
        subjectService.importData(file);
        return Result.ok(null);
    }
}

数据文档的导入和导出使用EasyExcel进行操作:

首先为了清楚展示导入和导出的数据,本项目封装了SubjectEeVo类:

package com.lxl.ggkt.vo.vod;

import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;

/**
 * <p>
 * Dict
 * </p>
 *
 * @author lxl
 */
@Data
public class SubjectEeVo {

	@ExcelProperty(value = "id" ,index = 0)
	private Long id;

	@ExcelProperty(value = "课程分类名称" ,index = 1)
	private String title;

	@ExcelProperty(value = "上级id" ,index = 2)
	private Long parentId;

	@ExcelProperty(value = "排序" ,index = 3)
	private Integer sort;

}

编写service实现类(显示课程列表,导入数据,导出数据,判断是否为父子节点):

package com.lxl.ggkt.vod.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lxl.ggkt.exception.GgktException;
import com.lxl.ggkt.model.vod.Subject;
import com.lxl.ggkt.vo.vod.SubjectEeVo;
import com.lxl.ggkt.vod.listener.SubjectListener;
import com.lxl.ggkt.vod.mapper.SubjectMapper;
import com.lxl.ggkt.vod.service.SubjectService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.alibaba.excel.EasyExcel;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 课程科目 服务实现类
 * </p>
 *
 * @author atguigu
 * @since 2022-07-19
 */
@Service
public class SubjectServiceImpl extends ServiceImpl<SubjectMapper, Subject> implements SubjectService {

    @Autowired
    private SubjectListener subjectListener;

    @Override
    public List<Subject> selectSubjectList(Long id) {

        QueryWrapper<Subject> wrapper = new QueryWrapper<>();
        wrapper.eq("parent_id",id);
        List<Subject> subjectList = baseMapper.selectList(wrapper);

        for (Subject subject:subjectList){
            Long subjectId = subject.getId();
            boolean isChild = this.isChildren(subjectId);

            subject.setHasChildren(isChild);
        }
        return subjectList;
    }

    @Override
    public void exportData(HttpServletResponse response) {

        try{
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
            String fileName = URLEncoder.encode("课程分类", "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename="+ fileName + ".xlsx");

            List<Subject> subjectList = baseMapper.selectList(null);

            List<SubjectEeVo> subjectEeVoList = new ArrayList<>();
            for(Subject subject : subjectList) {
                SubjectEeVo subjectEeVo = new SubjectEeVo();
//                subjectEeVo.setId(subject.getId());
//                subjectEeVo.setParentId(subject.getParentId());

                BeanUtils.copyProperties(subject,subjectEeVo);
                subjectEeVoList.add(subjectEeVo);
            }

            EasyExcel.write(response.getOutputStream(), SubjectEeVo.class)
                    .sheet("课程分类").doWrite(subjectEeVoList);
        }catch (Exception e){
            throw new GgktException(20001,"导出失败");
        }



    }

    @Override
    public void importData(MultipartFile file) {
        try {
            EasyExcel.read(file.getInputStream(),SubjectEeVo.class,
                    subjectListener).sheet().doRead();
        } catch (IOException e) {
            throw new GgktException(20001,"导入失败");
        }
    }

    private boolean isChildren(Long subjectId) {

        QueryWrapper<Subject> wrapper = new QueryWrapper<>();
        wrapper.eq("parent_id",subjectId);
        Integer count = baseMapper.selectCount(wrapper);
        return count > 0;
    }
}

上述代码涉及到了读取监听器,它主要的作用是一行一行读取数据,调用方法插入数据,同时添加数据到数据库中:

package com.lxl.excel;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;

import java.util.Map;

@Component
public class SubjectListener extends AnalysisEventListener<SubjectEeVo> {
    @Autowired
    private SubjectMapper dictMapper;
    //一行一行读取
    @Override
    public void invoke(SubjectEeVo subjectEeVo, AnalysisContext analysisContext) {
        //调用方法添加数据库
        Subject subject = new Subject();
        BeanUtils.copyProperties(subjectEeVo,subject);
        dictMapper.insert(subject);
    }
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
    }
}

最后查看对应的controller接口,导入,导出controller直接调用SubjectServiceImp接口即可。

package com.lxl.ggkt.vod.controller;


import com.lxl.ggkt.model.vod.Subject;
import com.lxl.ggkt.result.Result;
import com.lxl.ggkt.vod.service.SubjectService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

/**
 * <p>
 * 课程科目 前端控制器
 * </p>
 *
 * @author lxl
 * @since 2022-07-19
 */
@RestController
@RequestMapping("/admin/vod/subject")
//@CrossOrigin
public class SubjectController {

    @Autowired
    private SubjectService subjectService;

    @ApiOperation("课程分类列表")
    @GetMapping("getChildSubject/{id}")
    public Result getChildSubject(@PathVariable Long id){
        List<Subject> list = subjectService.selectSubjectList(id);
        return Result.ok(list);
    }

    //课程分类导出
    @ApiOperation("课程分类导出")
    @GetMapping("exportData")
    public void exportData(HttpServletResponse response){
        subjectService.exportData(response);
    }

    //课程分类导入
    @ApiOperation("课程分类导入")
    @PostMapping("importData")
    public Result importData(MultipartFile file){
        subjectService.importData(file);
        return Result.ok(null);
    }
}


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

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

相关文章

超大规模数据库集群保稳系列之一:高可用系统

总第560篇 2023年 第012篇 基于过去多年在大规模数据集群保稳方面的实践经验&#xff0c;我们希望能够跟业界进行一些技术交流&#xff0c;美团技术团队举办了第75期技术沙龙。我们邀请到了美团研究员赵应钢担任出品人&#xff0c;同时请邀请到张洪、王占全、蔺瑞超、沈裕锋等4…

【Linux】进程信号(上)

文章目录 1.信号概念理解信号产生信号保存信号处理 2. 信号的产生证明输入 CTRL C 就是向前台进程发送信号signal 函数内部参数传递的理解对于信号自定义动作的捕捉问题 3.信号产生的方式1.从键盘输入2.使用系统调用向进程发送信号killraiseabort 3.由软件条件产生信号 1.信号概…

轻博客Plume的搭建

什么是 Plume &#xff1f; Plume 是一个基于 ActivityPub 的联合博客引擎。它是用 Rust 编写的&#xff0c;带有 Rocket 框架&#xff0c;以及 Diesel 与数据库交互。前端使用 Ructe模板、WASM 和SCSS。 反向代理 假设我们实际访问地址为&#xff1a; https://plume.laosu.ml…

Map和WeakMap对象的用法(JS)

&#x1f4dd;个人主页&#xff1a;爱吃炫迈 &#x1f48c;系列专栏&#xff1a;数据结构与算法 &#x1f9d1;‍&#x1f4bb;座右铭&#xff1a;道阻且长&#xff0c;行则将至&#x1f497; 文章目录 &#x1f338;Mapkey创建map的其他方式Map常用方法Map结构原生提供三个遍历…

项目成本管理

定义&#xff1a;项目各个成本的总和 作用&#xff1a;在预算范围内完成项目 考点&#xff1a; 直接成本是指一个由项目组承担的费用&#xff0c;例如员工的工资&#xff0c;电脑等硬件费用。 间接成本是指由多个项目组承担的费用&#xff0c;例如租金&#xff0c;水电费&am…

社交媒体中的“点赞”“喜欢”是如何存储在数据库中的?

你有没有想过 Instagram、Twitter、Facebook 或任何社交媒体平台如何跟踪谁喜欢你的帖子&#xff1f;让我们在这篇文章中弄清楚&#xff01; 1&#xff1a;序言 最近&#xff0c;我受邀在一个名为“CityJS”的活动中发言。但问题在于&#xff1a;我是 PHP 开发人员。我根本不懂…

Palo Alto Networks利用基于机器学习的自动化网络安全解决方案

“ Palo Alto Networks利用第三代英特尔至强可扩展处理器和部署在云中的英特尔软件&#xff0c;为其云端安全服务提供强有力的支持&#xff0c;从而提升机器学习和推断的性能。 面对恶意软件攻击的快速演变和难以捉摸的特性&#xff0c;企业网络安全团队面临着巨大的挑战。恶意…

"五步走"的MES系统改进战略,让ERP不再是面子工程

随着制造业信息化进程的不断推进&#xff0c;大部分的制造业企业都开始意识到&#xff0c;生产技术的领先和生产流程的有效管理是其发展的重要一环&#xff0c;有些企业已经或者正在实施的 ERP系统。但是&#xff0c;从相关部门的数据来看&#xff0c; ERP在分散型制造业企业中…

算法(一)—— 回溯(1)

文章目录 前言1 77 组合2 77 组合优化 前言 1、回溯法解决的问题都可以抽象为树形结构&#xff08;N叉树&#xff09;&#xff0c;使用树形结构来理解回溯。 2、回溯法解决的都是在集合中递归查找子集&#xff0c;集合的大小就构成了树的宽度&#xff0c;递归的深度&#xff…

FPGA基于GS2971/GS2972实现SDI视频收发 提供工程源码和技术支持

目录 1、前言2、我目前已有的SDI编解码方案3、GS2971/GS2972芯片解读GS2971解读GS2972解读 4、详细设计方案5、vivado工程1解读硬件逻辑工程软件SDK工程 6、vivado工程2解读硬件逻辑工程软件SDK工程 7、上板调试验证8、福利&#xff1a;工程代码的获取 1、前言 FPGA实现SDI视频…

多尺度深度特征(上):多尺度特征学习才是目标检测精髓(干货满满,建议收藏)...

计算机视觉研究院专栏 作者&#xff1a;Edison_G 深度特征学习方案将重点从具有细节的具体特征转移到具有语义信息的抽象特征。它通过构建多尺度深度特征学习网络 (MDFN) 不仅考虑单个对象和局部上下文&#xff0c;还考虑它们之间的关系。 公众号ID&#xff5c;ComputerVisionG…

零售行业公有云信息安全探讨

新钛云服已累计为您分享744篇技术干货 ⚠️公有云安全事件⚠️ 最近小半年接连处理了几起零售行业公有云安全事件&#xff0c;都是因为某种原因造成的数据泄露。有防护措施不当的&#xff0c;也有因为应用漏洞泄露被黑的&#xff0c;也有内部人员不慎造成的。 事后总结发现主要…

第一章 IRIS 基础知识:使用互操作性制作连接系统

文章目录 第一章 IRIS 基础知识&#xff1a;使用互操作性制作连接系统为什么要连接系统&#xff1f;介绍Productions 第一章 IRIS 基础知识&#xff1a;使用互操作性制作连接系统 本文介绍了如何将系统与 IRIS 数据平台互操作性产品连接在一起。 为什么要连接系统&#xff1f…

Golang每日一练(leetDay0063) 最大数、重复的DNA序列

目录 179. 最大数 Largest Number &#x1f31f;&#x1f31f; 187. 重复的DNA序列 Repeated DNA Sequences &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏…

博客管理系统--博客列表页

准备工作设计数据库封装数据库操作创建实体类数据库增删查改操作&#xff08;写法几乎很相似&#xff09;前后端交互 准备工作 博客管理系统前端部分在学习前端、css、js部分实现&#xff1b;现在我们将完成后端工作;并且部署云服务上&#xff1b;使其能让所有联网的人使用。 …

【代码】一个LVQ神经网络的详解实例

本站原创文章&#xff0c;转载请说明来自《老饼讲解-BP神经网络》bp.bbbdata.com LVQ神经网络是用于样本分类的一个常用算法&#xff0c;本文先简单回顾LVQ神经网络是什么&#xff0c;然后展示如何用matlab工具箱来训练一个LVQ神经网络 目录 一. LVQ神经网络简介 1.1 LVQ神经…

“首次公开一年连升两级的晋升大法”!

见字如面&#xff0c;我是军哥&#xff01; 今天把江湖上失传已久的技术人晋升大法分享给各位&#xff0c;据说当年雷jun总就是用了此法三年就干到了技术高管&#xff0c;请各位务必做好学习和分享动作。 说真的&#xff0c;用好这三个办法&#xff0c;就算在 IT 大厂一年都可以…

脑挫裂伤是什么?脑挫裂伤的4大症状要警惕!

脑挫裂伤是头部遭受暴力而引起的原发性脑器质性损伤。脑挫裂伤既可发生于着力点的脑组织&#xff0c;也可发生于对冲部位。脑挫裂伤病人的临床表现可以由于损伤部位&#xff0c;范围&#xff0c;程度的不同而有差异。受伤较轻的可以只有轻微的症状而受伤&#xff0c;较重的可以…

这玩意真的有用吗?对,是的!Kotlin 的 Nothing 详解

视频先行 下面是视频内容的脚本文案原稿分享。 文案原稿 Kotlin 的 Nothing 类&#xff0c;无法创建出任何实例&#xff1a; public class Nothing private constructor() 所以所有 Nothing 类型的变量或者函数&#xff0c;都找不到可用的值&#xff1a; val nothing: Nothing …

电脑视频录屏软件哪个好用 电脑视频录屏怎么录屏

录屏是我们的工作和生活中非常大的一个需求&#xff0c;尤其对于专业的视频制作者来说&#xff0c;经常需要录制屏幕和编辑视频&#xff0c;因此找到便捷好用的录屏软件非常重要。今天就来分享一下电脑视频录屏软件哪个好用&#xff0c;电脑视频录屏怎么录屏。 一、电脑视频录…