阿里云迁移AWS视频点播技术攻坚

文章目录

  • 🐷 背景
  • 🦥 简述
  • 🐥 Aws服务
  • 🦜 AWS CloudFormation
  • 🐞 问题
  • 🐉 落地方案
  • 🦉 Aws vs Aliyun
  • 🍄 避坑指南

🐷 背景

由于AWS整体成本略低于阿里云,公司决定将文件存储模块的业务迁移至AWS,迁移过程中发现,关于视频播放,转码之前阿里云已自动集成相关功能,但Aws并没有原生支持,为此踩了很多坑!

🦥 简述

本次主要是完成视频上传Aws并完成转码播放功能,基础功能已实现,但速度方面略低于阿里云,后续还需要优化改进。

Aws指南

🐥 Aws服务

  • Amazon Elemental MediaConvert 将媒体文件从源格式转码为可在智能手机、平板电 脑、PC 和其他设备播放的流媒体格式。
  • Amazon CloudFront 加速将您的视频内容分发给最终用户。
  • Amazon Step Functions 讲执行不同任务的离散函数整合为工作流。
  • Amazon Lambda 让您无需预置或管理服务器即可运行代码。
  • Amazon Simple Storage Service (Amazon S3) 用于对象存储。
  • Amazon DynamoDB 用于追踪源和目标文件的元数据以及工作流程的进度。
  • Amazon CloudWatch 用于追踪转码任务。
  • Amazon Simple Queue Service (Amazon SQS) 用于捕获工作流的输出。
  • Amazon Simple Notification Service (Amazon SNS) 用于发送发布、编码和错误的通知信息。

按照aws解决方案库提供的方案,部署大概需要20分钟,部署完成后怎么使用,部署过程都做了哪些事情,整体流程是怎么样的,从哪些环节可以接入自定义的逻辑和程序。
除了以上部署方案提到的服务,本身该部署方案也是一个服务AWS CloudFormation。
根据本次部署和开发经历,建议需要使用该解决方案的小伙伴不要着急部署,先通过各种学习途径了解以上服务,尤其是标红的服务是做什么的,什么应用场景下使用。

B站学习资料:AWS认证解决方案全栈架构师 中文课程

🦜 AWS CloudFormation


CloudFormation是一个模板,该模板包含一套解决方案所需的所有资源,将一个通用解决方案配置成一个模板,需要再次部署相同解决方案则使用模板即可快速实现复制。启动堆栈可对模板进行部署实施。

堆栈部署完成,删除堆栈可将堆栈相关的所有资源进行一并删除,想部署或尝试的可以放心实施,不用删除即可!

Aws点播使用的模板


整体转码流程

官方服务说明


堆栈部署产生的aws服务说明


mediaconvert作业模板


Lambda

Lambda很重要!Lambda很重要!Lambda很重要!


IAM Role

IAM很重要!IAM很重要!IAM很重要!

SQS

SQS消息队列,转码完成后会向消息队列写入消息,业务侧可读取消息实现转码完成事件通知。

🐞 问题

使用Workflow trigger选项VideoFile的话,部署的方案会默认转码成m3u8文件;会默认使用3个模板进行转码(MediaConvert_Template_2160p、MediaConvert_Template_1080p、MediaConvert_Template_720p)于现有阿里云转码成单个mp4文件不一致,现有业务对多种品质输出没有要求,需要能够定制转码输出格式。

备注:如要实现类似点播清晰度选择的话可考虑多个质量转码模板。
解决方案在github上

🐉 落地方案

调整堆栈参数

Workflow trigger选择MetadataFile ,工作流触发参数由VideoFile调整为Metadata,堆栈会进行自动调整。
调整后s3桶事件通知中由之前的视频文件触发工作流,变更为由metadata触发,metadata就是.json的一个文件,具体内容参考github

Metadata文件创建

