当前位置: 首页 > news >正文

webrtc快速入门——使用 WebRTC 拍摄静止的照片

文章目录

  • 使用 getUserMedia() 拍摄静态照片
    • HTML 标记
    • JavaScript 代码
    • 初始化
      • startup() 函数
        • 获取元素引用
        • 获取流媒体
      • 监听视频开始播放
        • 处理按钮上的点击
        • 包装 startup() 方法
      • 清理照片框
      • 从流中捕获帧
  • 例子代码
      • HTML代码
      • CSS代码
      • JavaScript代码
  • 过滤器
  • 使用特定设备

使用 getUserMedia() 拍摄静态照片

介绍了如何在 WebRTC 的支持下可以访问到电脑或者手机的摄像头并且使用它来拍摄照片。

本文介绍在支持 getUserMedia() 的计算机或手机上如何使用 navigator.mediaDevices.getUserMedia() 访问摄像机,并用其拍照。

HTML 标记

我们的 HTML 界面有两个主要的操作部分:流和捕获面板以及演示面板。它们俩都在它们自己的 <div> 中并排渲染,以便于添加样式和控制。

左边的面板包含两个组件:一个 <video> 元素,它将接收来自 navigator.mediaDevices.getUserMedia() 的流,以及用于用户点击以捕获视频帧的 <button>

<div class="camera"><video id="video">视频流目前不可用。</video><button id="startbutton">拍摄照片</button>
</div> 

这很简单,当我们进入 JavaScript 代码时,我们将看到它们是如何紧密联系在一起的。

接下来,我们有一个 <canvas> 元素,捕获的帧被存储到其中,可能以某种方式进行操作,然后转换为输出图像文件。通过使用样式 display:none 将画布保持隐藏,以避免画面的混乱——用户不需要看到这个中间过程。

我们还有一个 <img> 元素,我们将在其中绘制图像——这是让用户看到的最终显示。

<canvas id="canvas"> </canvas>
<div class="output"><img id="photo" alt="捕获的图像会显示在这里。" />
</div> 

这是所有相关的 HTML。其余的只是一些页面布局和提供一个返回页面链接的些许文本。

JavaScript 代码

现在来看看 JavaScript 代码。我们将把它分解成几个小的部分,使其更容易解释。

初始化

我们首先将整个脚本包装在匿名函数中,以避免使用全局变量,然后设置我们将要使用的各种变量。

