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 访问。
"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.js和content_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.js和content_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);} }
}相关文章:
chrom扩展开发配合百度图像文字识别实现自动登录(后端.net core web api)
好久没做浏览器插件开发了,因为公司堡垒机,每次登录都要输入账号密码和验证码。太浪费时间了,就想着做一个右键菜单形式的扩展。 实现思路也很简单,在这里做下记录,方便下次开发参考。 一,先来了解下chro…...
香港服务器怎么打开SSH
SSH是一种远程登录协议,可以通过加密方式在网络上安全地传输数据。它允许用户在远程服务器上执行命令,管理文件和目录,并进行其他系统管理任务。 如何打开SSH服务? 1.确认已安装OpenSSH服务器: 你可以通过命令sudoapt-geti…...
【LeetCode】437.路径总和Ⅲ
题目 给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。 路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节…...
Mybatis-plus中操作JSON字段
1.实体类上要加上自动映射 TableName(value "school", autoResultMap true)2.json字段上加上json处理器 TableField(value "cover_url", typeHandler JacksonTypeHandler.class)private List<String> cover_url;参考博客 http://www.dedeyun.co…...
第十五课、Windows 下打包发布 Qt 应用程序
功能描述:讲解了 Windows 下打包发布 Qt 应用程序的三种方法,并对比优缺点 一、利用 windepolyqt 工具打包发布 Qt 提供了一个 windeployqt 工具来自动创建可部署的文件夹。 打包发布流程: 1. 新建一个文件夹,将编译后的可执行…...
【php】windows下php运行已有php web项目环境配置教程
php环境配置教程 php安装composer安装扩展安装redis扩展安装 composer install 本文操作系统使用的是win11,软件PhpStorm 2023.1 php安装 要安装的php版本可以在composer.json看到,下载安装对应版本 windows下载地址https://windows.php.net/download …...
【mybatis】 mybatis在mysql 更新update 操作 更新时间字段按照年月日时分秒格式 更新为当前时间...
参考链接 【mybatis】 mybatis在mysql 更新update 操作 更新时间字段按照年月日时分秒格式 更新为当前时间…...
C++动态规划经典案例解析之合并石子
1. 前言 区间类型问题,指求一个数列中某一段区间的值,包括求和、最值等简单或复杂问题。此类问题也适用于动态规划思想。 如前缀和就是极简单的区间问题。如有如下数组: int nums[]{3,1,7,9,12,78,32,5,10,11,21,32,45,22}现给定区间信息[…...
go MongoDB
安装 go get go.mongodb.org/mongo-driver/mongo package mongodbexampleimport ("context""fmt""ginapi/structs""time""go.mongodb.org/mongo-driver/bson""go.mongodb.org/mongo-driver/bson/primitive""…...
算法与数据结构(八)--优先队列
普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除,在某些情况下,我们可能需要找出队列中的最大值或者最小值。 例如使用一个队列保存计算机的任务,一般情况下计算机的任务都是有优先级的ÿ…...
React 全栈体系(三)
第二章 React面向组件编程 四、组件三大核心属性3: refs与事件处理 1. 效果 需求: 自定义组件, 功能说明如下: 点击按钮, 提示第一个输入框中的值当第2个输入框失去焦点时, 提示这个输入框中的值 2. 理解 组件内的标签可以定义ref属性来标识自己 3. 编码 3.1 字符串形式…...
腾讯云下一代CDN -- EdgeOne加速MinIO对象存储
省流 使用MinIO作为EdgeOne的源站。 背景介绍 项目中需要一个兼容S3协议的对象存储服务,腾讯云的COS虽然也兼容S3协议,但是也只是支持简单的上传下载,对于上传的时候同时打标签这种需求,就不兼容S3了。所以决定自建一个对象存储…...
GitLab-CI 指南
GitLab CI 指南 前置工作 部署GitLab 部署GitLab-Runner 注册Runner到GitLab docker exec -it gitlab-runner bash # 进入容器 gitlab-runner register #调用register命令开始注册 # 在Gitlab Setting中找到Runners,如下图所示Enter the GitLab instance URL (for example, …...
MyBatis的核心技术掌握,简单易懂(上)
目录 一.MyBatis中的动态SQL 二.MyBatis中的模糊查询 1. # 符号 2. $ 符号 ---问题 ---所以大家知道 # 和 $ 在MyBatis中的模糊查询中的区别了嘛?? 三.MyBatis 中的结果映射 1. resultType: 2. resultMap: ---问题 ---…...
Redisson自定义序列化
Redisson自定义序列化_redisson 序列化_yzh_1346983557的博客-CSDN博客 redis存取的数据一定是可序列化的,而可序列化方式可以自定义。如果不同客户端设置的可序列化方式不一样,会导致读取不一致的问题。常见的序列化方式有几下几种...
MongoDB Long 类型 shell 查询
场景 1、某数据ID为Long类型,JAVA 定义实体类 Id Long id 2、查询数据库,此数据存在 3、使用 shell 查询,查不到数据 4、JAVA代码查询Query.query 不受任何影响 分析 尝试解决(一) long 在 mongo中为 int64 类型…...
回归预测 | MATLAB实现GA-APSO-IBP改进遗传-粒子群算法优化双层BP神经网络多输入单输出回归预测
回归预测 | MATLAB实现GA-APSO-IBP改进遗传-粒子群算法优化双层BP神经网络多输入单输出回归预测 目录 回归预测 | MATLAB实现GA-APSO-IBP改进遗传-粒子群算法优化双层BP神经网络多输入单输出回归预测效果一览基本介绍模型描述程序设计参考资料 效果一览 基本介绍 MATLAB实现GA-…...
Spring cache整合Redis使用介绍
🍓 简介:java系列技术分享(👉持续更新中…🔥) 🍓 初衷:一起学习、一起进步、坚持不懈 🍓 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正🙏 🍓 希望这篇文章对你有所帮助,欢…...
Metasploit提权
一、bypassuac 用户账户控制(User Account Control,简写作UAC)是微软公司在其Windows Vista及更高版本操作系统中采用的一种控制机制。其原理是通知用户是否对应用程序使用硬盘驱动器和系统文件授权,以达到帮助阻止恶意程序(有时也…...
TypeScript三种特殊类型
1.any类型 说明:any类型代表着可以赋值任意类型 let nickname:any"王二"nickname15nicknametruenicknameundefinednicknamenullnickname{}2.unknown类型 说明:类似any类型;只是不能赋值到其它类型上;除了any和known。…...
stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...
Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)
目录 一、👋🏻前言 二、😈sinx波动的基本原理 三、😈波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、🌊波动优化…...
LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