堆栈调整后,上传视频文件不再触发工作流,此时需要对上传的视频文件生成对应的metadata文件(.json文件),可以由业务侧进行两次上传,先上传视频文件,再上传metadata文件,进而触发工作流。考虑存在失败的可能以及业务侧需要实现额外的逻辑,因此使用了Lambda创建Metadata文件,文件上传完成触发Lambda,由Lambda实现生成Metadata文件。

const aws = require('aws-sdk');

const s3 = new aws.S3({ apiVersion: '2006-03-01' });

exports.handler = async (event, context) => {
    // Get the object from the event and show its content type
    const bucket = event.Records[0].s3.bucket.name;
    const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' '));
    if(key.endsWith('.json')){
        console.log('json file');
        return;
    }
    const params = {
        Bucket: bucket,
        Key: key,
    }; 
    console.log(params);
    const metadata={
        "srcVideo": key,
        "ArchiveSource": true,
        "FrameCapture":true,
        "JobTemplate":"jiaoyubaoCompanyVideo_trans_template"
    };
    const metadataKey=key.substring(0,key.lastIndexOf('.'))+'.json';
    console.log('metadatakey:',metadataKey);
    try {
        //const { ContentType } = await s3.headObject(params).promise();
        //console.log('CONTENT TYPE:', ContentType);
        //upload metadata
        const uploadParams = {Bucket: bucket, Key: metadataKey, Body: JSON.stringify(metadata)};
        console.log(uploadParams);
        // call S3 to retrieve upload file to specified bucket
        const res=await s3.upload (uploadParams).promise();
        return res;
        
    } catch (err) {
        console.log(err);
        throw err;
    }
};

自定义转码模板

默认模板转码为m3u8格式文件,需要实现mp4格式转码,自定义转码模板jiaoyubaoCompanyVideo_trans_template

质量优化级别建议使用单一传递 HQ,太低转码文件质量较低,但文件大小会降低明显,考虑效果,采用中间质量。
最大比特率(位/秒)设置为了2000000,该值会影响转码视频大小,实际测试2000000转码视频较源视频大小略有压缩,固采用该值,如需高质量可参考该值设置更大的值,如需更多的压缩,可降低该值。

完成以上三部,即可通过aws控制台上传视频,触发转码工作流,输出转码视频了

业务侧上传视频

机构后台限制了视频大小50M,考虑到后台上传会导致占用更多的流量,和服务器资源,因此使用客户端直传方案。
使用jssdk实现web直传。

客户端直连S3实现分片续传思路与实践

https://aws.amazon.com/cn/blogs/china/s3-multipul-upload-practice/

授权方案

方案一:STS临时授权

由于客户端直传,使用长久AK(AwsAccessKeyId、AwsSecretAccessKey)的话会造成敏感信息泄漏,存在安全隐患。
临时授权需要一个Role(角色)用于创建一个临时的凭证,前端使用临时凭证上传s3。
为了安全考虑,该角色拥有可用的最小权限,角色绑定策略实现权限限制。因此需要先创建一个策略,然后绑定到该角色。

策略:ClientUploadVideoS3Policy

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject"
            ],
            "Resource": "arn:aws-cn:s3:::videoondemand-company-source234242b7/*"
        }
    ]
}

角色:ClientVideoUploadAssumeRole


AssumeRole