(() => {const width = 320;    const height = 0;     const streaming = false;let video = null;let canvas = null;let photo = null;let startbutton = null; 

这些变量分别是:

width

无论输入视频的尺寸如何,我们将把所得到的图像缩放到宽度为 320 像素。

height

给定流的 width 和宽高比,计算出图像的输出高度。

streaming

指示当前是否有活动的视频流正在运行。

video

这将是页面加载完成后对 <video> 元素的引用。

canvas

这将是页面加载完成后对 <canvas> 元素的引用。

photo

这将在页面加载完成后引用 <img> 元素。

startbutton

这将引用用于触发捕获的 <button> 元素。我们会在页面加载完成之后得到。

startup() 函数

当页面加载完成时,提供给 EventTarget.addEventListenerstartup() 函数将会运行。此函数的作用是请求访问用户的网络摄像头,将用于输出的 <img> 初始化为默认状态,并建立从相机接收每帧视频所需的事件监听器,并在点击按钮捕获图像时作出反应。

获取元素引用

首先,我们参考我们需要访问的主要内容。

 function startup() {video = document.getElementById('video');canvas = document.getElementById('canvas');photo = document.getElementById('photo');startbutton = document.getElementById('startbutton'); 
获取流媒体

接下来的任务是获取媒体流:

navigator.mediaDevices.getUserMedia({ video: true, audio: false }).then((stream) => {video.srcObject = stream;video.play();}).catch((err) => {console.error(`An error occurred: ${err}`);}); 

在这里,我们调用 MediaDevices.getUserMedia() 并请求视频流(无音频)。它返回一个 promise,我们给它附加成功和失败情况下的回调方法。

成功回调接收一个 stream 对象作为输入。它是新视频的 <video> 元素的源。

一旦流被链接到 <video> 元素,我们通过调用 HTMLMediaElement.play() 开始播放。

如果打开流失败,则调用失败回调函数。在没有连接兼容的相机,或者用户拒绝访问时,则会发生这种情况。

监听视频开始播放

<video> 上调用 HTMLMediaElement.play() 之后,在视频流开始流动之前,有一段(希望简短)的时间段过去了。为了避免在此之前一直阻塞,我们为 video 加上一个 canplay 事件的监听器,当视频播放实际开始时会触发该事件。那时,视频对象中的所有属性都已基于流的格式进行配置。

video.addEventListener("canplay",(ev) => {if (!streaming) {height = (video.videoHeight / video.videoWidth) * width;video.setAttribute("width", width);video.setAttribute("height", height);canvas.setAttribute("width", width);canvas.setAttribute("height", height);streaming = true;}},false,
); 

这个回调什么都不做,除非它是第一次被调用;这是通过查看我们的 streaming 变量的值进行测试,这是第一次运行此方法时为 false

如果这是第一次运行,我们会根据视频的实际大小,video.videoWidth 和要渲染视频宽度的宽度(witdh)之间的大小差异来设置视频的高度。

最后,通过在视频和画布上调用 Element.setAttribute() 来设置视频和画布的宽度(witdh)和高度(height),以使得两者相互匹配。最后,我们将 streaming 变量设置为 true,以防止我们无意中再次运行此设置代码。

处理按钮上的点击

为了在每次用户点击 startbutton 时捕获静态照片,我们需要向按钮添加一个事件监听器,以便在发出 click 事件时被调用:

startbutton.addEventListener("click",(ev) => {takepicture();ev.preventDefault();},false,
); 

这个方法很简单:它只是调用我们的 takepicture() 函数,在从流中捕获帧的部分中定义,然后在接收的事件上调用 Event.preventDefault(),以防止点击被多次处理。

包装 startup() 方法

startup() 方法中只有两行代码:

这就是我们调用 clearphoto() 方法的地方,我们将在下面的清理照片框部分进行描述。

清理照片框

清理照片框包括创建一个图像,然后将其转换为可以显示最近捕获的帧的 <img> 元素使用的格式。该代码如下所示:

function clearphoto() {const context = canvas.getContext("2d");context.fillStyle = "#AAA";context.fillRect(0, 0, canvas.width, canvas.height);const data = canvas.toDataURL("image/png");photo.setAttribute("src", data);
} 

我们首先得到对我们用于屏幕外渲染的隐藏的 <canvas> 元素的引用。接下来,我们将 fillStyle 设置为 #AAA(相当浅的灰色),并通过调用 fillRect() 来填充整个画布。

最后在此功能中,我们将画布转换为 PNG 图像,并调用 photo.setAttribute() 以使我们捕获的静止框显示图像。

从流中捕获帧

最后一个定义的功能是整个练习的重点:takepicture() 函数,其捕获当前显示的视频帧的作业将其转换为 PNG 文件,并将其显示在捕获的帧框中。代码如下所示:

function takepicture() {const context = canvas.getContext("2d");if (width && height) {canvas.width = width;canvas.height = height;context.drawImage(video, 0, 0, width, height);const data = canvas.toDataURL("image/png");photo.setAttribute("src", data);} else {clearphoto();}
} 

正如我们需要处理画布内容的情况一样,我们首先得到隐藏画布的 2D 绘图上下文

然后,如果宽度和高度都是非零(意味着至少有潜在有效的图像数据),我们将画布的宽度和高度设置为与捕获帧的宽度和高度相匹配,然后调用 drawImage() 将视频的当前帧绘制到上下文中,用帧图像填充整个画布。

一旦画布包含捕获的图像,我们通过调用它的 HTMLCanvasElement.toDataURL() 将它转换为 PNG 格式; 最后,我们调用 photo.setAttribute() 来使我们捕获的静态框显示图像。

如果没有可用的有效图像(即宽度和高度均为 0),则通过调用 clearphoto() 清除捕获帧框的内容。

例子代码

HTML代码

<div class="contentarea"><h1>MDN——navigator.mediaDevices.getUserMedia(): 静态照片拍摄演示</h1><p>此示例演示了如何使用内置的网络摄像头来获取媒体流,并从中获取图像,以使用该图像来创建一个PNG 图像。</p><div class="camera"><video id="video">视频流目前不可用。</video><button id="startbutton">拍摄照片</button></div><canvas id="canvas"> </canvas><div class="output"><img id="photo" alt="捕获的图像会显示在这里。" /></div><p>访问我们的文章:<ahref="https://developer.mozilla.org/zh-CN/docs/Web/API/WebRTC_API/Taking_still_photos">使用 getUserMedia() 拍摄静态照片</a>以详细了解此处使用的技术。</p>
</div>

CSS代码

#video {border: 1px solid black;box-shadow: 2px 2px 3px black;width: 320px;height: 240px;
}#photo {border: 1px solid black;box-shadow: 2px 2px 3px black;width: 320px;height: 240px;
}#canvas {display: none;
}.camera {width: 340px;display: inline-block;
}.output {width: 340px;display: inline-block;vertical-align: top;
}#startbutton {display: block;position: relative;margin-left: auto;margin-right: auto;bottom: 32px;background-color: rgba(0, 150, 0, 0.5);border: 1px solid rgba(255, 255, 255, 0.7);box-shadow: 0px 0px 1px 2px rgba(0, 0, 0, 0.2);font-size: 14px;font-family: "Lucida Grande", "Arial", sans-serif;color: rgba(255, 255, 255, 1);
}.contentarea {font-size: 16px;font-family: "Lucida Grande", "Arial", sans-serif;width: 760px;
} 

