Spring Cloud + Vue前后端分离-第10章 基于阿里云OSS的文件上传

 源代码在GitHub - 629y/course: Spring Cloud + Vue前后端分离-在线课程

Spring Cloud + Vue前后端分离-第10章 基于阿里云OSS的文件上传

前面介绍的文件上传是基于本地文件服务器的文件上传,但是自己搭文件服务器会有很多运维的问题,比如磁盘满了要扩容,高峰期要增加带宽,低谷期要减少带宽,为了安全,我们还要对文件做备份等等。

所以一般会选择云平台来存储文件,云平台有很多,比如阿里云、腾讯云、百度云等,云平台做的最好的是亚马逊,我们这里选择国内最大的阿里云。

10-1 阿里云OSS简介

面向海量数据规模的分布式存储服务

从浏览器通过ECS( 服务器)访问OSS,算内网访问,不算流量费,但是要考虑ECS的带宽问题。从浏览器直接访问OSS资源,算外网访问OSS,需付流量费,但是可以不用考虑带宽。

10-2 基于OSS接口的文件上传

阿里云OSS控台的使用

bucket:存储空间,名称必须是全阿里云唯一

这个文件地址可以直接打开观看

基于OSS接口实现文件上传

1.大文件断点续传与极速秒传:基于OSS接口实现文件上传,增加文件追加上传oss-append 和简单上传oss-simple

pom.xml

OssController.java