private Amazon.SecurityToken.Model.AssumeRoleResponse GetOriginalAwsAssumeRole(int durationSeconds=3600)
{
    //Amazon Resource Name
    var roleArnToAssume = "arn:aws-cn:iam::77480923422862:role/ClientVideoUploadAssumeRole";

    var client = new Amazon.SecurityToken.AmazonSecurityTokenServiceClient(AWSCredentialsConfig.AwsAccessKeyId,AWSCredentialsConfig.AwsSecretAccessKey,Amazon.RegionEndpoint.CNNorthWest1);

    // Create the request to use with the AssumeRoleAsync call.
    var assumeRoleReq = new Amazon.SecurityToken.Model.AssumeRoleRequest()
    {
        DurationSeconds = durationSeconds,
        RoleSessionName = "AwsUpload",
        RoleArn = roleArnToAssume
    };

    var assumeRoleRes =  client.AssumeRole(assumeRoleReq);
    return assumeRoleRes;
}
{
  "AssumedRoleUser": {
    "Arn": "arn:aws-cn:sts::77421422862:assumed-role/ClientVideoUploadAssumeRole/AwsUpload",
    "AssumedRoleId": "AROA3IZSCNY432ZRBYTQU5:AwsUpload"
  },
  "Credentials": {
    "AccessKeyId": "ASIA3234fsdfwe42Q3UK6",
    "Expiration": "2022-12-30T15:24:37+08:00",
    "SecretAccessKey": "khoROTW8vyDSD7tcaDmvMZg7S0Ujte6TiRP+ql2b",
    "SessionToken": "IQoJb3JpZ2luX2VjEGEaDmNuLW5vcnRod2VzdC0xIkYwRAIgUZ7b3+OGVn+agrp3IJ+JRQDjTTgKcGW+F92eOi0CsMICICqooZD6FJoCnOdBNaAPfrvzB5iG4fxzSQL234234uEKp8CCI///wEQABoMNzc0ODA5NzQyODYyIgw+fkUE58kq72ukAWkq8wEbHoeO5inZTKxAr/kafYBQ5RFOyYx8EJjrYd/uCReS6Te/Ix64D9/aqSM/BimyACmnjM3BmeDuys12zYDdHLfOAhuOUR4dyjKEXPWTwV6usxVb+5EYw4T9Z47dX9gV8EoI0n342342jzKnY8iQIikpidsYFjo6g27Q/dddCUXGqhIUB0QtzAhoMRAM9ndVl9w2VH7CBUsFPFXPhUaxhhIol59p8L2342342BKkFFpY/Zf8wSMLBYLLO3p9/uCBA4D06chibtYVoiIPLoMvPDlOEU6iAk+LtKTOtdmZ5/J8+WTGeZB8Y6R7II14wpe++rAY6ngGPSJw/q0JoPm8WMMH84c3Lv47V9n9sDTSX8WHlASPvgySRiuP+DvSd3RqMDootuk3awg1x3hvzU438xJ+BCo5qAWFIJE2niwyslWh3zrxiGADBAqqg/XT2P4yQg8wiscQh38KxSOlj+LxgvTcdVfe4/Yy4uWtudvWR4EkcVvpWWIw+sxDkX0LlYvfHz3yhAq209Gt8E5zcX4NWQ8mnug=="
  },
  "PackedPolicySize": 0,
  "SourceIdentity": null,
  "ResponseMetadata": {
    "RequestId": "15c423624-923f4-452b-902e-1e8561175523",
    "Metadata": {
      
    },
    "ChecksumAlgorithm": 0,
    "ChecksumValidationStatus": 0
  },
  "ContentLength": 1463,
  "HttpStatusCode": 200
}

方案二:使用预签名 URL(未验证)

https://docs.amazonaws.cn/AmazonS3/latest/userguide/using-presigned-url.html

jssdk直传

<script src="/js/aws-sdk-2.45.0.min.js"></script>