JavaScript代码

(() => {// The width and height of the captured photo. We will set the// width to the value defined here, but the height will be// calculated based on the aspect ratio of the input stream.const width = 320; // We will scale the photo width to thislet height = 0; // This will be computed based on the input stream// |streaming| indicates whether or not we're currently streaming// video from the camera. Obviously, we start at false.let streaming = false;// The various HTML elements we need to configure or control. These// will be set by the startup() function.let video = null;let canvas = null;let photo = null;let startbutton = null;function showViewLiveResultButton() {if (window.self !== window.top) {// Ensure that if our document is in a frame, we get the user// to first open it in its own tab or window. Otherwise, it// won't be able to request permission for camera access.document.querySelector(".contentarea").remove();const button = document.createElement("button");button.textContent = "查看以上示例代码的实时演示";document.body.append(button);button.addEventListener("click", () => window.open(location.href));return true;}return false;}function startup() {if (showViewLiveResultButton()) {return;}video = document.getElementById("video");canvas = document.getElementById("canvas");photo = document.getElementById("photo");startbutton = document.getElementById("startbutton");navigator.mediaDevices.getUserMedia({ video: true, audio: false }).then((stream) => {video.srcObject = stream;video.play();}).catch((err) => {console.error(`An error occurred: ${err}`);});video.addEventListener("canplay",(ev) => {if (!streaming) {height = video.videoHeight / (video.videoWidth / width);// Firefox currently has a bug where the height can't be read from// the video, so we will make assumptions if this happens.if (isNaN(height)) {height = width / (4 / 3);}video.setAttribute("width", width);video.setAttribute("height", height);canvas.setAttribute("width", width);canvas.setAttribute("height", height);streaming = true;}},false,);startbutton.addEventListener("click",(ev) => {takepicture();ev.preventDefault();},false,);clearphoto();}// Fill the photo with an indication that none has been// captured.function clearphoto() {const context = canvas.getContext("2d");context.fillStyle = "#AAA";context.fillRect(0, 0, canvas.width, canvas.height);const data = canvas.toDataURL("image/png");photo.setAttribute("src", data);}// Capture a photo by fetching the current contents of the video// and drawing it into a canvas, then converting that to a PNG// format data URL. By drawing it on an offscreen canvas and then// drawing that to the screen, we can change its size and/or apply// other changes before drawing it.function takepicture() {const context = canvas.getContext("2d");if (width && height) {canvas.width = width;canvas.height = height;context.drawImage(video, 0, 0, width, height);const data = canvas.toDataURL("image/png");photo.setAttribute("src", data);} else {clearphoto();}}// Set up our event listener to run the startup process// once loading is complete.window.addEventListener("load", startup, false);
})();

过滤器

由于我们通过从 <video> 元素中抓取帧来捕获用户网络摄像头的图像,因此我们可以非常轻松地将过滤器和有趣的效果应用于视频。事实证明,使用 filter 属性应用于元素的任何 CSS 过滤器都会影响捕获的照片。这些过滤器可以从简单(使图像黑白)到复杂(高斯模糊和色调旋转)。

你可以使用例如 Firefox 开发者工具的样式编辑器来播放此效果;有关如何执行此操作的详细信息,请参阅编辑 CSS 过滤器。

使用特定设备

如果需要,你可以将允许的视频源限定为特定的设备或特定的一组设备。要做到这一点,请调用 MediaDevices.enumerateDevices。若返回的 promise 兑现了一个 MediaDeviceInfo 对象(描述了可用的设备)数组,可以从中选取一个你想要允许的设备,并将对应的 deviceIdMediaTrackConstraints 对象的 deviceId 作为参数传入到 getUserMedia() 中。