package com.course.file.controller.admin;import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.AppendObjectRequest;
import com.aliyun.oss.model.AppendObjectResult;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectRequest;
import com.course.server.dto.FileDto;
import com.course.server.dto.ResponseDto;
import com.course.server.enums.FileUseEnum;
import com.course.server.service.FileService;
import com.course.server.util.Base64ToMultipartFile;
import com.course.server.util.UuidUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;import javax.annotation.Resource;
import java.io.ByteArrayInputStream;@RestController
@RequestMapping("/admin")
public class OssController {private static final Logger LOG = LoggerFactory.getLogger(OssController.class);@Value("${oss.accessKeyId}")private String accessKeyId;@Value("${oss.accessKeySecret}")private String accessKeySecret;@Value("${oss.endpoint}")private String endpoint;@Value("${oss.bucket}")private String bucket;@Value("${oss.domain}")private String ossDomain;public static final String BUSINESS_NAME = "文件上传";@Resourceprivate FileService fileService;@PostMapping("/oss-append")public ResponseDto fileUpload(@RequestBody FileDto fileDto) throws Exception {LOG.info("上传文件开始");String use = fileDto.getUse();String key = fileDto.getKey();String suffix = fileDto.getSuffix();Integer shardIndex = fileDto.getShardIndex();Integer shardSize = fileDto.getShardSize();String shardBase64 = fileDto.getShard();MultipartFile shard = Base64ToMultipartFile.base64ToMultipart(shardBase64);// 保存文件到本地FileUseEnum useEnum = FileUseEnum.getByCode(use);//        //如果文件夹不存在则创建String dir = useEnum.name().toLowerCase();
//        File fullDir = new File(FILE_PATH + dir);
//        if (!fullDir.exists()) {
//            fullDir.mkdir();
//        }//        String path = dir + File.separator + key + "." + suffix + "." + fileDto.getShardIndex();String path = new StringBuffer(dir).append("/").append(key).append(".").append(suffix).toString(); // course\6sfSqfOwzmik4A4icMYuUe.mp4
//        String localPath = new StringBuffer(path)
//                .append(".")
//                .append(fileDto.getShardIndex())
//                .toString(); // course\6sfSqfOwzmik4A4icMYuUe.mp4.1
//        String fullPath = FILE_PATH + localPath;
//        File dest = new File(fullPath);
//        shard.transferTo(dest);
//        LOG.info(dest.getAbsolutePath());// 创建OSSClient实例。OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);ObjectMetadata meta = new ObjectMetadata();
// 指定上传的内容类型。meta.setContentType("text/plain");// 通过AppendObjectRequest设置多个参数。AppendObjectRequest appendObjectRequest = new AppendObjectRequest(bucket, path, new ByteArrayInputStream(shard.getBytes()), meta);// 通过AppendObjectRequest设置单个参数。// 设置存储空间名称。//appendObjectRequest.setBucketName("<yourBucketName>");// 设置文件名称。//appendObjectRequest.setKey("<yourObjectName>");// 设置待追加的内容。有两种可选类型:InputStream类型和File类型。这里为InputStream类型。//appendObjectRequest.setInputStream(new ByteArrayInputStream(content1.getBytes()));// 设置待追加的内容。有两种可选类型:InputStream类型和File类型。这里为File类型。//appendObjectRequest.setFile(new File("<yourLocalFile>"));// 指定文件的元信息,第一次追加时有效。//appendObjectRequest.setMetadata(meta);// 第一次追加。// 设置文件的追加位置。
//        appendObjectRequest.setPosition(0L);appendObjectRequest.setPosition((long) ((shardIndex - 1) * shardSize));AppendObjectResult appendObjectResult = ossClient.appendObject(appendObjectRequest);// 文件的64位CRC值。此值根据ECMA-182标准计算得出。System.out.println(appendObjectResult.getObjectCRC());System.out.println(JSONObject.toJSONString(appendObjectResult));//        // 第二次追加。
//        // nextPosition指明下一次请求中应当提供的Position,即文件当前的长度。
//        appendObjectRequest.setPosition(appendObjectResult.getNextPosition());
//        appendObjectRequest.setInputStream(new ByteArrayInputStream(content2.getBytes()));
//        appendObjectResult = ossClient.appendObject(appendObjectRequest);
//
//        // 第三次追加。
//        appendObjectRequest.setPosition(appendObjectResult.getNextPosition());
//        appendObjectRequest.setInputStream(new ByteArrayInputStream(content3.getBytes()));
//        appendObjectResult = ossClient.appendObject(appendObjectRequest);// 关闭OSSClient。ossClient.shutdown();LOG.info("保存文件记录开始");fileDto.setPath(path);fileService.save(fileDto);ResponseDto responseDto = new ResponseDto();fileDto.setPath(ossDomain + path);responseDto.setContent(fileDto);//        if (fileDto.getShardIndex().equals(fileDto.getShardTotal())) {
//            this.merge(fileDto);
//        }return responseDto;}@PostMapping("/oss-simple")public ResponseDto fileUpload(@RequestParam MultipartFile file, String use) throws Exception {LOG.info("上传文件开始");FileUseEnum useEnum = FileUseEnum.getByCode(use);String key = UuidUtil.getShortUuid();String fileName = file.getOriginalFilename();String suffix = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();String dir = useEnum.name().toLowerCase();String path = dir + "/" + key + "." + suffix;// 创建OSSClient实例。OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);// 创建PutObjectRequest对象。
//        String content = "Hello OSS";// <yourObjectName>表示上传文件到OSS时需要指定包含文件后缀在内的完整路径,例如abc/efg/123.jpg。PutObjectRequest putObjectRequest = new PutObjectRequest(bucket, path, new ByteArrayInputStream(file.getBytes()));// 如果需要上传时设置存储类型与访问权限,请参考以下示例代码。// ObjectMetadata metadata = new ObjectMetadata();// metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());// metadata.setObjectAcl(CannedAccessControlList.Private);// putObjectRequest.setMetadata(metadata);// 上传字符串。ossClient.putObject(putObjectRequest);//        LOG.info("保存文件记录开始");
//        fileDto.setPath(path);
//        fileService.save(fileDto);ResponseDto responseDto = new ResponseDto();FileDto fileDto = new FileDto();fileDto.setPath(ossDomain + path);responseDto.setContent(fileDto);return responseDto;}
}

application.properties

big-file.vue

file.vue

演示:头像上传是用的单文件传输到OSS;视频上传是用的分片传输到OSS

teacher.vue

测试

上传视频和头像

10-3{white}阿里云视频点播服务介绍{white}{white}

阿里云视频点播服务介绍

视频点播控台的使用

视频点播帮助文档概览

10-4 基于OSS原生SDK上传视频到点播服务

VOD上传并加密转码

VOD上传并加密转码

1.视频加密与授权播放:基于 OSS原生SDK上传视频到点播服务,官方示例代码

pom.xml(course)

pom.xml(server)

VodUtil.java

package com.course.server.util;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.OSSClient;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.FormatType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.vod.model.v20170321.*;
import org.apache.commons.codec.binary.Base64;import java.io.File;
import java.io.InputStream;public class VodUtil {/*** 使用AK初始化VOD客户端* @param accessKeyId* @param accessKeySecret* @return* @throws ClientException*/public static DefaultAcsClient initVodClient(String accessKeyId, String accessKeySecret) throws ClientException {// 点播服务接入区域,国内请填cn-shanghai,其他区域请参考文档[点播中心](~~98194~~)String regionId = "cn-shanghai";DefaultProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);DefaultAcsClient client = new DefaultAcsClient(profile);return client;}/*** 获取视频上传地址和凭证* @param vodClient* @return* @throws ClientException*/public static CreateUploadVideoResponse createUploadVideo(DefaultAcsClient vodClient, String fileName) throws ClientException {CreateUploadVideoRequest request = new CreateUploadVideoRequest();request.setFileName(fileName);request.setTitle(fileName);//request.setDescription("this is desc");//request.setTags("tag1,tag2");
//        request.setCoverURL("http://vod.aliyun.com/test_cover_url.jpg");
//        request.setCateId(1000115308L);
//        request.setTemplateGroupId("78fffb8c0c2426efd5baaaafed76fe36");//request.setWorkflowId("");//request.setStorageLocation("");//request.setAppId("app-1000000");//设置请求超时时间request.setSysReadTimeout(1000);request.setSysConnectTimeout(1000);return vodClient.getAcsResponse(request);}/*** 使用上传凭证和地址初始化OSS客户端(注意需要先Base64解码并Json Decode再传入)* @param uploadAuth* @param uploadAddress* @return*/public static OSSClient initOssClient(JSONObject uploadAuth, JSONObject uploadAddress) {String endpoint = uploadAddress.getString("Endpoint");String accessKeyId = uploadAuth.getString("AccessKeyId");String accessKeySecret = uploadAuth.getString("AccessKeySecret");String securityToken = uploadAuth.getString("SecurityToken");return new OSSClient(endpoint, accessKeyId, accessKeySecret, securityToken);}/*** 简单上传* @param ossClient* @param uploadAddress* @param inputStream*/public static void uploadLocalFile(OSSClient ossClient, JSONObject uploadAddress, InputStream inputStream){String bucketName = uploadAddress.getString("Bucket");String objectName = uploadAddress.getString("FileName");// 单文件上传ossClient.putObject(bucketName, objectName, inputStream);/* 视频点播不支持追加上传// 追加上传ObjectMetadata meta = new ObjectMetadata();meta.setContentType("text/plain");AppendObjectRequest request = new AppendObjectRequest(bucketName, objectName, file, meta);request.setPosition(0L);ossClient.appendObject(request);*/}/*** 上传本地文件* @param ossClient* @param uploadAddress* @param localFile*/public static void uploadLocalFile(OSSClient ossClient, JSONObject uploadAddress, String localFile){String bucketName = uploadAddress.getString("Bucket");String objectName = uploadAddress.getString("FileName");File file = new File(localFile);// 单文件上传ossClient.putObject(bucketName, objectName, file);/* 视频点播不支持追加上传// 追加上传ObjectMetadata meta = new ObjectMetadata();meta.setContentType("text/plain");AppendObjectRequest request = new AppendObjectRequest(bucketName, objectName, file, meta);request.setPosition(0L);ossClient.appendObject(request);*/}/*** 刷新上传凭证* @param vodClient* @return* @throws ClientException*/public static RefreshUploadVideoResponse refreshUploadVideo(DefaultAcsClient vodClient) throws ClientException {RefreshUploadVideoRequest request = new RefreshUploadVideoRequest();request.setAcceptFormat(FormatType.JSON);request.setVideoId("VideoId");//设置请求超时时间request.setSysReadTimeout(1000);request.setSysConnectTimeout(1000);return vodClient.getAcsResponse(request);}/*** 获取源文件信息* @param client 发送请求客户端* @return GetMezzanineInfoResponse 获取源文件信息响应数据* @throws Exception*/public static GetMezzanineInfoResponse getMezzanineInfo(DefaultAcsClient client, String videoId) throws Exception {GetMezzanineInfoRequest request = new GetMezzanineInfoRequest();request.setVideoId(videoId);//源片下载地址过期时间request.setAuthTimeout(3600L);return client.getAcsResponse(request);}/*** 获取播放凭证函数* @param client* @return* @throws Exception*/public static GetVideoPlayAuthResponse getVideoPlayAuth(DefaultAcsClient client, String videoId) throws Exception {GetVideoPlayAuthRequest request = new GetVideoPlayAuthRequest();request.setVideoId(videoId);return client.getAcsResponse(request);}public static void main(String[] argv) {//您的AccessKeyIdString accessKeyId = "xxxxxxxxxxxxx";//您的AccessKeySecretString accessKeySecret = "xxxxxxxxxxxxx";//需要上传到VOD的本地视频文件的完整路径,需要包含文件扩展名String localFile = "/Users/zhaoxiaoyun/Downloads/fourcats.mp4";try {// 初始化VOD客户端并获取上传地址和凭证DefaultAcsClient vodClient = initVodClient(accessKeyId, accessKeySecret);String fileName = "test.mp4";CreateUploadVideoResponse createUploadVideoResponse = createUploadVideo(vodClient, fileName);// 执行成功会返回VideoId、UploadAddress和UploadAuthString videoId = createUploadVideoResponse.getVideoId();JSONObject uploadAuth = JSONObject.parseObject(Base64.decodeBase64(createUploadVideoResponse.getUploadAuth()), JSONObject.class);JSONObject uploadAddress = JSONObject.parseObject(Base64.decodeBase64(createUploadVideoResponse.getUploadAddress()), JSONObject.class);// 使用UploadAuth和UploadAddress初始化OSS客户端OSSClient ossClient = initOssClient(uploadAuth, uploadAddress);// 上传文件,注意是同步上传会阻塞等待,耗时与文件大小和网络上行带宽有关uploadLocalFile(ossClient, uploadAddress, localFile);System.out.println("上传视频成功, VideoId : " + videoId); // 7d6b8c07ab48456e932187080f42e88fGetMezzanineInfoResponse response = new GetMezzanineInfoResponse();response = getMezzanineInfo(vodClient, videoId);System.out.println("获取视频信息, response : " + JSON.toJSONString(response));} catch (Exception e) {System.out.println("上传视频失败, ErrorMessage : " + e.getLocalizedMessage());}}
}

 记得修改自己的

VOD上传到指定分类,并自动转码

1.视频加密与授权播放:基于 OSS原生SDK上传视频到点播服务,上传到指定分类,并自动转码

VodUtil.java

视频点播不支持追加上传

1.视频加密与授权播放:基于 OSS原生SDK上传视频到点播服务,视频点播不支持追加上传

官方示例中,用的是简单上传putObject ,我们要改成我们需要的追加上传

但是有严格封装编码要求的文件类型,都是不支持追加方式上传的;比如音视频、图片等,上传到视频点播不支持追加上传

VodUtil.java

效果就不演示了,感兴趣的自己尝试一下,把这段代码打开,把上面的单文件上传注释,然后看效果,是一直在上传,但是无法上传成功!

项目集成VOD上传

小节增加vod字段

1.视频加密与授权播放:小节增加vod字段

由于视频点播不支持追加上传,我们只能改成单文件传

all.sql

generatorConfig.xml

SectionDto.java

section.vue

简单快速的方案:在阿里云控台做文件上传和转码,然后将vod复制到我们自己的控台

增加vod组件,用于上传视频到视频点播服务

1.视频加密与授权播放:增加vod组件,用于上传视频到视频点播服务

big-file.vue

vod组件就是对big-file组件做了一层封装。程序员天然喜欢对代码做封装,要注意度,封装得越多,灵活度就越低

vod.vue

<template><big-file v-bind:input-id="inputId"v-bind:text="text"v-bind:suffixs="suffixs"v-bind:use="use"v-bind:after-upload="afterUpload"v-bind:shard-size="shardSize"v-bind:url="'oss-append'"></big-file>
</template><script>
import BigFile from "./big-file";
export default {name: "vod",components: {BigFile},props: {text: {default: "上传VOD"},inputId: {default: "vod-upload"},suffixs: {default: []},use: {default: ""},shardSize: {default: 50 * 1024},afterUpload: {type: Function,default: null},},
}
</script>

目前为止,vod和big-file功能一样,需要先测试下vod组件是否可用

section.vue

 我们在js里对video赋值,vue会监听到值的变化,并渲染视频控件。如果还没渲染完,我们就去获取时长,这时就会得到NaN,所以需要加延时获取

测试,记得先清空数据库和oss文件

file表增加vod字段

1.视频加密与授权播放:file表增加vod字段

all.sql

generatorConfig.xml

FileDto.java

增加视频点播文件上传功能

1.视频加密与授权播放:增加视频点播文件上传功能

application.properties

vod.vue

由于视频点播不支持追加上传,所以使用vod组件进行上传的,只能有一个分片

section.vue

获取到的可播放地址是有时效的,所以就算保存到数据库也会过期,没用。以后会根据vod来播放。

文件检查时,根据是否是视频点播文件来获取视频信息

1.视频加密与授权播放:文件检查时,根据是否是视频点播文件来获取视频信息

UploadController.java

 测试 

10-6 视频授权播放功能开发

阿里云播放器的基本使用

1.视频加密与授权播放:集成阿里云播放器,制作player播放器组件

player.vue

<template><div v-bind:id="playerId">
<!--要做成动态的,所以不在这个地方写了-->
<!--    <div class="prism-player" id="J_prismPlayer"></div>--></div>
</template>
<script>
export default {name: "player",props: {playerId: {default: "player-div"},},data: function () {return {aliPlayer: {},//播放器实例}},methods: {playUrl(url) {let _this = this;console.log("开始播放:", url);//如果已经有播放器了,则将播放器div删除if (_this.aliPlayer) {_this.aliPlayer = null;$("#J_prismPlayer").remove();}// 初始化播放器$("#" + _this.playerId).append("<div class=\"prism-player\" id=\"J_prismPlayer\"></div>");_this.aliPlayer = new Aliplayer({id: "J_prismPlayer",width: '100%',autoplay: false,source: url,cover: 'http://imooc-coursemac.oss-cn-shanghai.aliyuncs.com/头像1.jpg',}, function (player) {console.log('播放器创建好了。')});},}
}
</script>

index.html

section.vue

 video控件隐藏掉,不要删除,要通过它获取时长。也可以通过视频点播的API去获取视频时长。

测试

功能更强,可以倍速等等

获取vod授权码并授权播放

1.视频加密与授权播放:获取vod授权码并授权播放,需要在存储管理中把权限设置成公共读

默认是私有的,更改为公共读

VodUtil.java

VodController.java

player.vue

一个页面可能会放多个player组件,所以需要把id做成动态变化的,一个页面的元素,id值要是唯一的。

modal-player.vue

带有模态框的播放器组件modal-player,里面包含了player组件

<template><div id="player-modal" class="modal" tabindex="-1" role="dialog"><div class="modal-dialog" role="document"><div class="modal-content"><div class="modal-body"><player v-bind:player-id="'modal-player-div'"ref="player"></player></div></div></div></div>
</template>
<script>
import Player from "./player";
export default {name: 'modal-player',components: {Player},data: function () {return {aliPlayer: {}, // 播放器实例}},methods: {playUrl(url) {let _this = this;_this.$refs.player.playUrl(url);},playVod(vod) {let _this = this;_this.$refs.player.playVod(vod);$("#player-modal").modal("show");}}
}
</script><style scoped>
#player-modal .modal-body {padding: 0;
}
</style>

section.vue

测试

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第一节 Linux操作系统简介

一&#xff1a;Linux的发展历史 1&#xff1a;1969年美国贝尔实验室的肯• 汤普森开发出了UNIX系统。   2&#xff1a;1973年&#xff0c;UNIX系统的绝大部分源代码用C语言重写&#xff0c;为提高UNIX系统的可移植性打下基础。   3&#xff1a;Linux系统诞生于1991年&#…

前端实现埋点监控

文章目录 一、埋点&监控二、前置知识1. 区分JS模块化2. rollup3. History3.1 history.pushState()3.2 history.replaceState()3.3 popstate事件 4. JS二进制4.1 Blob4.2 File4.3 FileReader4.4 ArrayBuffer4.5 Object URL4.6 Base644.7 格式转换 5. sendBeacon发送请求 三、…

【AI】人类视觉感知特性与深度学习模型(2/2)

目录 二、人类视觉感知特性对深度学习模型的启发 2.1 视觉关注和掩盖与调节注意力模型的关系 1.视觉关注和掩盖 2. 注意力机制模型 2.2 对比敏感度与U形网络的联系 2.3 非局部约束与点积注意力的联系 续上节 【AI】人类视觉感知特性与深度学习模型&#xff08;1/2&#…

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK设置相机本身的数据保存(CustomData)功能(C#)

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK设置相机本身的数据保存&#xff08;CustomData&#xff09;功能&#xff08;C#&#xff09; Baumer工业相机Baumer工业相机的数据保存&#xff08;CustomData&#xff09;功能的技术背景CameraExplorer如何使用图像剪切&#xff…

【后端】Docker学习笔记

文章目录 Docker一、Docker安装&#xff08;Linux&#xff09;二、Docker概念三、Docker常用命令四、数据卷五、自定义镜像六、网络七、DockerCompose Docker Docker是一个开源平台&#xff0c;主要基于Go语言构建&#xff0c;它使开发者能够将应用程序及其依赖项打包到一个轻…

Visual Studio 2015 中 SDL2 开发环境的搭建

Visual Studio 2015 中 SDL2 开发环境的搭建 Visual Studio 2015 中 SDL2 开发环境的搭建新建控制台工程拷贝并配置 SDL2 开发文件拷贝 SDL2 开发文件配置 SDL2 开发文件 测试SDL2 开发文件的下载链接 Visual Studio 2015 中 SDL2 开发环境的搭建 新建控制台工程 新建 Win32 …

vue-springboot基于JavaWeb的汽配汽车配件销售采购管理系统

过对知识内容的学习研究&#xff0c;进而设计并实现一个基于JavaWeb的汽配销售管理系统。系统能实现的主要功能应包括&#xff1b;汽车配件、销售订单、采购订单、采购入库等的一些操作&#xff0c;ide工具&#xff1a;IDEA 或者eclipse 编程语言: java 数据库: mysql5.7 框架&…

OFDM——PAPR减小

文章目录 前言一、PAPR 减小二、MATLAB 仿真1、OFDM 信号的 CCDF①、MATLAB 源码②、仿真结果 2、单载波基带/通频带信号的 PAPR①、MATLAB 源码②、仿真结果 3、时域 OFDM 信号和幅度分布①、MATLAB 源码②、仿真结果 4、Chu 序列和 IEEE802.16e 前导的 PAPR①、MATLAB 源码②…

C#编程-编写和执行C#程序

编写和执行C#程序 可以使用Windows记事本应用程序来编写C#程序。在记事本应用程序中创建C#程序后,您需要编译并执行该程序以获得所需的输出。编译器将程序的源代码转换为机器代码,这样计算机就能理解程序中的指令了。 注释 除了记事本,您还可以使用任何其他文本编辑器来编写…

MAC 中多显示器的设置(Parallels Desktop)

目录 一、硬件列表&#xff1a; 二、线路连接&#xff1a; 三、软件设置&#xff1a; 1. 设置显示器排列位置及显示参数 2. 分别设置外接显示器为&#xff1a;扩展显示器&#xff0c;内建显示器为主显示器 3. 设置Parallels Desktop屏幕参数 四、结果 一、硬件列表&a…

C++实现定积分运算

文章目录 题目代码 题目 代码 #include <iostream> #include <cmath> #include <functional>using namespace std;// 定积分函数 double integrate(function<double(double)> func, double a, double b, int num_intervals) {double h (b - a) / num…

使用STM32实现多设备UART通信指南

本文将介绍如何在STM32上实现多设备UART通信&#xff0c;包括配置多个UART接口、数据的发送和接收&#xff0c;以及如何有效地进行多设备通信。我们将使用STM32CubeMX和HAL库来演示配置过程&#xff0c;并给出相关的示例代码和技巧。UART&#xff08;Universal Asynchronous Re…