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

news2024/11/19 20:42:15

文章目录

  • 🐷 背景
  • 🦥 简述
  • 🐥 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();                         
  });

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

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

相关文章

k8s集群部署Harbor镜像仓库

部署k8s集群参考 https://blog.csdn.net/m0_59933574/article/details/134936188?spm1001.2014.3001.5502https://blog.csdn.net/m0_59933574/article/details/134936188?spm1001.2014.3001.5502 安装Harbor 准备一台干净服务器&#xff0c;关闭防火墙与selinux 下载dock…

rime中州韵 inputShow lua Filter

在 rime中州韵 inputShow lua Translator 一文中&#xff0c;我们通过 inputShow.lua 定制了 inputShow_translator&#xff0c;这使得我们的输入方案可以将用户输入的字符透传到候选列表中来。如下&#x1f447;&#xff1a; &#x1f446;上图中我们在候选列表中看到了 inpu…

C# WinForm MessageBox自定义按键文本 COM组件版

c# 更改弹窗MessageBox按钮文字_c# messagebox.show 字体-CSDN博客 需要用到大佬上传到百度云盘的Hook类&#xff0c;在大佬给的例子的基础上改动了点。 应用时自己加GUID和ProgID。 组件实现&#xff1a; using System; using System.Collections.Generic; using System.L…

Flink Watermark和时间语义

Flink 中的时间语义 时间语义&#xff1a;EventTime&#xff1a;事件创建时间&#xff1b;Ingestion Time&#xff1a;数据进入 Flink 的时间&#xff1b;Processing Time&#xff1a;执行操作算子的本地系统时间&#xff0c;与机器无关。不同的时间语义有不同的应用场合&#…

MySQL之四大引擎、建库建表以及账号管理

目录 一.四大引擎 1.1 InnoDB 1.2 MyISAM 1.3 MEMORY 1.4 Archive 二.数据库管理 2.1 元数据库简介 2.2 元数据库分类 2.3 数据库的增删改查及使用 2.4 MySQL库权限相关 三.数据表管理 3.1 三大范式 3.2 基本数据类型 3.2.1 优化原则 3.2.2 分类 四.数据库账号管理 4.1 相同…

LabVIEW在高精度机器人视觉定位系统中的应用

在现代工业自动化中&#xff0c;精确的机器人视觉定位系统对于提高生产效率和产品质量至关重要。LabVIEW软件&#xff0c;以其卓越的图像处理和自动化控制功能&#xff0c;在这一领域发挥着重要作用。本案例将展示LabVIEW如何帮助开发和实现一个高精度的机器人视觉定位系统&…

【WPF.NET开发】WPF中的输入

本文内容 输入 API事件路由处理输入事件文本输入触摸和操作侧重点鼠标位置鼠标捕获命令输入系统和基元素 Windows Presentation Foundation (WPF) 子系统提供了一个功能强大的 API&#xff0c;用于从各种设备&#xff08;包括鼠标、键盘、触摸和触笔&#xff09;获取输入。 本…

easycython和cython将py编译为pyd对比

前提了解 为了实验的准确性,在全过程使用的python环境版本都为同一版本 easycython和cython编译为pyd文件的不同在于,easycython编译的原始文件后缀为pyx,cython编译的原始文件为py 1.cython 1.1原始文件 def ZWHCythonTest():print("Z_W_H_") def ZWHCython…

C语言数组习题

1.数组遍历 #include <stdio.h>int main(){int i,a[10];for(i0;i<9;i) //对数组元素a[0]~a[9]赋值 a[i]i;for(i9;i>0;i--) //输出a[9]~a[0]共10个数组元素 printf("%d ",a[i]);printf("\n");return 0;} 运行结果&#xff1a; 2.数组应用&a…

IOS - 手机安装包 ipa 常见几种方式

安装 ipa 包的方法有很多中&#xff0c;可以通过不同的软件安装&#xff0c;本文只列出了常用的几种&#xff0c;做个简单的归纳整理 1、iTunes 安装 数据线连接手机之后&#xff0c;会自动连接iTunes&#xff0c;&#xff08;第一次连接的时候会提示是否信任此电脑&#xff0…

