chrom扩展开发配合百度图像文字识别实现自动登录(后端.net core web api)

好久没做浏览器插件开发了,因为公司堡垒机,每次登录都要输入账号密码和验证码。太浪费时间了,就想着做一个右键菜单形式的扩展。

实现思路也很简单,在这里做下记录,方便下次开发参考。

一,先来了解下chrome扩展开发,必备文件。

manifest.json也叫清单文件。
先简单看下配置:

 //需要做C#兼职  或者浏览器插件兼职的请加我QQ:3388486286
{"manifest_version": 2,"name": "堡垒机自动化","version": "1.0","description": "右键菜单跳转到堡垒机页面,并自动登录","permissions": ["tabs", "contextMenus","webRequest","webRequestBlocking","https://127.0.0.4/*","http://127.0.0.4/*"],"background": {"scripts": ["background.js"]},"content_scripts": [{"matches": ["https://127.0.0.4/*","http://127.0.0.4/*"],"js": ["JS/log.js"]}],"web_accessible_resources": ["JS/config.json"],"icons": {"16": "Images/icon.png","48": "Images/icon.png","128": "Images/icon.png"}}

上述配置基本包含了插件开发的常用配置。现在简单介绍下每个配置的作用:

1.manifest_version :也就是版本的意思,你需要使用的插件版本,不过现在已经是第三版了,因为之前用过2开发,2和3的语法有差别,估本版本还是用到老版本的语法。新版本没时间去研究,反正都能用。

2.name :插件名称(自定义)

3.version : 当前插件的版本(自定义)

4.description : 插件描述

5.permissions :权限(你需要使用哪些API就需要对应的开通权限,也叫注册服务吧)

 "tabs", (该权限可以打开新的页面)
"contextMenus",(该权限可以右键创建新的菜单)
"webRequest",(该权限可以监听网络请求)
"webRequestBlocking",(该权限可以监听网络请求并且修改请求)
"https://127.0.0.4/*",(表示插件需要访问以 HTTPS 协议开头、IP 地址为 127.0.0.4 的所有页面或资源。这意味着插件将被允许在浏览器中与这些特定的 IP 地址和相关页面进行通信,无论是通过 HTTP 还是 HTTPS 访问。)
"http://127.0.0.4/*"(表示插件需要访问以 HTTP 协议开头、IP 地址为 127.0.0.4 的所有页面或资源。)
添加对应的地址作用:这意味着插件将被允许在浏览器中与这些特定的 IP 地址和相关页面进行通信,无论是通过 HTTP 还是 HTTPS 访问。
  1. "background":
    { "scripts": ["background.js"] }该脚本会一直运行监听。

具体含义如下:

"background":表示指定插件的后台脚本。
"scripts":表示指定后台脚本文件的路径。
"background.js":表示插件的后台页面脚本文件名为 "background.js"。
通过将脚本文件名添加到 "scripts" 数组中,你可以告诉浏览器插件在加载时要运行哪个脚本文件作为后台页面。这个后台页面脚本通常用于处理插件的核心功能、与浏览器 API 进行交互以及响应来自其他插件部分(如内容脚本或浏览器操作栏)的事件。

7.content_scripts

 //content_scripts  为业务JS注入,就是你要操作前端页面元素的JS  其中matches表示你JS要注入到哪些页面地址。"content_scripts": [{"matches": ["https://127.0.0.4/*","http://127.0.0.4/*"],"js": ["JS/log.js"]   //需要注入的Js文件}]

8.web_accessible_resources:这里面往往放些静态全局的配置文件。
9.icons:图标文件。

二 、先说下background.js的思路。

1.先注册右键菜单,这样你右键浏览器,就可以看到自己的扩展程序了。