<script>
        var accessKeyId = "{$:token.Credentials.AccessKeyId}";
        var accessKeySecret = "{$:token.Credentials.SecretAccessKey}";
        var sessionToken = "{$:token.Credentials.SessionToken}";
        var endpoint = `${location.protocol}//s3.cn-northwest-1.amazonaws.com.cn`;
        var s3 = new AWS.S3({ accessKeyId: accessKeyId, secretAccessKey: accessKeySecret, sessionToken: sessionToken, region: 'cn-northwest-1', endpoint: endpoint, signatureVersion: 'v4' });

        var filePicker = document.getElementById("videoPicker");
        var progress = document.getElementById("progress");

        filePicker.addEventListener('change', function (event) {
                console.log(event.target.files[0]);
                if (event.target.files[0].size / 1024 / 1024 > 50) {
                    alert("文件超出大小限制,不得超过50M");
                    document.getElementById("videoPicker").value = null;
                    return;
                }
                
        });

        function start() {
            if (filePicker.files.length <= 0) {
                alert("请先选择视频");
                return;
            }
            var file = filePicker.files[0];
            var ext = file.name.substring(file.name.lastIndexOf('.'));
            $("#progressInfo").css({ width: '100%' });
            filePicker.setAttribute("disabled", "disabled");
            var params = {
                Bucket: 'videoondemand-company-source-gbwnqmc885b7',
                Key: `{$:companyInfo.comid}_${Date.now()}${ext}`,
                Body:file
            };
            var upload = s3.upload(params, {
                queueSize: 1,//分片上传并发队列,代表了能同时上传的分片数量
                connectTimeout: 60 * 1000 * 10,
                httpOptions: {
                    timeout: 60 * 1000 * 10
                }
            }).on('httpUploadProgress', function (e) {
                var percent = parseInt(e.loaded, 10) / parseInt(e.total, 10);
                percent = percent.toFixed(2) * 100;
                setTimeout(function () {
                    var p = percent + "%";
                    $(".progress span").css({ width: p });
                });
            });

            upload.send(function (err, data) {
                console.log(err);
                console.log(data);
                if (err) {
                    alert("上传失败,请稍后重试");
                    $("#progressInfo").css({ width: '0' });
                }
                //上传成功
                $.ajax({
                    url: '/CompanyHandler.ashx',
                    type: 'post',
                    dataType: 'json',
                    data: { action: 'saveVideo', object: data.Key, rand: Math.random() },
                    success: function (result) {
                        if (result.result == 1) {
                            //关闭弹窗,刷新列表页
                            parent.location.reload();
                        }
                        else {
                            alert(result.description);
                        }
                    },
                    error: function () {
                        alert("发生异常,请稍后重试或联系客服反馈");
                    }
                });
                filePicker.removeAttribute("disabled");
            });
        }

    </script>

S3 CORS


[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "PUT",
            "POST",
            "DELETE"
        ],
        "AllowedOrigins": [
            "https://*.cn"
        ],
        "ExposeHeaders": [
            "ETag"
        ]
    },
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "PUT",
            "POST",
            "DELETE"
        ],
        "AllowedOrigins": [
            "http://*.jiaoyubao.cn"
        ],
        "ExposeHeaders": [
            "ETag"
        ]
    },
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "PUT",
            "POST",
            "DELETE"
        ],
        "AllowedOrigins": [
            "http://localhost:53762"
        ],
        "ExposeHeaders": [
            "ETag"
        ]
    }
]

SQS异步通知机制

转码完成后会将完成消息写入SQS,业务侧可以通过读取SQS拿到消息,进行业务侧数据和逻辑完善。可以实现一个服务轮询SQS那消息,但由于SQS按照接口调用次数收费以及可以预测的大量的空队列读取情况,空队列读取依旧计入接口调用次数(aws有100万次的接口调用免费额度)。
基于以上考虑,使用SQSLambda 触发器实现推送机制,有消息到达SQS则触发Lambda函数,将消息推送给业务侧,需要自行编写Lambda函数。

VideoonDemand-Company-Notify

const http = require('http')

exports.handler = async (event) => {
  const data = JSON.stringify(event);
  console.log(data)
  //其他业务复用当前部署堆栈的话,异步通知需要差异化的话,可以考虑通过视频前缀约束在此处进行差异化处理。
  const options = {
    hostname: 'vip3.shhddg.cn',
    port: 80,
    path: '/awsnotifications.aspx',
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Content-Length': data.length
    }
  }

  await new Promise(function (resolve, reject) {

    const req = http.request(options, res => {
      console.log(`状态码: ${res.statusCode}`)

      res.on('data', d => {
        
      })
    });

    req.on('error', error => {
      console.error(error)
    });

    req.write(data);
    req.end();                         
  });

  return event;
};