相关文章:

webrtc快速入门——使用 WebRTC 拍摄静止的照片

文章目录 使用 getUserMedia() 拍摄静态照片HTML 标记JavaScript 代码初始化startup() 函数获取元素引用获取流媒体 监听视频开始播放处理按钮上的点击包装 startup() 方法 清理照片框从流中捕获帧 例子代码HTML代码CSS代码JavaScript代码 过滤器使用特定设备 使用 getUserMedi…...

预约按摩app软件开发定制足浴SPA上们服务小程序

同城按摩小程序是一种基于地理位置服务的小程序&#xff0c;它可以帮助用户快速找到附近的按摩师&#xff0c;并提供在线预约、评价、支付等功能。用户可以通过手机或者其他移动设备访问同城按摩小程序&#xff0c;实现足不出户就能预约到专业的按摩服务。 一、同城按摩小程序的…...

jenkins出错与恢复

如果你的jenkins出现了如下图所示问题&#xff08;比如不能下载插件&#xff0c;无法保存任务等&#xff09;&#xff0c;这个时候就需要重新安装了。 一、卸载干净jenknis 要彻底卸载 Jenkins&#xff0c;您可以按照以下步骤进行操作&#xff1a; 1、停止 Jenkins 服务&…...

ssh免密登录的原理RSA非对称加密的理解

RSA非对称加密&#xff0c;是采用公钥加密私钥解密的原则。 举个例子SSH的免密登录 SSH免密登录是通过使用公钥加密技术实现的。以下是SSH免密登录的原理&#xff1a; 1. 生成密钥对&#xff1a;首先&#xff0c;在客户端上生成一对密钥&#xff0c;包括一个私钥和一个公钥。私…...

