flask 后端 + 微信小程序和网页两种前端:调用硬件(相机和录音)和上传至服务器
选择 flask 作为后端,因为后续还需要深度学习模型,python 语言最适配;而 flask 框架轻、学习成本低,所以选 flask 作为后端框架。
微信小程序封装了调用手机硬件的 api,通过它来调用手机的摄像头、录音机,非常方便。
网页端使用 JavaScript 调用则困难一些,走了很多弯路,在这里记录下来。
前提:已经配置好 python 环境、安装了 flask;
flask 端
flask 的任务是收取前端传来的文件,保存在本地。
from flask import Flask, request, jsonify, render_template
app = Flask(__name__)
app.config.from_object(__name__)
app.config["JSON_AS_ASCII"] = False # 防止中文乱码
app.json.ensure_ascii = False # 防止中文乱码
# 设置上传文件夹
app.config['UPLOAD_FOLDER'] = r'D:\A_data_trans\test(改成你的位置)'@app.route('/vqa', methods=['POST'])
def app_vqa():# 保存图片img_file = request.files['img'] # 这里规定了前端传图片过来的时候,用的关键字是 'img',别的,比如 'image' 就会拿不到if img_file.filename == '':return jsonify({'error': 'No image'}), 400try:image_path = os.path.join(app.config['UPLOAD_FOLDER'], img_file.filename)img_file.save(image_path)log(f"save image: {image_path}")except Exception as e:return jsonify({'error': str(e)}), 500# 传过来的就是文本question = request.form['question'] # 前端传来的文本信息都是放在 form 中的# 预测答案try:answer = vqa(image_path, question)return jsonify(answer)except Exception as e:return jsonify({'error': str(e)}), 500# 接收文件的代码,其实和上面长得一样,略微有一 miu miu 区别
@app.route('/upload', methods=['POST'])
def app_upload_file():# 保存图片img_file = request.files['img']if img_file.filename == '':return jsonify({'error': 'No image'}), 400try:image_path = os.path.join(app.config['UPLOAD_FOLDER'], img_file.filename)img_file.save(image_path)shutil.copy(image_path, os.path.join(os.path.dirname(__file__), 'static/show.jpg')) # 用于展示在网页上log(f"save image: {image_path}")except Exception as e:return jsonify({'error': str(e)}), 500try:# 传过来的就是文本question = request.form['question']except:question = "请描述图片内容"return jsonify({"image": img_file.filename, "question": question})@app.route('/upload/speech', methods=['POST'])
def recognize_speech():speech_file = request.files['speech']try:save_path = os.path.join(app.config['UPLOAD_FOLDER'], speech_file.filename)speech_file_path = os.path.join(app.config['UPLOAD_FOLDER'], save_path)speech_file.save(speech_file_path)# question = speech2txt(speech_file_path)# print('百度识别结果:', question)except Exception as e:return jsonify({'error': str(e)}), 500return jsonify({"speech": speech_file.filename})
微信小程序
微信小程序端的任务是,调用手机相机,把相机画面展示给用户,加一个按钮,点击按钮拍照;另外一个按钮,点击可以把拍到的照片上传。
wxml
中,放上一个 camera
用来显示相机画面;放上几个 button
,控制拍照、上传。
<!--index.wxml-->
<scroll-view class="scrollarea" scroll-y type="list">
<!-- 相机画面 --><view class="my-container"><!-- 显示相机画面 --><camera device-position="back" flash="off" binderror="error" style="width: 90%; height: 200px;"></camera></view><!-- 按钮集合 --><view class="my-container"><!-- 拍照、录音、ocr 按钮 --><view class="button-row"><!-- 拍摄照片按钮 --><button class="btn-normal btn-large" hover-class="btn-pressed" bind:tap="takePhoto">拍摄图片</button><!-- 录音得到 Question --><button class="btn-normal btn-large" hover-class="btn-pressed" bind:touchstart="startRecord" bind:touchend="stopRecord">长按提问</button></view><!-- caption 和 vqa 按钮 --><view class="button-row"><!-- 发送预测 caption 请求 --><button class="btn-normal btn-large" hover-class="btn-pressed" bind:tap="predCaption">描述图片</button><!-- 发送预测 vqa 请求 --><button class="btn-normal btn-large" hover-class="btn-pressed" bind:tap="predVQA">回答问题</button></view></view>
</scroll-view>
用到的 wxss
/**index.wxss**/
page {height: 100vh;display: flex;flex-direction: column;
}
.scrollarea {flex: 1;overflow-y: hidden;
}.btn-normal {margin-top: 10px;padding: 10px;background-color: rgb(252, 226, 230);color: black;border-radius: 0ch;border-color: brown;border-width: 1px;border-style: dotted;cursor: pointer;height: 70px;line-height: 50px;width: 90%;text-align: center;font-size: xx-large;
}.btn-large {height: 300px;
}.btn-pressed {background-color: rgb(202, 129, 140);color: rgb(82, 75, 75);
}.btn-human {background-color: darkseagreen;
}.btn-human-pressed {background-color:rgb(89, 141, 89);color: rgb(75, 82, 77);
}button:not([size=mini]) {width: 90%;
}.useGuide {margin-top: 10px;margin-bottom: 10px;width: 90%;
}.text-question {margin-top: 10px;width: 90%;
}.my-container { display: flex; flex-direction: column; align-items: center; justify-content: center;
} .button-row { display: flex; justify-content: space-between;width: 90%;
} .donot-display {display: none;
}
js
部分。因为微信小程序给封装得很好,所以基本没有什么坑,按照这个写就行,基本不出错。要注意各种 success 方法,要用 success: (res) => {}
的写法,不然在里面调用 this
是识别不到的。
Page({data: {serverUrl: 'http://改成你的', // 服务器地址 photoData: '', // 用户拍摄的图片speechData: '', // 用户提问的录音文件textQuestion: '', // 用户提问文本recorderManager: null,textAnswer: '', // vqa模型识别的文本},// 点击拍照的方法在这里 (按钮绑定在 wxml 就写好了)takePhoto(e) {console.log("拍摄照片")const ctx = wx.createCameraContext();ctx.takePhoto({quality: 'low',success: (res) => {this.setData({photoData: res.tempImagePath // res.tempImagePath 就可以拿到拍到的照片文件的 object url 地址,把这个地址传给服务器,就可以把该文件传给服务器});}});},// 控制长按录音的代码放在这里(按钮绑定在 wxml 就写好了)startRecord() {const recorderManager = wx.getRecorderManager();this.setData({ recorderManager });// 停止录音的回调方法;在这里我加了调用百度语音 api 的东西,这部分会另外写文详说,这里只放出来一部分。所以这里没有把录音文件上传,而是直接把语音识别的结果上传文件夹recorderManager.onStop((res) => {console.log('recorder stop', res);this.setData({ speechData: res.tempFilePath });var baiduAccessToken = wx.getStorageSync('baidu_yuyin_access_token');// 读取文件并转为 ArrayBufferconst fs = wx.getFileSystemManager();fs.readFile({filePath: res.tempFilePath,success: (res) => {const base64 = wx.arrayBufferToBase64(res.data);wx.request({url: 'https://vop.baidu.com/server_api',data: {format: 'pcm',rate: 16000,channel: 1,cuid: 'sdfdfdfsfs',token: baiduAccessToken,speech: base64,len: res.data.byteLength,},method: "POST",header: {'content-type': 'application/json'},success: (res) => {wx.hideLoading();console.log("拿到百度语音api返回的结果")console.log(res.data);var baiduResults = res.data.result;console.log(baiduResults[0]);if (baiduResults.lenth == 0) {wx.showToast({title: '未识别要语音信息!',icon: 'none',duration: 3000})} else {this.setData({textQuestion: baiduResults[0]});}}})}})});// 这里才是控制录音的参数;微信小程序端可以设置这些录音参数,因为后面要调用百度语音识别 api,该 api 仅支持采样率 16000 或 8000,对压缩格式也有要求,所以录音的时候要和 api 的要求保持一致recorderManager.start({format: 'PCM',duration: 20000, // 最长支持 20ssampleRate:16000,encodeBitRate: 48000,numberOfChannels: 1,success: (res) => {console.log('开始录音');},fail: (err) => {console.error('录音失败', err);}});},// 上传的代码放在这里predVQA() {if (this.data.photoData != '' && this.data.textQuestion != ''){console.log('send img' + this.data.photoData);wx.uploadFile({filePath: this.data.photoData,name: 'img', // 文件对应 key,后端通过该 key 获取文件;前后端注意保持一致url: this.data.serverUrl+'/vqa',formData: {'question': this.data.textQuestion},success: (res) => { console.log('成功上传'); if (res.statusCode == 200) {var answer = res.datathis.setData({ textAnswer: answer })} else { console.error(res) }},fail: (err) => { console.error('上传失败'); }})}},
})
网页端的实现
网页端就要复杂很多……掉过很多坑真的很难搞……(这里感谢 b站 up主 “前端石头”,其中摄像头拍照和录音的 js 代码参考了他的代码)
而且这里有 2 个关键的问题:
- 关于视频拍照:如果我把展示视频流的那个控件隐藏掉,那拍出来的照片就是黑的。在微信小程序里就不会有这个问题。原因是,它拍照的原理是,通过 canvas 控件在 video 控件上截图,如果你隐藏掉了,自然没有图可截,就是黑的。我找了很多资料,貌似没有别的解决方法,所以我只能把视频放很小,放角落里……
- 关于录音:js 调用硬件就是很有限制。因为我后面想接百度语音识别的 api,该 api 仅支持采样率 16000 或者 8000 的音频,但是 js 默认录音采样率 48000。我找到一些人说,在
constrains
里面传参,但是,不仅没用,而且传了之后会导致音频损坏……然后问了 chatgpt,它说 js 很难变,只能你先录好,然后通过代码改采样率。我试了直接传音频到服务器,然后 python 代码改采样率。但是 python 代码改采样率用的那个包,在 Windows 下运行会报错,还得下一个软件怎么怎么设置……就是很麻烦。所以,暂时没有找到优雅的解决方案。
html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><link rel="stylesheet" href="{{ url_for('static', filename='css/full_button.css') }}" type="text/css">
</head>
<body><div style="display: flex"><div><video id="videoElement" autoplay="autoplay" muted="muted" style="width: 40px"></video><img id="photo" alt="你的照片" src="" style="display: none"></div><div id="answer" class="answer-text">答案等待中...</div></div><div class="button-grid"><button id="snapButton">拍摄照片</button><button id="recorderButton">录音</button><button id="captionButton">描述图片</button><button id="vqaButton">回答问题</button></div>{# <input type="text" id="textQuestion" placeholder="请输入问题...">#}<script>var imageBlob = null; // 拍摄的图片var speechBlob = null; // 提出的问题// 生成随机文件名function randomFilename() {let now = new Date().getTime();let str = `xxxxxxxx-xxxx-${now}-yxxx`;return str.replace(/[xy]/g, function(c) {const r = Math.random() * 16 | 0;const v = c === 'x' ? r : (r & 0x3 | 0x8);return v.toString(16)})}</script><script type="text/javascript" src="../static/js/user_camera.js"></script><script type="text/javascript" src="../static/js/user_recorder.js"></script><script>// 绑定 vqa 按钮document.getElementById('vqaButton').onclick = function () {if (imageBlob == null) {alert('请先拍摄照片,再点击“描述图片”按钮')} else {if (speechBlob == null) {alert('您还没有提问,请先点击录音按钮录音提问')} else {let filename = randomFilename();const speechFormData = new FormData();// 注意,这里是第一个点:这里放进去的第一个参数是 key,后端就要通过这个 key 拿到文件。第二个参数是文件的二进制数据,blob,别搞错了!我会在 recorder.js 的代码里给这个 speechBlob 赋值,总之它应该是一个 Blob 对象。第三个参数是文件名,这个看你自己的需求。speechFormData.append('speech', speechBlob, filename+'.wav');// 这里是第二个点,把这个路径换成你的位置。// 而且我发现,localhost 和 127.0.0.1 居然是有区别的,// 我搞不太懂这二者的区别,但是有时候我填 127.0.0.1 就会告诉我跨域传数据之类的,// 总之很难……如果你部署到服务器的话,应该是要改成服务器的地址的fetch('http://localhost:8099/upload/speech', {method: 'POST',// 这里把 FormData 放到 body 传过去;如果你还要传别的数据,都放到这个 FormData 里就可以传过去body: speechFormData}).then(response => {console.log('response:', response);if (response.status === 200) {console.log('成功上传音频', response);}}).then(data => console.log('data:', data)).catch(error => console.error(error));const imgFormData = new FormData();imgFormData.append('img', imageBlob, filename+'.jpg');fetch('http://localhost:8099/upload', {method: 'POST',body: imgFormData}).then(response => {console.log('response:', response);if (response.status === 200) {console.log('上传完成');}}).then(data => console.log('data:', data)).catch(error => console.error(error));}}};</script>
</body>
</html>
javascript
的部分
有两个文件,放在 static 文件夹的 js 文件夹下:
user_camera.js
class SnapVideo {// 摄像头流媒体stream;// 页面domvideoElement = document.getElementById('videoElement');snapButton = document.getElementById('snapButton');photoElement = document.getElementById('photo');constructor() {const constraints = {audio: true,video: {facingMode: "environment", // "user" 代表前置摄像头width: 448, // 视频宽度height: 448,frameRate: 60, // 每秒 60 帧}};// 绑定方法this.snapButton.onclick = () => this.takeSnapshot();// this.videoElement.width = constraints.video.width;// this.videoElement.height = constraints.video.height;// 获取摄像头流媒体this.getUserMedia(constraints, (stream) => {// 摄像头流媒体成功回调this.stream = stream;this.videoElement.srcObject = stream;}, (e) => {// 摄像头流媒体失败回调if (e.message === 'Permission denied') {alert('您已经禁止使用摄像头');}console.log('navigator.getUserMedia error: ', e);})}getUserMedia(constrains, success, error) {if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {//最新的标准APInavigator.mediaDevices.getUserMedia(constrains).then(success).catch(error);} else if (navigator.webkitGetUserMedia) {//webkit核心浏览器navigator.webkitGetUserMedia(constraints, success, error)} else if (navigator.getUserMedia) {//旧版APInavigator.getUserMedia(constraints, success, error);}}// 拍照takeSnapshot() {console.log('点击了拍摄按钮');// 利用 canvas 截取视频图片const canvas = document.createElement('canvas');const context = canvas.getContext('2d');canvas.width = this.videoElement.videoWidth;canvas.height = this.videoElement.videoHeight;context.drawImage(this.videoElement, 0, 0, canvas.width, canvas.height);this.photoElement.src = canvas.toDataURL('image/png');canvas.toBlob(function (blob) {// 把 blob 赋给 imageBlob;注意这个 imageBlob 是在 html 文件中声明的!!imageBlob = new Blob([blob], {type: "image/png"});}, "image/png", 1);// this.photoElement.style.display = 'block';}
}new SnapVideo();
另一个文件是 user_recorder.js
// 录音
const recordBtn = document.getElementById('recorderButton');if (navigator.mediaDevices.getUserMedia) {let chunks = [];// 注意,这里这个 audio 传参只能传 true,传别的,录到的音频就是损坏的!!const constraints = { audio: true };navigator.mediaDevices.getUserMedia(constraints).then(stream => {const mediaRecorder = new MediaRecorder(stream);recordBtn.onclick = () => {console.log("点击");if (mediaRecorder.state === "recording") {mediaRecorder.stop();recordBtn.textContent = "录音结束";} else {mediaRecorder.start();recordBtn.textContent = "录音中...";}};mediaRecorder.ondataavailable = e => {chunks.push(e.data);};mediaRecorder.onstop = e => {// 一样的,把 blob 赋给 speechBlob,这个也是在 html 里面的 <script> 声明的speechBlob = new Blob(chunks, {type: "audio/wav"});chunks = [];}},() => { console.error("授权失败!"); });
} else {console.error("浏览器不支持 getUserMedia");
}
相关文章:

flask 后端 + 微信小程序和网页两种前端:调用硬件(相机和录音)和上传至服务器
选择 flask 作为后端,因为后续还需要深度学习模型,python 语言最适配;而 flask 框架轻、学习成本低,所以选 flask 作为后端框架。 微信小程序封装了调用手机硬件的 api,通过它来调用手机的摄像头、录音机,…...

蓝桥杯嵌入式(G431)备赛笔记——ADC+LCD
目录 题目要求(真题): cubeMX配置: 小试牛刀: Keil代码: 效果演示: 题目要求(真题): 使用第十一届第二场真题,练习ADC波部分的代码 cubeMX配…...

最近公共祖先(LCA)
题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。 输入格式 第一行包含三个正整数 N,M,S,分别表示树的结点个数、询问的个数和树根结点的序号。 接下来 N−1 行每行包含两个正整数x,y,表示 x 结点和 y 结点之间有一条直接连接的边(数据保证可以…...

ABBYY FineReader15免费电脑OCR图片文字识别软件
产品介绍:ABBYY FineReader 15 OCR图片文字识别软件 ABBYY FineReader 15是一款光学字符识别(OCR)软件,专门设计用于将扫描的文档、图像和照片中的文本转换成可编辑和可搜索的格式。这款软件利用先进的OCR技术,能够识别…...

2024年第十七届 认证杯 网络挑战赛 (A题)| 保暖纤维的保暖能力 |数学建模完整代码+建模过程全解全析
当大家面临着复杂的数学建模问题时,你是否曾经感到茫然无措?作为2022年美国大学生数学建模比赛的O奖得主,我为大家提供了一套优秀的解题思路,让你轻松应对各种难题。 让我们来看看认证杯 网络挑战赛 (A题)!…...

算法训练营第37天|LeetCode 738.单调递增的数字 968.监控二叉树
LeetCode 738.单调递增的数字 题目链接: LeetCode 738.单调递增的数字 解题思路: 从后向前遍历,当不满足递增条件时,当前位置赋值为9,前一位减一。之后记录不满足位置,将后续全部赋值为9. 代码&#x…...

Vue+el-table 修改表格 单元格横线边框颜色及表格空数据时边框颜色
需求 目前 找到对应的css样式进行修改 修改后 css样式 >>>.el-table th.el-table__cell.is-leaf {border-bottom: 1px solid #444B5F !important;}>>>.el-table td.el-table__cell,.el-table th.el-table__cell.is-leaf {border-bottom: 1px solid #444B5F …...

大恒相机-程序异常退出后显示被占用
心跳时间代表多久向相机发送一次心跳包,如果超时则设备会认为断开了,停止工作并主动释放占用资源。 在相机打开后添加代码: #ifdef _DEBUG//设置心跳超时时间 3sObjFeatureControlPtr->GetIntFeature("GevHeartbeatTimeout")-&…...

头歌-机器学习 第16次实验 EM算法
第1关:极大似然估计 任务描述 本关任务:根据本节课所学知识完成本关所设置的选择题。 相关知识 为了完成本关任务,你需要掌握: 什么是极大似然估计; 极大似然估计的原理; 极大似然估计的计算方法。 什么是极大似然估计 没有接触过或者没有听过”极大似然估计“的同学…...

电脑启动引导的两种方式
电脑启动引导的两种方式 电脑启动引导有两种方式:Legacy 传统模式 和 UEFI 新型模式。 一、Legacy:指 主板的 传统的 BIOS 传输模式引导启动加载操作系统。 1.只支持 MBR 分区表,支持 32位和64位操作系统(如:winXP&…...

用php编写网站源码的一些经验
一、var_dump()函数 var_dump()函数在有页面跳转的情况下会看不到信息。因为 var_dump()函数输出信息默认显示到本页面。因此要看到var_dump()函数的输出,在有页面跳转时,需要将页面跳转改成显示本页面。 放在var_dump()函数里的变量如果是空值&#x…...

海山数据库(He3DB)原理剖析:浅析OLAP数据库计算引擎中的统计信息
背景: 统计信息在计算引擎的优化器模块中经常被提及,尤其是在基于成本成本优化(CBO)框架中统计信息发挥着至关重要的作用。CBO旨在通过评估执行查询的可能方法,并选择最有效的执行计划来提高查询性能。而统计信息则提…...

视频图像的两种表示方式YUV与RGB(4)
本篇主要讲YUV与RGB之间的转换,包括YUV444 颜色编码格式 转为 RGB 格式 ,RGB颜色编码格式转为 YUV444 格式。 一、 YUV与RGB之间的转换 YUV与RGB颜色格式之间进行转换时 , 涉及一系列的数学运算 ; YUV 颜色编码格式转为RGB格式的转换公式 取决于 于 YUV …...

PostgreSQL入门到实战-第十四弹
PostgreSQL入门到实战 PostgreSQL数据过滤(七)官网地址PostgreSQL概述PostgreSQL中BETWEEN 命令理论PostgreSQL中BETWEEN 命令实战更新计划 PostgreSQL数据过滤(七) BETWEEN运算符允许您检查值是否在值的范围内。 官网地址 声明: 由于操作系统, 版本更新等原因, 文章所列内容…...

分布式数据库中间件 Mycat 和 ShardingSphere 对比
Mycat 和 ShardingSphere 都是流行的分布式数据库中间件,都可以用于实现数据分片、读写分离和分布式事务等功能,但它们在设计理念、特点和功能实现上有一些区别 1. 设计理念: Mycat: 基于 MySQL 协议的代理式架构,主要…...

Python实现BOA蝴蝶优化算法优化BP神经网络回归模型(BP神经网络回归算法)项目实战
说明:这是一个机器学习实战项目(附带数据代码文档视频讲解),如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 蝴蝶优化算法(butterfly optimization algorithm, BOA)是Arora 等人于2019年提出的一种元启发式智能算…...

3D Web轻量化引擎HOOPS Commuicator如何从整体装配中创建破碎的装配零件和XML?
前言 虽然可以从某些本机CAD格式(其子组件驻留在单独的文件中,例如CATIA V5、Creo - Pro/E、NX或SolidWorks)创建破碎装配,但无法从整体装配文件(例如IFC、Revit)创建或3DXML。 本文介绍了一个示例&#…...

关于运行阿里云直播Demo pub get 报的错
flutter --version dart --version 如何使用Flutter框架推流_音视频终端 SDK(Apsara Video SDK)-阿里云帮助中心 终端输入 dart pub --trace get --no-precompile 打印详细报错信息 详细咨询chatgpt pub.dev 中已经是最新版本了 项目中已经是最新版本了 最终定位到 终端…...

C语言调用Python
目录 1.直接调用python语句 头文件引用 2.调用无参有参函数 1、调用无参函数 1.建立nopara.py文件 2.使用c语言根据上面流程进行调用 2、调用有参函数 1.建立nopara.py文件 2.使用c语言根据上面流程进行调用 C语言调用python需要我们已经安装好了libpython3的 dev依赖…...

SVN客户端异常问题处理
1、如遇svn服务端异常(所有用户登录不上) (1)登录192.168.**.**服务器,找到E:\仓库所在盘\VisualSVN-GlobalWinAuthz.ini (2)先备份VisualSVN-GlobalWinAuthz.ini文件 (3…...

gin+sse实现离散的消息通知
虽然网上的都是用sse实现将实时消息流不间断的推给前端,但是sse也可以模拟websocket进行突发的消息通知,而不是一直读取数据并返回数据。即服务端保存所有的连接对象,前端管理界面发送正常的http请求,在后端遍历所有的连接对象&am…...

C++ //练习 11.38 用unordered_map重写单词计数程序(参见11.1节,第375页)和单词转换程序(参见11.3.6节,第391页)。
C Primer(第5版) 练习 11.38 练习 11.38 用unordered_map重写单词计数程序(参见11.1节,第375页)和单词转换程序(参见11.3.6节,第391页)。 环境:Linux Ubuntu࿰…...

【示例】MySQL-4类SQL语言-DDL-DML-DQL-DCL
前言 本文主要讲述MySQL中4中SQL语言的使用及各自特点。 SQL语言总共分四类:DDL、DML、DQL、DCL。 SQL-DDL | Data Definition Language 数据定义语言:用来定义/更改数据库对象(数据库、表、字段) 用途 | 操作数据库 # 查询所…...

基于SpringBoot+Vue的果蔬种植销售一体化服务平台(源码+文档+部署+讲解)
一.系统概述 伴随着我国社会的发展,人民生活质量日益提高。于是对果蔬种植销售一体化服务管理进行规范而严格是十分有必要的,所以许许多多的信息管理系统应运而生。此时单靠人力应对这些事务就显得有些力不从心了。所以本论文将设计一套果蔬种植销售一体…...

数据结构面试
当然可以!以下是数据结构面试问题及答案整理: **什么是数据结构?** 答:数据结构是指组织和存储数据的方式,它允许高效地访问和操作数据。不同的数据结构有不同的优势和适用场景。常见的基本数据结构包括数组、链表、…...

Linux 上安装 SQLite
SQLite Download Page 从上面的链接中源代码区下载 sqlite-autoconf-*.tar.gz。 历史版本见下连接: https://sqlite.org/chronology.html...

C++模板初阶(个人笔记)
模板初阶 1.泛型编程2.函数模板2.1函数模板的实例化2.2模板参数的匹配规则 3.类模板3.1类模板的实例化 1.泛型编程 泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。 //函数重载 //交换函数的逻辑是一致的,…...

如何用Java后端处理JS.XHR请求
Touching searching engine destroies dream to utilize php in tomcat vector.The brave isn’t knocked down,turn its path to java back-end. Java Servlet Bible schematic of interaction between JS front-end and Java back-end Question 如何利用Java…...

分布式锁-redission
5、分布式锁-redission 5.1 分布式锁-redission功能介绍 基于setnx实现的分布式锁存在下面的问题: 重入问题:重入问题是指 获得锁的线程可以再次进入到相同的锁的代码块中,可重入锁的意义在于防止死锁,比如HashTable这样的代码…...

C/C++ 自定义头文件,及头文件结构详解
头文件 在之前介绍的大部分C语言语法基础的章节中列举的实例代码部分,都会在源文件的开始的第一行通过#include预处理指令包含进"stdio.h",后面这个".h"后缀名的就是头文件了。而什么是头文件呢? 通俗方式理解头文件 …...