VideoonDemand-Company-Notify 要能够被SQS触发,需要配置对应的权限,创建的Lambda的执行角色需要加入AWSLambdaSQSExecutionRole 权限策略



域名解析到S3

将域名解析到S3 转码结果目标桶,并配置S3公开访问,从而可以使用域名访问S3资源。域名需要进行解析,注意需要去掉Host转发


CloudFormation部署的堆栈,默认会在当前账户创建CloudFront用于域名解析,仅做Dns解析调整即可。堆栈默认对S3桶权限做了配置。但目前的CloudFront统一在另外账户,不在当前部署堆栈的账户,因此,需要将域名直接解析到S3桶,并对S3桶设置了公开访问和策略调整,Principal设置了所有,默认是堆栈创建的对应域名的CloudFront用户允许。

🦉 Aws vs Aliyun

机构后台使用STS临时授权方案上传S3,对比阿里云接口调用和方式,Aws和阿里云从风格到接口基本一致。

阿里云

private AssumeRoleResponse GetStsToken()
{
    //获取sts凭证
    const string REGIONID = "cn-hangzhou";
    const string ENDPOINT = "sts.cn-yuenan.aliyuncs.com";
    // 构建一个 Aliyun Client, 用于发起请求
    // 构建Aliyun Client时需要设置AccessKeyId和AccessKeySevcret
    DefaultProfile.AddEndpoint(REGIONID, REGIONID, "Sts", ENDPOINT);
    IClientProfile profile = DefaultProfile.GetProfile(REGIONID, AppCode.ConstSetting.AccessKeyId, AppCode.ConstSetting.AccessKeySecret);
    DefaultAcsClient client = new DefaultAcsClient(profile);
    // 构造AssumeRole请求
    AssumeRoleRequest request = new AssumeRoleRequest();
    request.AcceptFormat = FormatType.JSON;
    // 指定角色Arn
    request.RoleArn = "acs:ram::14898694342448426:role/jiaoyubaovideorole";
    request.RoleSessionName = "upload";
    // 可以设置Token有效期,可选参数,默认3600秒;
    // request.DurationSeconds = 3600;
    // 可以设置Token的附加Policy,可以在获取Token时,通过额外设置一个Policy进一步减小Token的权限;
    // request.Policy="<policy-content>"
    try
    {
        AssumeRoleResponse response = client.GetAcsResponse(request);
        //Token过期时间;服务器返回UTC时间,这里转换成北京时间显示;
        return response;
    }
    catch (Exception ex)
    {
        //Console.Write(ex.ToString());
        return null;
    }
}

Aws

private Amazon.SecurityToken.Model.AssumeRoleResponse GetOriginalAwsAssumeRole(int durationSeconds=3600)
{
    var roleArnToAssume = "arn:aws-cn:iam::7743242742862:role/ClientVideoUploadAssumeRole";

    var client = new Amazon.SecurityToken.AmazonSecurityTokenServiceClient(AWSCredentialsConfig.AwsAccessKeyId,AWSCredentialsConfig.AwsSecretAccessKey,Amazon.RegionEndpoint.CNNorthWest1);

    // Create the request to use with the AssumeRoleAsync call.
    var assumeRoleReq = new Amazon.SecurityToken.Model.AssumeRoleRequest()
    {
        DurationSeconds = durationSeconds,
        RoleSessionName = "AwsUpload",
        RoleArn = roleArnToAssume
    };

    var assumeRoleRes =  client.AssumeRole(assumeRoleReq);
    return assumeRoleRes;
}

🍄 避坑指南