Git管理项目

大家好我是苏麟 , 今天和大家聊聊用Git管理项目 . 一步一步上传到Git仓库 . 1.找到VCS点击创建Git仓库 2.点击目录 3.点击绿色对号提交 4.点击提交 5.点击提交 6.成功提交到本地 7.打开GitLab 或 Gitee 或 GitHub 并创建项目 (注意 : 这里一定是什么都没有的) 否则一会上传是…

[足式机器人]Part2 Dr. CAN学习笔记-自动控制原理Ch1-9PID控制器

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记-自动控制原理Ch1-9PID控制器&#xff09; P —— Proportional I —— Integral D —— Derivative 当前误差/过去误差/误差的变化趋势 K p ⋅ e K_{\mathrm{p}}\cdot e Kp​⋅e&#xff1a;比…

挑战 ChatGPT 和 Google Bard 的防御

到目前为止&#xff0c;科学家已经创建了基于人工智能的聊天机器人&#xff0c;可以帮助内容生成。我们还看到人工智能被用来创建像 WormGPT 这样的恶意软件&#xff0c;尽管地下社区对此并不满意。但现在正在创建聊天机器人&#xff0c;可以使用生成人工智能通过即时注入活动来…

论文笔记:CellSense: Human Mobility Recovery via Cellular Network Data Enhancement

1 intro 1.1 背景 1.1.1 蜂窝计费记录&#xff08;CBR&#xff09; 人类移动性在蜂窝网络上的研究近些年得到了显著关注&#xff0c;这主要是因为手机的高渗透率和收集手机数据的边际成本低蜂窝服务提供商收集蜂窝计费记录&#xff08;CBR&#xff09;用于计费目的&#xf…

利用提示工程,提升LLM将自然语言转化为SQL的准确性

大型语言模型 (LLM) 已展现出理解自然语言提示并生成连贯响应的卓越能力。 这为将自然语言翻译成 SQL 等结构化查询语言开辟了新的可能性。 过去&#xff0c;编写 SQL 查询需要技术专业知识&#xff0c;而LLM允许任何人用简单的英语描述他们想要的内容&#xff0c;并自动生成相…

Github 2024-01-03 开源项目日报 Top10

根据Github Trendings的统计&#xff0c;今日(2024-01-03统计)共有10个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量Python项目3TypeScript项目3Jupyter Notebook项目1Dart项目1C项目1Rust项目1 系统设计指南 创建周期&#x…

STC进阶开发(三)蜂鸣器、RTC时钟、I2C总线、外部中断、RTC闹钟设置、RTC计时器设置

前言 这一期我们首先学习如何让蜂鸣器响起来&#xff0c;并且如何让蜂鸣器发出简单的歌曲&#xff0c;然后我们介绍RTC时钟&#xff0c;要想明白RTC时钟&#xff0c;我们还需要先介绍I2C总线和外部中断。接下来就开始这一期的学习吧&#xff01; 蜂鸣器 简单介绍 蜂鸣器是一种…

geemap学习笔记039:分析地理空间数据--合成无云影像

前言 本节介绍的内容是对于众多的原始Landsat数据&#xff0c;利用ee.Algorithms.Landsat.simpleComposite()将其处理为TOA数据&#xff0c;并且合成无云影像。 1 导入库并显示地图 import ee import geemap ee.Initialize()2 无云影像合成 Map geemap.Map()collection e…

王道考研计算机组成原理——数据的表示和运算

数制转换 任意进制》十进制&#xff1a;位权*位数即可 整数部分补0是补在头部&#xff0c;小数部分补0是补在尾部 一般都是先把十进制》二进制&#xff1b;然后二进制再转换成8/16进制这样子 一种更快的方法->拼凑法&#xff1a;小数部分整数部分都可以这样求 一般都是先…

TypeError: unsupported operand type(s) for +: ‘NoneType‘ and ‘str‘

报错 找到出错代码&#xff0c;发现默认值是None 解决 改为‘’即可