chrome.contextMenus.create({title:"堡垒机自动化",onclick:function(){console.log('准备跳转..')//跳转指定页面chrome.tabs.create({url:'https://127.0.0.4/index.php/login'},(tab)=>{console.log('跳转成功..')console.log(tab)//执行业务代码 注入JSchrome.tabs.executeScript(tab.id,{ file: 'JS/content_script.js'})})}})

其中executeScript也是注入JS的一种方式,这种方式比较灵活,这种注入方式也可以和页面元素进行交互。

2.跳转到指定页面后,我们需要输入账号和密码,这块业务逻辑就写在content_script.js


var credentials = {};// 读取配置文件并存储到全局对象
async function getConfig() {try {const response = await fetch(chrome.runtime.getURL('JS/config.json'));credentials = await response.json();console.log(credentials); // 打印全局对象// 调用填充函数fillCredentials();} catch (error) {console.error('Error reading config file:', error);}
} // 在页面上填充账号和密码
function fillCredentials() {document.querySelector("#pwd_username").value = credentials.username;document.querySelector("#pwd_pwd").value = credentials.password;//GetAccessToken();}

这里我们调用下getConfig()方法,进行配置文件读取,然后赋值给全局变量存储。,然后调用fillCredentials()进行账号密码赋值。

3.验证码识别,并赋值登录。
我们验证码就是一个4位字母图片,这块逻辑我是直接调用百度API实现的,本来想着自己后端实现,但是用了下第三方库,要么响应时间太久了,要么就是识别不准确。百度API直接帮你解决了这2个麻烦。但是要收费,我只用了100次的免费体验。
现在说下具体实现思路吧,本来想着直接前端发送请求识别,但是浏览器有同源策略,跨域了…这个最终还是要用到后端服务,于是就用了后端API去做图片识别功能了。

3.1先获取验证码图片的url地址。
这里我们在background.js中添加网络监听,因为图片url有固定格式,很好匹配。具体代码如下:

// 声明一个变量用于保存监听器
let requestListener;
// 启动网络请求监听器requestListener = function(details) {// 检查请求地址是否以指定的 URL 开头if (details.url.startsWith('https://127.0.0.4/captcha/')) {// 提取图片地址const imageUrl = details.url;// 输出图片地址console.log('图片地址:', imageUrl);// 在这里可以进行进一步的处理sendImageURLToContentScript(imageUrl);}}chrome.webRequest.onBeforeRequest.addListener(requestListener,{ urls: ['https://127.0.0.4/captcha/*'] }, // 监听以指定 URL 开头的请求['blocking']);

通过 chrome.webRequest.onBeforeRequest.addListener开启网络监听,当匹配到指定url开头的信息后,就代表该url是图片url,然后把imageUrl传给content_script.js
这里声明下:
background.js是不能直接和操作的页面通信的,background.js主要操作浏览器提供的API。实际操作Dom元素需要通过业务JS去操作。这里的业务JS就是content_script.js
那么background.jscontent_script.js如何通信呢,看代码:

 // 在background.js中获取到图片地址后,延迟1秒发送消息给业务 JSfunction sendImageURLToContentScript(imageUrl) {chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {if (tabs.length > 0) {const tabId = tabs[0].id;setTimeout(function() {chrome.tabs.sendMessage(tabId, { imageUrl: imageUrl }, function(response) {// 在收到业务 JS 的响应后进行处理(可选)console.log('收到业务 JS 的响应:', response);});}, 500); // 延迟1秒后发送消息}});}

通过 chrome.tabs.sendMessage 发送消息。

// 业务 JS 接收消息,并进行处理
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {// 接收到来自 background.js 的消息if (request.imageUrl) {const imageUrl = request.imageUrl;// 在这里进行业务逻辑处理,使用获取到的图片地址console.log('收到来自 background.js 的图片地址:', imageUrl);GetCode(imageUrl);//GetBase64(imageUrl);// 可以通过 sendResponse 向 background.js 发送响应消息(可选)sendResponse({ message: '已收到图片地址' });}});

通过chrome.runtime.onMessage.addListener接收消息
这样就实现了background.jscontent_script.js的通信。
至于为什么要延迟500毫秒再发送,是因为我们点击右键后,content_script.js才注入到页面,而再这时候,background.js已经再运行了。所有延迟会再发送消息,这样可以避免content_script.js那边接收不到消息。

3.2业务JS发送imageUrl给后端api进行图像验证码识别

 function GetCode(imageUrl){fetch(credentials.getcode_url, {method: "POST",headers: {"Content-Type": "application/json"},body: JSON.stringify(imageUrl)}).then(response => response.text()).then(result => {let data = JSON.parse(result);// 获取 words_result 字段的值  let wordsResult = data.words_result;// 获取数组中第一个元素的 words 字段的值let words = wordsResult[0].words;console.log(words);  // 输出: "code"//赋值codedocument.querySelector("#pwd_captcha").value = words;//点击登录document.querySelector("#sign-box > form.form-vertical.login-content.active > div.submit-row > button").click();}).catch(error => {console.error("请求出错:", error);});}

好了这样就实现了验证码识别,然后赋值登录操作呢。

三、给出所有相关代码

config.json:

{"username": "username","password": "password","client_id":"FrktGAjFVjGv9SSA6S3","client_secret":"IFL6FbU6tuFrPoCjaYnvvRrCGd","token_url":"https://aip.baidubce.com/oauth/2.0/token","getcode_url":"http://localhost:5270/api/CodeIdentity"
}

background.js


// 声明一个变量用于保存监听器
let requestListener;
// 启动网络请求监听器requestListener = function(details) {// 检查请求地址是否以指定的 URL 开头if (details.url.startsWith('https://127.0.0.4/captcha/')) {// 提取图片地址const imageUrl = details.url;// 输出图片地址console.log('图片地址:', imageUrl);// 在这里可以进行进一步的处理sendImageURLToContentScript(imageUrl);}}chrome.webRequest.onBeforeRequest.addListener(requestListener,{ urls: ['https://127.0.0.4/captcha/*'] }, // 监听以指定 URL 开头的请求['blocking']);// 在background.js中获取到图片地址后,延迟1秒发送消息给业务 JSfunction sendImageURLToContentScript(imageUrl) {chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {if (tabs.length > 0) {const tabId = tabs[0].id;setTimeout(function() {chrome.tabs.sendMessage(tabId, { imageUrl: imageUrl }, function(response) {// 在收到业务 JS 的响应后进行处理(可选)console.log('收到业务 JS 的响应:', response);});}, 500); // 延迟1秒后发送消息}});}chrome.contextMenus.create({title:"堡垒机自动化",onclick:function(){console.log('准备跳转..')//跳转指定页面chrome.tabs.create({url:'https://127.0.0.4/index.php/login'},(tab)=>{console.log('跳转成功..')console.log(tab)//执行业务代码 注入JSchrome.tabs.executeScript(tab.id,{ file: 'JS/content_script.js'})})}})// // 启动网络请求监听器
// chrome.webRequest.onBeforeRequest.addListener(
//     function(details) {
//       // 检查请求地址是否以指定的 URL 开头
//       if (details.url.startsWith('https://127.0.0.4/captcha/')) {
//         // 提取图片地址
//         const imageUrl = details.url;//         // 输出图片地址
//         console.log('图片地址:', imageUrl);//         // 在这里可以进行进一步的处理
//         sendImageURLToContentScript(imageUrl);
//       }
//     },
//     { urls: ['https://127.0.0.4/captcha/*'] }, // 监听以指定 URL 开头的请求
//     ['blocking']
//   );// 在background.js中获取到图片地址后,延迟1秒发送消息给业务 JS
// function sendImageURLToContentScript(imageUrl) {
//     chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
//       if (tabs.length > 0) {
//         const tabId = tabs[0].id;//         setTimeout(function() {
//           chrome.tabs.sendMessage(tabId, { imageUrl: imageUrl }, function(response) {
//             // 在收到业务 JS 的响应后进行处理(可选)
//             console.log('收到业务 JS 的响应:', response);
//                  // 移除监听器
//         chrome.webRequest.onBeforeRequest.removeListener(requestListener);
//           });
//         }, 100); // 延迟1秒后发送消息
//       }
//     });
//   }// 开始监听网络请求
//startRequestListener();

content_script.js

// 业务 JS 接收消息,并进行处理
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {// 接收到来自 background.js 的消息if (request.imageUrl) {const imageUrl = request.imageUrl;// 在这里进行业务逻辑处理,使用获取到的图片地址console.log('收到来自 background.js 的图片地址:', imageUrl);GetCode(imageUrl);//GetBase64(imageUrl);// 可以通过 sendResponse 向 background.js 发送响应消息(可选)sendResponse({ message: '已收到图片地址' });}});var credentials = {};// 读取配置文件并存储到全局对象
async function getConfig() {try {const response = await fetch(chrome.runtime.getURL('JS/config.json'));credentials = await response.json();console.log(credentials); // 打印全局对象// 调用填充函数fillCredentials();} catch (error) {console.error('Error reading config file:', error);}
} // 在页面上填充账号和密码
function fillCredentials() {document.querySelector("#pwd_username").value = credentials.username;document.querySelector("#pwd_pwd").value = credentials.password;//GetAccessToken();}/*** 使用 AK,SK 生成鉴权签名(Access Token)* @returns 鉴权签名信息(Access Token)*/
//  function GetAccessToken() {
//     const url = credentials.token_url;
//     const data = {
//       grant_type: 'client_credentials',
//       client_id: credentials.client_id,
//       client_secret: credentials.client_secret
//     };//     const requestOptions = {
//       method: 'POST',
//       mode: 'cors',
//       headers: { 'Content-Type': 'application/json' },
//       body: JSON.stringify(data)
//     };//     return fetch(url, requestOptions)
//       .then(response => {
//         if (!response.ok) {
//           throw new Error('Network response was not ok');
//         }
//         return response.json();
//       })
//       .then(data => {
//         console.log(data);
//         console.log(data.access_token);
//         return data.access_token;
//       })
//       .catch(error => {
//         console.error(error);
//       });
//   }function GetCode(imageUrl){fetch(credentials.getcode_url, {method: "POST",headers: {"Content-Type": "application/json"},body: JSON.stringify(imageUrl)}).then(response => response.text()).then(result => {let data = JSON.parse(result);// 获取 words_result 字段的值  let wordsResult = data.words_result;// 获取数组中第一个元素的 words 字段的值let words = wordsResult[0].words;console.log(words);  // 输出: "code"//赋值codedocument.querySelector("#pwd_captcha").value = words;//点击登录// document.querySelector("#sign-box > form.form-vertical.login-content.active > div.submit-row > button").click();}).catch(error => {console.error("请求出错:", error);});}//图片转base64
//   function getFileContentAsBase64(path) {
//     return new Promise((resolve, reject) => {
//       fetch(path)
//         .then(response => response.arrayBuffer())
//         .then(buffer => {
//           const bytes = new Uint8Array(buffer);
//           let binary = '';
//           for (let i = 0; i < bytes.byteLength; i++) {
//             binary += String.fromCharCode(bytes[i]);
//           }
//           const base64 = btoa(binary);
//           resolve(base64);
//         })
//         .catch(error => reject(error));
//     });
//   }// function GetBase64(captchaUrl)
// {
// // 使用fetch函数获取验证码图片的二进制数据
// fetch(captchaUrl)
//   .then(response => response.blob())
//   .then(blob => {
//     // 创建一个FileReader对象来读取blob数据
//     const reader = new FileReader();
//     reader.onloadend = function() {
//       // 读取完成后,将二进制数据转换为Base64编码
//       const base64 = reader.result.split(',')[1];//       // 调用getFileContentAsBase64方法进行后续处理
//       getFileContentAsBase64(base64)
//         .then(result => {
//           console.log("base64:",result);
//         })
//         .catch(error => {
//           console.error(error);
//         });
//     };
//     reader.readAsDataURL(blob);
//   })
//   .catch(error => {
//     console.error(error);
//   });// }//识别验证码// 获取配置并存储到全局对象
getConfig();

后端代码:

using CodeIdentify.Servers;var builder = WebApplication.CreateBuilder(args);// Add services to the container.builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddScoped<IBaiDuCodeIdentity, BaiDuCodeIdentityRepostoty>();
builder.Services.AddCors(options => {options.AddPolicy("AllowAll", builder =>{builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader();});
});var app = builder.Build();app.UseCors("AllowAll");
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{app.UseSwagger();app.UseSwaggerUI();
}app.UseAuthorization();app.MapControllers();app.Run();
namespace CodeIdentify.Servers
{public interface IBaiDuCodeIdentity{Task<string> GetAccessTokenAsync();Task<string> GetCodeAsync(string base64);Task DownloadImageAsync(string url , string saveDirectory);string GetFileContentAsBase64(string path);void DeleteImage(string path);}
}
using Microsoft.OpenApi.Models;
using Newtonsoft.Json;
using RestSharp;
using System.Net;namespace CodeIdentify.Servers
{public class BaiDuCodeIdentityRepostoty : IBaiDuCodeIdentity{public void DeleteImage(string path){try{// 获取指定文件夹中的所有图片文件string[] imageFiles = Directory.GetFiles(path, "*.jpg");foreach (string file in imageFiles){// 删除文件File.Delete(file);Console.WriteLine($"已删除文件: {file}");}Console.WriteLine("所有图片文件已删除。");}catch (Exception ex){throw new Exception(ex.Message);Console.WriteLine($"删除图片文件时出错:{ex.Message}");}}public async Task DownloadImageAsync(string imageUrl, string saveDirectory){// 创建自定义的 HttpClientHandler,并禁用证书验证var handler = new HttpClientHandler(){ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true};// 创建 HttpClient 实例,并使用自定义的 HttpClientHandlerusing (HttpClient httpClient = new HttpClient(handler)){try{// 发送 GET 请求并获取响应消息HttpResponseMessage response = await httpClient.GetAsync(imageUrl);response.EnsureSuccessStatusCode();// 从响应消息中获取图片内容byte[] imageBytes = await response.Content.ReadAsByteArrayAsync();// 创建文件保存路径Directory.CreateDirectory(saveDirectory);string savePath = Path.Combine(saveDirectory, "image.jpg"); // 要保存的文件名// 将图片内容保存到本地文件File.WriteAllBytes(savePath, imageBytes);Console.WriteLine("图片下载完成。");}catch (Exception ex){throw new  Exception($"图片下载失败:{ex.Message}");Console.WriteLine($"图片下载失败:{ex.Message}");}}}public async Task<string> GetAccessTokenAsync(){try{var client = new RestClient($"https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=FrktGAjFVjSSA6S3Tcs0f&client_secret=IFL6FbU6rPoCjaSiKjMLYnvvRrCGd");var request = new RestRequest(String.Empty, Method.Post);request.AddHeader("Content-Type", "application/json");request.AddHeader("Accept", "application/json");var body = @"";request.AddParameter("application/json", body, ParameterType.RequestBody);var response = await client.ExecuteAsync(request);var result = JsonConvert.DeserializeObject<dynamic>(response.Content??"");Console.WriteLine(result?.access_token.ToString());return result?.access_token.ToString()??"";}catch (Exception e){throw new Exception($"可能次数用完了:{e.Message}");}}public async Task<string> GetCodeAsync(string base64){var token = await GetAccessTokenAsync();var client = new RestClient($"https://aip.baidubce.com/rest/2.0/ocr/v1/accurate_basic?access_token={token}");var request = new RestRequest(String.Empty, Method.Post);request.AddHeader("Content-Type", "application/x-www-form-urlencoded");request.AddHeader("Accept", "application/json");// image 可以通过 GetFileBase64Content('C:\fakepath\captcha.png') 方法获取request.AddParameter("image", base64);var  response = await client.ExecuteAsync(request);Console.WriteLine(response.Content);return response.Content??"";}public string GetFileContentAsBase64(string path){using (FileStream filestream = new FileStream(path, FileMode.Open)){byte[] arr = new byte[filestream.Length];filestream.Read(arr, 0, (int)filestream.Length);string base64 = Convert.ToBase64String(arr);return base64;}}}
}
using CodeIdentify.Servers;
using Microsoft.AspNetCore.Mvc;namespace CodeIdentify.Controllers
{[Route("api/[controller]")][ApiController]public class CodeIdentityController : ControllerBase{private readonly IBaiDuCodeIdentity _baiDuCodeIdentity;public CodeIdentityController(IBaiDuCodeIdentity baiDuCodeIdentity) {_baiDuCodeIdentity = baiDuCodeIdentity;}[HttpPost]public async Task<IActionResult> GetCode([FromBody] string url) {string path = "Images\\image.jpg";string deletepath = "Images";await _baiDuCodeIdentity.DownloadImageAsync(url, "Images");string code = await _baiDuCodeIdentity.GetCodeAsync(_baiDuCodeIdentity.GetFileContentAsBase64(path));if(string.IsNullOrWhiteSpace(code)) return BadRequest("空字符串,识别有误");_baiDuCodeIdentity.DeleteImage(deletepath);return Ok(code);} }
}

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

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

相关文章

浅谈APP自动化测试工具的优势和应用

随着移动应用市场的迅速发展&#xff0c;APP的质量和性能变得至关重要。为了确保APP的稳定性和用户体验&#xff0c;自动化测试工具成为开发者和测试团队的关键利器。那么&#xff0c;APP自动化测试工具的优势和应用是什么?下面&#xff0c;就跟随掌控智能小编一起来看看具体介…

LeetCode——二叉树篇(九)

刷题顺序及思路来源于代码随想录&#xff0c;网站地址&#xff1a;https://programmercarl.com 目录 669. 修剪二叉搜索树 108. 将有序数组转换为二叉搜索树 538. 把二叉搜索树转换为累加树 669. 修剪二叉搜索树 给你二叉搜索树的根节点 root &#xff0c;同时给定最小边界…

【Jetpack】Navigation 导航组件 ④ ( Fragment 跳转中使用 safe args 安全传递参数 )

文章目录 一、页面跳转间的传统的数据传递方式1、传统的数据传递方式 - Bundle 传递数据1、Navigation 组件中的 Bundle 数据传递2、传统数据传递实现步骤3、FragmentA 完整代码示例4、FragmentB 完整代码示例5、执行结果 2、使用 Bundle 传递数据安全性差 二、页面跳转间的传统…

TCP的可靠性之道:确认重传和流量控制

TCP 全称为 Transmission Control Protocol&#xff08;传输控制协议&#xff09;&#xff0c;是一种面向连接的、可靠的、基于字节流的传输层通信协议&#xff0c;其中可靠性是相对于其他传输协议的优势点。TCP 为了确保数据传输的可靠性主要做了以下几点&#xff1a; 发送确…

电脑文件删除了可以找回吗?分享一种简单恢复删除电脑文件办法!

电脑文件删除了可以找回吗&#xff1f;可以。在原理上讲电脑删除的文件是有希望恢复的&#xff0c;因为操作系统在删除文件的时候并会不会立刻将文件彻底删除。当文件被删除的时候&#xff0c;其文件记录被删除&#xff0c;并且被文件占用的磁盘空间被标记为空闲。 这样对于用户…

1.1 VMware Workstation与Kali的安装和配置1

资源见专栏第一篇文章https://blog.csdn.net/algorithmyyds/article/details/132457258 安装VMware 不多加赘述&#xff0c;直接按顺序安装即可。 有以下需注意的地方&#xff1a; 1.建议选择增强型服务&#xff1b; 2.不要加入体验改进计划。是否开启提示更新看你的想法&…

Ubuntu22.0网络/网卡丢失

Ubuntu22.0开机突然连不上网了&#xff0c;右上角网络图标消失了&#xff0c;设置里网络也没有了“有线”&#xff0c;只剩下VPN了&#xff0c;试了好多种办法&#xff0c;最终终于解决了。 看到有些直接用的下面的两条命令&#xff0c;有解决的&#xff0c;不过我这不行。 s…

解决 KylinOS “Could not get lock /var/lib/dpkg/lock”错误

最近,我遇到了 “Could not get lock /var/lib/dpkg/lock”的错误,我既不能安装任何软件包,也不能更新系统。此错误也与“Could not get lock /var/lib/apt/lists/lock”错误密切相关。以下是 Ubuntu 20.04 上的一些样本输出。 Reading package lists… Done E: Could not…

Nvidia Jetson 编解码开发(7)Jetpack 4.x版本Multimedia API 硬件编码开发--输出端对接ROS publish

1.前言 Nvidia Jetson 编解码开发(6)Jetpack 4.x版本Multimedia API 硬件编码开发--输入端对接Camera V4L2采集_free-xx的博客-CSDN博客 基于上篇基于开发 需求: (1)2路Camera采集 + H265编码 (2)2路编码完的H265数据通过ROS 发布出去,上位机播放 2. 开发记录 2…

后端项目开发:分页功能的实现(Mybatis+pagehelper)

分页查询是项目中的常用功能&#xff0c;此处我们基于Mybatis对分页查询进行处理。 引入分页依赖 <!-- pagehelper --> <dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId>…

CSS选择器-CSS3属性

CSS选择器-CSS3属性 持续更新… 1、CSS3的概念和优势 css3概念:是css的升级版本,新增加了一些模块 css3优点:完全向后兼容,可使用新的选择器和属性,能实现新的设计效果CSS3是CSS技术的升级版本&#xff0c;CSS3语言开发是朝着模块化发展的。以前的规范作为一个模块实在是太庞…

【linux】2 make/Makefile和gitee

文章目录 一、Linux项目自动化构建工具-make/Makefile1.1 背景1.2 实例代码1.3 原理1.4 项目清理 二、linux下第一个小程序-进度条2.1 行缓冲区2.2 进度条 三、git以及gitee总结 ヾ(๑╹◡╹)&#xff89;" 人总要为过去的懒惰而付出代价ヾ(๑╹◡╹)&#xff89;" 一…

盛最多水的容器——力扣11

int maxArea(vector<int>& height) {int l=0, r=height.size()

JAVA笔试基础知识-final/static+abstract/interface+wait/sleep+tcp/udp

1、final关键字和static关键字的区别 /*** final修饰类&#xff1a;* 使用final修饰类的目的简单明确&#xff0c;表明这个类不能被继承。* 当程序中有永远不会被继承的类时&#xff0c;可以使用final关键字修饰。* 被final修饰的类所有成员方法都将被隐式修饰为final方法。**…

半导体低压热氧工艺中的真空度精密控制解决方案

摘要&#xff1a;在目前的各种半导体材料热氧化工艺中&#xff0c;往往需要对正负压力进行准确控制并对温度变化做出快速的响应&#xff0c;为此本文提出了热氧化工艺的正负压力控制解决方案。解决方案的核心是基于动态平衡法分别对进气和排气流量进行快速调节&#xff0c;具体…

一文读懂Redis配置,史上真香配置

文章目录 基本配置项AOF持久化配置项RDB持久化配置项淘汰策略配置项主从复制配置项鸣谢 让那些总为redis连接异常的小白指引明灯&#xff0c;少走弯路。为那些不知道如何进行高级配置的大佬整一杯小酒。 基本配置项 bind&#xff1a;用于设置Redis绑定的IP地址。默认情况下&…

SpringBoot 微人事 职称管理模块(十三)

职称管理前端页面设计 在职称管理页面添加输入框 export default {name: "JobLevelMarna",data(){return{Jl:{name:""}}}}效果图 添加一个下拉框 v-model的值为当前被选中的el-option的 value 属性值 <el-select v-model"Jl.titlelevel" …

ViewBinding的基本使用

在app目录下的build.gradle文件中开启viewBinding&#xff0c;开启方式跟DataBinding类似&#xff0c;其中apply plugin: kotlin-android-extensions 是启用绑定机制&#xff0c;跟 ViewBinding 功能类似&#xff0c;都是kotlin自动的 build.gradle apply plugin: com.androi…

Android 9.0 kenel和frameworks中修改ram运行内存的功能实现

1.前言 在9.0的系统rom产品开发定制中,在对一些产品开发中的配置需求方面,在产品后续订单中,在某些机型中需要升级下系统内核配置,项目时间比较仓促,所以 来不及对硬件重新定制,就需要软件方面在ram运行内存的容量大小方面作假,修改ram真实的大小容量,所以就需要在ken…

2023华为杯研赛数学建模A题B题C题D题E题F题资料 华为杯

本次比赛我们将会全程更新华为杯研赛赛题思路模型及代码&#xff0c;大家查看文末名片获取 之前华为杯相关的资料和助攻可以查看 2022华为杯数学建模研赛选题建议和思路分析_方形件组批优化问题_UST数模社_的博客-CSDN博客 我们华为杯更新的流程如下&#xff1a; A题思路&a…