坑1:文档版本不一致,不好找

服务商给到的视频点播文档中的方案链接https://www.amazonaws.cn/solutions/video-on-demand/?nc1=h_ls

搜索引擎或官方给到的视频点播文档链接https://aws.amazon.com/cn/solutions/implementations/video-on-demand-on-aws/

这两个链接的github源码不一致,而实现自定义定制功能的解决方案,在第一个链接中可以找到,第二个链接找不到。

坑2:SQS Lambda 触发器需要额外的权限

The provided execution role does not have permissions to call ReceiveMessage
Lambda函数 配置-权限-执行角色 的权限策略需要添加AWSLambdaSQSExecutionRole 策略

坑3:S3 CORS

分片上传ETag问题,使用分片上传的话ExposeHeaders务必设置ETag,否则会存在跨域问题,可参考以下文档
https://www.cnblogs.com/duhuo/p/14828021.html

[
        {
            "AllowedHeaders": [
                "*"
            ],
            "AllowedMethods": [
                "PUT",
                "POST",
                "DELETE"
            ],
            "AllowedOrigins": [
                "https://*.hfdhsa.cn"
            ],
            "ExposeHeaders": [
                "ETag" //坑
            ]
        },
        {
            "AllowedHeaders": [
                "*"
            ],
            "AllowedMethods": [
                "PUT",
                "POST",
                "DELETE"
            ],
            "AllowedOrigins": [
                "http://*.geertfd.cn"
            ],
            "ExposeHeaders": [
                "ETag"
            ]
        },
        {
            "AllowedHeaders": [
                "*"
            ],
            "AllowedMethods": [
                "PUT",
                "POST",
                "DELETE"
            ],
            "AllowedOrigins": [
                "http://localhost:53762" //坑,本地测试,多端口尚未解决
            ],
            "ExposeHeaders": [
                "ETag"
            ]
        }
    ]

坑4:jssdk

var accessKeyId = "{$:token.Credentials.AccessKeyId}";
var accessKeySecret = "{$:token.Credentials.SecretAccessKey}";
var sessionToken = "{$:token.Credentials.SessionToken}";//坑,要传
//坑,要传,特别是协议cors
var endpoint = `${location.protocol}//s3.cn-northwest-1.amazonaws.com.cn`;
var s3 = new AWS.S3({ accessKeyId: accessKeyId
    , secretAccessKey: accessKeySecret
    , sessionToken: sessionToken
    , region: 'cn-northwest-1'
    , endpoint: endpoint
    , signatureVersion: 'v4' });//坑 signatureVersion不指定报错

坑5:Lambda

需要重点了解和会编写脚本,比如Nodejs phython等解释性语言。如果使用Java或.NET需要编译的语言,需要按照官方文档实现开发,和打包成.gz文件上传到Aws,Aws提供了编译语言对应的运行环境,需要注意环境支持的版本等信息。建议使用解释性语言,好处是只要Aws提供了对应的解释器,就可以在线编写代码进行测试,缺点是无法本地调试或逐语句调试。即使使用解释性语言,复杂业务逻辑或引用第三方包,还是需要本地开发并打包成.gz文件上传到Aws。
Nodejs 支持异步的话,务必使用await,否则异步操作不会执行,比如:

 await new Promise(function (resolve, reject) {

    const req = http.request(options, res => {
      console.log(`状态码: ${res.statusCode}`)

      res.on('data', d => {
        
      })
    });

    req.on('error', error => {
      console.error(error)
    });

    req.write(data);
    req.end();                         
  });

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

一个项目的上线全过程

营业执照 个体户&#xff08;700执照、2年财务代理 4000&#xff09; 一张一类卡&#xff0c;转卡专用&#xff08;个人卡&#xff09; 120w 45%企业资质&#xff08;个体户&#xff09; 企业微信&#xff08;300元/年、公众号、小程序&#xff09; 相关手续 备案域名去申请支付…