【监督学习】基于合取子句进化算法(CCEA)和析取范式进化算法(DNFEA)解决分类问题(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

力扣每日一题41:缺失的第一个正数

题目描述&#xff1a; 给你一个未排序的整数数组 nums &#xff0c;请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,0] 输出&#xff1a;3示例 2&#xff1a; 输…...

OpenCV与mediapipe实践

1. 安装前准备 开发环境&#xff1a;vscode venv 设置vscode, 建立项目&#xff0c;如: t1/src, 用vscode打开&#xff0c;新建终端Terminal&#xff0c;这时可能会有错误产生&#xff0c;解决办法&#xff1a; 运行命令&#xff1a;Set-ExecutionPolicy -ExecutionPolicy …...

【css拾遗】粘性布局实现有滚动条的情况下,按钮固定在页面底部展示

效果&#xff1a; 滚动条滚动过程中&#xff0c;按钮的位置位于手机的底部 滚动条滚到底部时&#xff0c;按钮的位置正常 这个position:sticky真的好用&#xff0c;我原先的想法是利用滚动条滚动事件去控制&#xff0c;没想到css就可以解决 <template><view class…...

git 创建并配置 GitHub 连接密钥

前记&#xff1a; git svn sourcetree gitee github gitlab gitblit gitbucket gitolite gogs 版本控制 | 仓库管理 ---- 系列工程笔记. Platform&#xff1a;Windows 10 Git version&#xff1a;git version 2.32.0.windows.1 Function&#xff1a; git 创建并配置 GitHub…...

使用Premiere、PhotoShop和Audition做视频特效

今天接到一个做视频的任务&#xff0c;给一个精忠报国的视频&#xff0c;要求&#xff1a;   ①去掉人声&#xff0c;就是将唱歌的人声去掉&#xff0c;只留下伴奏&#xff1b;   ②截图视频中的横幅&#xff0c;做一个展开的效果&#xff0c;类似卷纸慢慢展开&#xff1b;…...

vueday01——动态参数

我们现在知道了 v-bind:的语法糖是: v-on:的语法糖是 我们现在来尝试一下&#xff0c;定义一个动态参数模拟点击事件按钮 <div :id"idValue" ref"myDiv">我是待测div{{ resultId }}</div> <button v-on:[eventName]"doSomething&…...

双向链表C语言版本

1、声明链表节点操作函数 linklist.h #ifndef LINKLIST_H__ #define LINKLIST_H__ #include <stdio.h> #include <stdlib.h> #include <stdbool.h>//#define TAIL_ADD #define HEAD_ADD typedef int LinkDataType; // 构造节点 struct LinkNode {LinkDataTy…...

visual studio安装时候修改共享组件、工具和SDK路径方法

安装了VsStudio后,如果自己修改了Shared路径&#xff0c;当卸载旧版本&#xff0c;需要安装新版本时发现&#xff0c;之前的Shared路径无法进行修改&#xff0c;这就很坑爹了&#xff0c;因为我运行flutter程序的时候&#xff0c;报错找不到windows sdk的位置&#xff0c;所以我…...

Motorola IPMC761 使用边缘TPU加速神经网络

Motorola IPMC761 使用边缘TPU加速神经网络 人工智能(AI)和机器学习(ML)正在塑造和推进复杂的自动化技术解决方案。将这些功能集成到硬件中&#xff0c;解决方案可以识别图像中的对象&#xff0c;分析和检测模式中的异常或找到关键短语。这些功能对于包括但不限于自动驾驶汽车…...

EM@直线的参数方程

文章目录 abstract直线参数方程从运动轨迹的角度从普通方程转换导参数方程向量法 参数方程间的转换从第3型转化为第2型方程组例 abstract 平面直线的参数方程的3种表示形式直线参数方程间的转换 直线参数方程 以下从不同角度推导直线参数方程分别记为第1,2,3形式参数方程 从…...

day08-注册功能、前端登录注册页面复制、前端登录功能、前端注册功能

1 注册功能 补充(开放文件夹内) 2 前端登录注册页面复制 4 前端注册功能 1 注册功能 # 分析前端&#xff1a;携带数据格式 {mobile:,code:,password}后端&#xff1a;-1 视图类---》注册方法-2 序列化类---》校验&#xff0c;保存&#xff08;表中字段多&#xff0c;传的少---…...

rust: function

///file: nestd.rs ///ide: RustRover 233.8264.22 /// /// /// /***自定义函数*/ pub fn function() {println!("called my::nested::function()"); }#[allow(dead_code)] fn private_function() {println!("called my::nested::private_function()"); }/…...

零代码编程:用ChatGPT批量下载谷歌podcast上的播客音频

谷歌podcast有很多播客音频&#xff0c;如何批量下载到电脑呢&#xff1f; 以这个播客为例&#xff1a; https://podcasts.google.com/feed/aHR0cHM6Ly9oYWRhcnNoZW1lc2guY29tL2ZlZWQvcG9kY2FzdC8?saX&ved0CAkQlvsGahcKEwi4uauWsvKBAxUAAAAAHQAAAAAQAg 查看网页源代码&a…...

nginx.4——正向代理和反向代理(七层代理和四层代理)

1、正向代理反向代理 nginx当中有两种代理方式 七层代理(http协议) 四层代理(tcp/udp流量转发) 七层代理 七层代理&#xff1a;代理的是http的请求和响应。 客户端请求代理服务器&#xff0c;由代理服务器转发给客户端http请求。转发到内部服务器&#xff08;可以单台&#…...

基于RuoYi-Flowable-Plus的若依ruoyi-nbcio支持自定义业务表单流程(三)

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 相应的后端也要做一些调整 1、启动流程修改如下&#xff1a; /*** 启动流程实例*/private R startProce…...

Spring-事务源码解析2

上一篇文章我们介绍了事务开启注解EnableTransactionManagement源码解析《Spring-事务源码解析1》 里面提到了2个关键组件&#xff0c;这里我们分析下Spring如何利用这2个组件来给Bean创建代理对象。 本篇文章我们看下当一个类里面包含了Transactional注解&#xff0c;Spring如…...

基于ssm008医院门诊挂号系统+jsp【附PPT|开题|任务书|万字文档(LW)和搭建文档】

主要功能 后台登录&#xff1a;4个角色 管理员&#xff1a; ①个人中心、修改密码、个人信息 ②药房管理、护士管理、医生管理、病人信息管理、科室信息管理、挂号管理、诊断信息管理、病例库管理、开药信息管理、药品信息管理、收费信息管理 药房&#xff1a; ①个人中心、修…...

【Linux常用命令11】Linux文件与权限详解

权限 r &#xff1a;读权限&#xff0c;用数字4表示 w &#xff1a;写权限&#xff0c;用数字2表示 x &#xff1a;执行权限&#xff0c;用数字1表示 常用权限 644&#xff1a;代表所有者拥有读、写权限&#xff0c;而所属组和其他人拥有只读权限。 755&#xff1a;代表所有…...

BAT026:删除当前目录指定文件夹以外的文件夹

引言&#xff1a;编写批处理程序&#xff0c;实现删除当前目录指定文件夹以外的文件夹。 一、新建Windows批处理文件 参考博客&#xff1a; CSDNhttps://mp.csdn.net/mp_blog/creation/editor/132137544 二、写入批处理代码 1.右键新建的批处理文件&#xff0c;点击【编辑】…...

Python浏览器自动化

如果你正在进行手机爬虫的工作&#xff0c;并且希望通过模拟浏览器行为来抓取数据&#xff0c;那么Pyppeteer将会是你的理想选择。Pyppeteer是一个强大的Python库&#xff0c;它可以让你控制浏览器进行自动化操作&#xff0c;如点击按钮、填写表单等&#xff0c;从而实现数据的…...

基于tornado BELLE 搭建本地的web 服务

我的github 将BELLE 封装成web 后端服务&#xff0c;采用tornado 框架 import timeimport torch import torch.nn as nnfrom gptq import * from modelutils import * from quant import *from transformers import AutoTokenizer import sys import json #import lightgbm a…...

信息系统漏洞与风险管理制度

1、总则 1.1、目的 为了进一步规范XXXXX单位信息系统风险管理活动&#xff0c;提升风险管理工作的可操纵性和适用性&#xff0c;使信息网络正常运行&#xff0c;防止网络攻击&#xff0c;保证业务的正常进行&#xff0c;依据XXXXX单位员的相关规范和标准规定&#xff0c;特制…...

Hadoop3教程(十七):MapReduce之ReduceJoin案例分析

文章目录 &#xff08;113&#xff09;ReduceJoin案例需求分析&#xff08;114&#xff09;ReduceJoin案例代码实操 - TableBean&#xff08;115&#xff09;ReduceJoin案例代码实操 - TableMapper&#xff08;116&#xff09;ReduceJoin案例代码实操 - Reducer及Driver参考文献…...

BAT026:删除当前目录及子目录下的空文件夹

引言&#xff1a;编写批处理程序&#xff0c;实现批量删除当前目录及子目录下的空文件夹。 一、新建Windows批处理文件 参考博客&#xff1a; CSDNhttps://mp.csdn.net/mp_blog/creation/editor/132137544 二、写入批处理代码 1.右键新建的批处理文件&#xff0c;点击【编辑…...

nodejs+vue网课学习平台

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…...

四川省二级建造师报名入口官网/搜外网 seo教程

描述 Description   在河上有一座独木桥&#xff0c;一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子&#xff0c;青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数&#xff0c;我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整…...

做网站做丝袜美女的能行吗/今天的新闻

与客户“调情” 作者 Jenni (Dow) Jepsen 译者 侯伯薇 在世界上&#xff0c;到处都有教人们如何调情的课程。某个德国的大学甚至要求他们的IT工程师参加调情的课程——并不是要吸引伙伴&#xff0c;而是要学习如何在工作中更有效地交流。乍听起来似乎有些“轻浮”&#xff0c;但…...

重庆做网站的程序员待遇/苏州关键词搜索排名

拿一个小点的项目来说&#xff0c;不用管什么开发框架&#xff0c;页面中掺和数据库访问代码加逻辑实现代码&#xff0c;直接搞定即可 大一点的项目一般采用三层开发框架&#xff0c;前台表示层&#xff0c;基本逻辑层&#xff0c;数据访问层&#xff0c;数据库的设计... …...

可以做推广的门户网站/线上销售渠道有哪几种

根据此视频入门即可 https://www.bilibili.com/video/av54488377?fromsearch&seid15888036743951343421 把Auto.js的“后台弹出界面” 的权限打开&#xff0c;不进入Auto.js也可以运行调试脚本。...

怎么介绍自己做的电影网站/seo网站推广

360手机N4S配置怎么样&#xff1f;360手机N4S值得购买吗&#xff1f;360手机N4S有几个版本&#xff1f;各版本有什么区别&#xff1f;下面脚本之家的小编就带来了360手机N4S各版本区别对比评测&#xff0c;一起来看看吧。外观设计360手机N4S是360手机N4的升级版&#xff0c;但是…...

大学城网站开发公司/廊坊seo

学习于&#xff1a; https://www.bilibili.com/video/av29268873/?p23 《鸟哥的linux私房菜》先让我们来看看进程的虚拟地址中的0~3g用户空间的存储&#xff1a;正文即代码段&#xff0c;初始化的数据段&#xff0c;未初始化的数据即bss段。bss段和初始化的数据段加在一起就是…...