使用UDF扩展Spark SQL

Apache Spark是一个强大的分布式计算框架&#xff0c;Spark SQL是其一个核心模块&#xff0c;用于处理结构化数据。虽然Spark SQL内置了许多强大的函数和操作&#xff0c;但有时可能需要自定义函数来处理特定的数据需求。在Spark SQL中&#xff0c;可以使用UDF&#xff08;User…

C++初阶——类与对象

目录 C宏函数 在使用宏函数时&#xff0c;有几个常见的错误需要注意&#xff1a; 宏函数在某些情况下有以下优势&#xff1a; 1.C宏函数 在 C 中&#xff0c;宏函数&#xff08;Macro Function&#xff09;是使用预处理器定义的宏&#xff08;Macro&#xff09;&#xff0…

Android studio ViewPager2应用设计

一、ViewPager2应用场景&#xff1a; ViewPager2是一个功能强大的滑动容器&#xff0c;提供灵活的页面切换和布局定制功能&#xff0c;使得应用程序界面更加丰富和交互性强&#xff0c;主要应用于以下场景&#xff1a; 1&#xff09;、实现引导页或欢迎页&#xff1a;ViewPag…

Microsoft Visual Studio 2022 install Project 下载慢

1. 关闭Internet 协议版本6 2. 如果没有效果&#xff0c;打开Internet 协议版本4&#xff0c;更改DNS 3. 在浏览器中下载后安装&#xff0c;下载地址如下&#xff1a; Microsoft Visual Studio Installer Projects 2022 - Visual Studio Marketplace 4. 安装时注意关闭vs&…

Maven 整理(含面试题)

Maven是Java 项目必备&#xff0c;Maven 主要服务于基于Java平台的项目构建、依赖管理和项目信息管理。项目构建工具 、更好的管理依赖 目录 Maven安装与运行maven 重要知识点面试题 Maven安装与运行 Maven 是一个项目管理工具&#xff0c;可以对 Java 项目进行构建、依赖管理…

ThreadLocal线程重用导致用户信息错乱的 Bug

在生产上遇到一个诡异的问题&#xff0c;有时获取到的用户信息是别人的。查看代码后&#xff0c;我发现他使用了 ThreadLocal 来缓存获取到的用户信息。 我们知道&#xff0c;ThreadLocal 适用于变量在线程间隔离&#xff0c;而在方法或类间共享的场景。如果用户信息的获取比较…

[足式机器人]Part2 Dr. CAN学习笔记-自动控制原理Ch1-8Lag Compensator滞后补偿器

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记-自动控制原理Ch1-8Lag Compensator滞后补偿器 从稳态误差入手&#xff08;steady state Error&#xff09; 误差 Error &#xff1a; E ( s ) R ( s ) − X ( s ) R ( s ) − E ( s ) ⋅ K G …

CMake入门教程【基础篇】CMake+Visual Studio2022构建C++项目

文章目录 1.概述2.Visual Studio 2022简介3.安装Visual Studio 20224.安装CMake5.创建CMake项目6. 构建项目 1.概述 CMake和Visual Studio 2022结合 在现代软件开发中&#xff0c;CMake和Visual Studio 2022的结合提供了一个强大的环境&#xff0c;用于构建和管理各种规模的C项…

1213:八皇后问题 深度优先搜索算法

1213&#xff1a;八皇后问题 时间限制: 1000 ms 内存限制: 65536 KB 【题目描述】 在国际象棋棋盘上放置八个皇后&#xff0c;要求每两个皇后之间不能直接吃掉对方。 【输入】 (无) 【输出】 按给定顺序和格式输出所有八皇后问题的解&#xff08;见样例&#xff09;。 题目…

【Matlab】PSO-BP 基于粒子群算法优化BP神经网络的数据时序预测(附代码)

资源下载&#xff1a; https://download.csdn.net/download/vvoennvv/88689096 一&#xff0c;概述 PSO-BP算法是一种结合了粒子群算法&#xff08;PSO&#xff09;和BP神经网络的方法&#xff0c;用于数据时序预测。下面是PSO-BP算法的原理和过程&#xff1a; 1. 数据准备&…

如何移除视频中的背景音乐或人物声音

移除视频声音是将视频指定的声音移除&#xff0c;可以选择移除人物声音还是视频的背景音乐&#xff0c;方便实现二次创作。 小编给大家推荐一些方法帮助大家更轻松地移除视频中的背景音乐或人物声音&#xff0c;有兴趣的朋友请自行百度查找&#xff0c;或小程序查找 1、方法&a…

ctfshow——信息搜集

文章目录 web 1web 2web 3web 4web 5web 6web 7web 8web 9web 10web 11web 12web 13web 14web 15web 16web 17web 18web 19web 20 web 1 题目提示开发注释未及时删除。 直接右键查看源代码。 web 2 在这关我们会发现&#xff1a;1&#xff09;无法使用右键查看源代码&…

基于simiulink的flyback反激型电路建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1 Flyback反激型电路的基本原理 4.2 Flyback反激型电路的数学建模 4.3 Flyback反激型电路的仿真方法 5.完整工程文件 1.课题概述 flyback反激型电路建模与仿真。反激变换器在开关管导通时电源将电能…

openssl 命令详解

openssl genrsa 命令产生私钥 openssl genrsa 命令是会用来生成 RSA 私有秘钥&#xff0c;不会生成公钥&#xff0c;因为公钥提取自私钥。生成时是可以指定私钥长度和密码保护。 如果需要查看公钥或生成公钥&#xff0c;可以使用 openssl rsa 命令。 命令语法&#xff1a; ope…

通用图片转Excel与票证转为结构化数据的Excel识别有什么区别?

引言&#xff1a; 随着数字化时代的到来&#xff0c;大量的纸质文档需要被转换为电子格式以便于管理和分析。其中&#xff0c;表格数据的转换尤为重要。通用图片转Excel表格识别和结构化OCR识别是两种常见的技术&#xff0c;它们虽然都是用于将图片中的内容转换为可编辑的Exce…

一、HTML5简介

一、简介 超文本标记语言&#xff08;英语&#xff1a;HyperText Markup Language&#xff0c;简称&#xff1a;HTML&#xff09;是一种用于创建网页的标准标记语言。可以使用 HTML 来建立自己的 WEB 站点&#xff0c;HTML 运行在浏览器上&#xff0c;由浏览器来解析。 <!…

多模型DCA曲线:如何展现和解读乳腺癌风险评估模型的多样性和鲁棒性?

一、引言 乳腺癌是女性常见的恶性肿瘤之一&#xff0c;对女性的身体健康和生命安全产生了重要影响。早期诊断和风险评估可以帮助医生和患者制定更好的治疗方案&#xff0c;并提高治愈率和生存率。因此&#xff0c;乳腺癌风险评估模型的研究和应用变得越来越重要。 在乳腺癌风险…

【已解决】打印PDF文件,如何跳过不需要的页面?

打印PDF文件的时候&#xff0c;有时候我们只需要打印其中的几页&#xff0c;并不需要全部打印&#xff0c;那如何在打印时跳过那些不需要的页面呢&#xff1f;不清楚的小伙伴一起来看看吧&#xff01; 如果你是通过网页打开PDF文件&#xff0c;那么可以在页面中找到并点击“打…

页面间动画之放大缩小视图

目录 1、Exchange类型的共享元素转场 2、Static类型的共享元素转场 3、场景示例 在不同页面间&#xff0c;有使用相同的元素&#xff08;例如同一幅图&#xff09;的场景&#xff0c;可以使用共享元素转场动画衔接。为了突出不同页面间相同元素的关联性&#xff0c;可为它们…