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

【后端】构建简洁的音频转写系统:基于火山引擎ASR实现

在当今数字化时代,语音识别技术已经成为许多应用不可或缺的一部分。无论是会议记录、语音助手还是内容字幕,将语音转化为文本的能力对提升用户体验和工作效率至关重要。本文将介绍如何构建一个简洁的音频转写系统,专注于文件上传、云存储以及ASR(自动语音识别)的集成,特别是基于火山引擎ASR服务的实现。

系统架构概览

一个简洁的音频转写系统需要包含以下几个关键组件:

  1. 前端界面:提供用户上传音频文件的入口
  2. API服务层:处理请求和业务逻辑
  3. 云存储服务:安全存储音频文件
  4. ASR服务:将音频转写为文本(本文使用火山引擎ASR服务)

系统流程如下:

用户 → 上传音频 → 存储到云服务 → 触发ASR转写 → 获取转写结果 → 返回给用户

技术选型

我们的最小实现基于以下技术栈:

  • 后端框架:FastAPI(Python)
  • 云存储:兼容S3协议的对象存储
  • ASR服务:火山引擎ASR服务
  • 异步处理:基于asyncio的异步请求处理

详细实现

1. 音频文件上传流程

实现音频上传有两种主要方式:

1.1 预签名URL上传

这种方式适合大文件上传,减轻服务器负担:

async def create_upload_url(file_name, file_size, mime_type):"""创建上传链接"""# 生成唯一文件名timestamp = datetime.now().strftime("%Y%m%d%H%M%S")random_suffix = os.urandom(4).hex()file_ext = os.path.splitext(file_name)[1]filename = f"{timestamp}_{random_suffix}{file_ext}"# 生成存储路径storage_path = f"audio/{filename}"# 获取预签名URLupload_url = storage_client.generate_presigned_url(storage_path,expiry=300,  #5分钟有效期http_method="PUT",content_length=file_size)return {"upload_url": upload_url,"storage_path": storage_path}

前端调用示例:

// 1. 获取上传URL
const response = await fetch('/api/audio/upload-url', {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify({file_name: file.name,file_size: file.size,mime_type: file.type})
});
const { upload_url, storage_path } = await response.json();// 2. 使用预签名URL上传文件
await fetch(upload_url, {method: 'PUT',body: file,headers: { 'Content-Type': file.type }
});// 3. 触发转写
const transcriptResponse = await fetch('/api/audio/transcribe', {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify({ storage_path })
});
const transcriptResult = await transcriptResponse.json();
1.2 直接上传方式

适合较小文件,通过API直接上传:

async def upload_audio(file):"""直接上传音频文件"""# 验证文件类型if file.content_type not in ALLOWED_AUDIO_TYPES:raise ValueError("不支持的文件类型")# 读取文件内容contents = await file.read()if len(contents) == 0:raise ValueError("文件内容为空")# 生成唯一文件名timestamp = datetime.now().strftime("%Y%m%d%H%M%S")random_suffix = os.urandom(4).hex()file_ext = os.path.splitext(file.filename)[1]filename = f"{timestamp}_{random_suffix}{file_ext}"# 存储路径storage_path = f"audio/{filename}"# 上传到云存储storage_client.upload(storage_path, contents)# 生成访问URLaccess_url = storage_client.generate_presigned_url(storage_path,expiry=3600,  # 1小时有效期http_method="GET")return {"file_name": file.filename,"storage_path": storage_path,"file_size": len(contents),"mime_type": file.content_type,"access_url": access_url,"url_expires_at": datetime.now() + timedelta(hours=1)}

2. ASR语音转写实现

可以通过两种方式调用ASR服务:基于存储路径或直接通过URL。

2.1 基于存储路径的转写
async def transcribe_audio_by_storage_path(storage_path):"""通过存储路径转写音频文件"""# 生成可访问的URLaccess_url = storage_client.generate_presigned_url(storage_path,expiry=3600,http_method="GET")# 调用ASR服务transcript_result = await _call_asr_service(access_url)return {"storage_path": storage_path,"transcript": transcript_result.get("text", ""),"segments": transcript_result.get("segments", []),"duration": transcript_result.get("duration")}
2.2 基于URL的转写
async def transcribe_audio_by_url(audio_url):"""通过URL转写音频"""# 调用ASR服务transcript_result = await _call_asr_service(audio_url)return {"audio_url": audio_url,"transcript": transcript_result.get("text", ""),"segments": transcript_result.get("segments", []),"duration": transcript_result.get("duration")}
2.3 上传并立即转写
async def upload_and_transcribe(file):"""上传并立即转写音频文件"""# 上传文件upload_result = await upload_audio(file)# 转写音频transcript_result = await _call_asr_service(upload_result["access_url"])# 组合结果return {"file_name": upload_result["file_name"],"storage_path": upload_result["storage_path"],"file_size": upload_result["file_size"],"mime_type": upload_result["mime_type"],"access_url": upload_result["access_url"],"transcript": transcript_result.get("text", ""),"segments": transcript_result.get("segments", []),"duration": transcript_result.get("duration")}

3. 火山引擎ASR服务调用实现

以下是基于火山引擎ASR服务的详细实现:

async def _call_asr_service(audio_url):"""调用火山引擎ASR服务进行转写"""# 生成唯一任务IDtask_id = str(uuid.uuid4())# 火山引擎ASR服务API端点submit_url = "https://openspeech.bytedance.com/api/v3/auc/bigmodel/submit"query_url = "https://openspeech.bytedance.com/api/v3/auc/bigmodel/query"# 构建请求头headers = {"Content-Type": "application/json","X-Api-App-Key": APP_KEY,"X-Api-Access-Key": ACCESS_KEY,"X-Api-Resource-Id": "volc.bigasr.auc","X-Api-Request-Id": task_id,"X-Api-Sequence": "-1"}# 请求体payload = {"audio": {"url": audio_url}}# 提交转写任务async with aiohttp.ClientSession() as session:async with session.post(submit_url, headers=headers, data=json.dumps(payload)) as response:if response.status != 200:error_detail = await response.text()raise ValueError(f"提交ASR任务失败: {error_detail}")response_headers = response.headersstatus_code = response_headers.get("X-Api-Status-Code")log_id = response_headers.get("X-Tt-Logid", "")if status_code not in ["20000000", "20000001", "20000002"]:raise ValueError(f"ASR任务提交错误: {response_headers.get('X-Api-Message', '未知错误')}")# 轮询查询结果max_retries = 10for i in range(max_retries):# 等待一段时间再查询await asyncio.sleep(0.5)# 查询转写结果async with aiohttp.ClientSession() as session:query_headers = {"Content-Type": "application/json","X-Api-App-Key": APP_KEY,"X-Api-Access-Key": ACCESS_KEY,"X-Api-Resource-Id": "volc.bigasr.auc","X-Api-Request-Id": task_id,"X-Tt-Logid": log_id}async with session.post(query_url, headers=query_headers,data=json.dumps({})) as response:if response.status != 200:continuequery_status_code = response.headers.get("X-Api-Status-Code")# 如果完成,返回结果if query_status_code == "20000000":try:response_data = await response.json()result = response_data.get("result", {})text = result.get("text", "")utterances = result.get("utterances", [])return {"text": text, "utterances": utterances}except Exception as e:raise ValueError(f"解析ASR响应失败: {str(e)}")# 如果仍在处理,继续等待elif query_status_code in ["20000001", "20000002"]:await asyncio.sleep(0.5)continueelse:error_message = response.headers.get("X-Api-Message", "未知错误")raise ValueError(f"ASR任务查询失败: {error_message}")# 超过最大重试次数raise ValueError("ASR转写超时,请稍后重试")

4. API接口设计

完整的API接口设计,专注于最小功能实现:

# 1. 获取上传URL
@router.post("/audio/upload-url")
async def create_upload_url(request: dict):return await audio_service.create_upload_url(request["file_name"], request["file_size"], request["mime_type"])# 2. 直接上传音频
@router.post("/audio/upload")
async def upload_audio(file: UploadFile):return await audio_service.upload_audio(file)# 3. 转写音频 (通过存储路径)
@router.post("/audio/transcribe")
async def transcribe_audio(request: dict):return await audio_service.transcribe_audio_by_storage_path(request["storage_path"])# 4. 通过URL转写音频
@router.post("/audio/transcribe-by-url")
async def transcribe_by_url(request: dict):return await audio_service.transcribe_audio_by_url(request["audio_url"])# 5. 上传并转写音频
@router.post("/audio/upload-and-transcribe")
async def upload_and_transcribe(file: UploadFile):return await audio_service.upload_and_transcribe(file)

性能与可靠性优化

在实际生产环境中,我们还应关注以下几点:

1. 大文件处理

对于大型音频文件,应当:

  • 使用分块上传方式
  • 实现断点续传
  • 限制文件大小
  • 采用预签名URL方式,避免通过API服务器中转

2. 错误处理和重试

增强系统稳定性:

  • 实现指数退避重试策略
  • 添加详细日志记录
  • 设置超时处理

3. 安全性考虑

保护用户数据:

  • 实现访问控制
  • 对音频URL设置短期有效期
  • 考虑临时文件清理机制

完整示例:构建最小可行实现

下面是一个使用FastAPI构建的基于火山引擎ASR的最小可行实现示例:

import os
import uuid
import json
import asyncio
import aiohttp
from datetime import datetime, timedelta
from fastapi import FastAPI, UploadFile, File
from typing import Dict, Any, Optionalapp = FastAPI()# 配置项
ALLOWED_AUDIO_TYPES = ["audio/mpeg", "audio/wav", "audio/mp4", "audio/x-m4a"]
APP_KEY = os.getenv("VOLCANO_ASR_APP_ID")
ACCESS_KEY = os.getenv("VOLCANO_ASR_ACCESS_TOKEN")# 简单的存储客户端模拟
class SimpleStorageClient:def upload(self, path, content):# 实际项目中应连接到S3、OSS等云存储print(f"Uploading {len(content)} bytes to {path}")return Truedef generate_presigned_url(self, path, expiry=3600, http_method="GET", **kwargs):# 简化示例,实际应返回带签名的URLreturn f"https://storage-example.com/{path}?expires={expiry}&method={http_method}"storage_client = SimpleStorageClient()# API端点
@app.post("/audio/upload-url")
async def create_upload_url(file_name: str, file_size: int, mime_type: str):"""获取上传URL"""timestamp = datetime.now().strftime("%Y%m%d%H%M%S")random_suffix = os.urandom(4).hex()file_ext = os.path.splitext(file_name)[1]filename = f"{timestamp}_{random_suffix}{file_ext}"storage_path = f"audio/{filename}"upload_url = storage_client.generate_presigned_url(storage_path,expiry=300,http_method="PUT",content_length=file_size)return {"upload_url": upload_url,"storage_path": storage_path}@app.post("/audio/upload")
async def upload_audio(file: UploadFile = File(...)):"""直接上传音频文件"""if file.content_type not in ALLOWED_AUDIO_TYPES:return {"error": "不支持的文件类型"}contents = await file.read()if len(contents) == 0:return {"error": "文件内容为空"}timestamp = datetime.now().strftime("%Y%m%d%H%M%S")random_suffix = os.urandom(4).hex()file_ext = os.path.splitext(file.filename)[1]filename = f"{timestamp}_{random_suffix}{file_ext}"storage_path = f"audio/{filename}"storage_client.upload(storage_path, contents)access_url = storage_client.generate_presigned_url(storage_path,expiry=3600,http_method="GET")return {"file_name": file.filename,"storage_path": storage_path,"file_size": len(contents),"mime_type": file.content_type,"access_url": access_url,"url_expires_at": (datetime.now() + timedelta(hours=1)).isoformat()}@app.post("/audio/transcribe")
async def transcribe_audio(storage_path: str):"""通过存储路径转写音频"""access_url = storage_client.generate_presigned_url(storage_path,expiry=3600,http_method="GET")transcript_result = await _call_volcano_asr(access_url)return {"storage_path": storage_path,"transcript": transcript_result}@app.post("/audio/transcribe-by-url")
async def transcribe_by_url(audio_url: str):"""通过URL转写音频"""transcript_result = await _call_volcano_asr(audio_url)return {"audio_url": audio_url,"transcript": transcript_result}@app.post("/audio/upload-and-transcribe")
async def upload_and_transcribe(file: UploadFile = File(...)):"""上传并转写音频文件"""upload_result = await upload_audio(file)if "error" in upload_result:return upload_resulttranscript_result = await _call_volcano_asr(upload_result["access_url"])return {**upload_result,"transcript": transcript_result}async def _call_volcano_asr(audio_url):"""调用火山引擎ASR服务"""if not APP_KEY or not ACCESS_KEY:return {"text": "火山引擎ASR配置缺失,请设置环境变量"}# 生成任务IDtask_id = str(uuid.uuid4())# 火山引擎ASR服务API端点submit_url = "https://openspeech.bytedance.com/api/v3/auc/bigmodel/submit"query_url = "https://openspeech.bytedance.com/api/v3/auc/bigmodel/query"# 提交请求头headers = {"Content-Type": "application/json","X-Api-App-Key": APP_KEY,"X-Api-Access-Key": ACCESS_KEY,"X-Api-Resource-Id": "volc.bigasr.auc","X-Api-Request-Id": task_id,"X-Api-Sequence": "-1"}# 请求体payload = {"audio": {"url": audio_url}}try:# 提交任务async with aiohttp.ClientSession() as session:async with session.post(submit_url, headers=headers, data=json.dumps(payload)) as response:status_code = response.headers.get("X-Api-Status-Code")log_id = response.headers.get("X-Tt-Logid", "")if status_code not in ["20000000", "20000001", "20000002"]:return {"error": f"提交转写任务失败: {response.headers.get('X-Api-Message', '未知错误')}"}# 查询结果max_retries = 10for i in range(max_retries):await asyncio.sleep(1)  # 等待1秒# 查询请求头query_headers = {"Content-Type": "application/json","X-Api-App-Key": APP_KEY,"X-Api-Access-Key": ACCESS_KEY,"X-Api-Resource-Id": "volc.bigasr.auc","X-Api-Request-Id": task_id,"X-Tt-Logid": log_id}async with aiohttp.ClientSession() as session:async with session.post(query_url, headers=query_headers, data="{}") as response:query_status = response.headers.get("X-Api-Status-Code")if query_status == "20000000":  # 转写完成result = await response.json()text = result.get("result", {}).get("text", "")return {"text": text}elif query_status in ["20000001", "20000002"]:  # 处理中continueelse:return {"error": f"查询转写结果失败: {response.headers.get('X-Api-Message', '未知错误')}"}return {"error": "转写超时,请稍后查询结果"}except Exception as e:return {"error": f"转写过程发生错误: {str(e)}"}if __name__ == "__main__":import uvicornuvicorn.run(app, host="0.0.0.0", port=8000)

结论

构建一个简洁的音频转写系统可以不依赖数据库,只需要专注于文件上传、获取URL和ASR转写三个核心功能。通过集成火山引擎ASR服务,我们可以快速实现高质量的语音转文本功能,无需自行构建复杂的语音识别模型。

本文的最小可行实现充分利用了火山引擎ASR的API功能,提供了一个完整的工作流程,包括文件上传、URL生成和转写调用。这种方式不仅开发效率高,而且可以在不断迭代中逐步增强功能。


进一步的拓展方向

在有了最小可行实现后,可以考虑以下拓展:

  • 添加数据库存储转写历史
  • 实现用户认证和授权
  • 支持实时语音转写
  • 多语言转写支持
  • 说话人分离功能
  • 情感分析集成
  • 关键词提取和主题识别

相关文章:

【后端】构建简洁的音频转写系统:基于火山引擎ASR实现

在当今数字化时代,语音识别技术已经成为许多应用不可或缺的一部分。无论是会议记录、语音助手还是内容字幕,将语音转化为文本的能力对提升用户体验和工作效率至关重要。本文将介绍如何构建一个简洁的音频转写系统,专注于文件上传、云存储以及…...

http通信之axios vs fecth该如何选择?

在HTTP通信中,axios和fetch都是常用的库或原生API用于发起网络请求。两者各有特点,适用于不同的场景。下面详细介绍它们的差异和各自的优势: fetch 特点: 原生支持:fetch是现代浏览器内置的API,不需要额外…...

iostat指令介绍

文章目录 1. 功能介绍2. 语法介绍3. 应用场景4. 示例分析 1. 功能介绍 iostat (input/output statistics),是 Linux/Unix 系统中用于监控 CPU 使用率和 磁盘 I/O 性能的核心工具,可实时展示设备负载、吞吐量、队列状态等关键指标。 可以使用 man iostat查…...

NLP高频面试题(五十)——大模型(LLMs)分词(Tokenizer)详解

在自然语言处理(NLP)任务中,将文本转换为模型可处理的数字序列是必不可少的一步。这一步通常称为分词(tokenization),即把原始文本拆分成一个个词元(token)。对于**大型语言模型(LLM,Large Language Model,大型语言模型)**而言,选择合适的分词方案至关重要:分词的…...

桌面我的电脑图标不见了怎么恢复 恢复方法指南

在Windows操作系统中,“我的电脑”或在较新版本中称为“此电脑”的图标,是访问硬盘驱动器、外部存储设备和系统文件的重要入口。然而,有些用户可能会发现桌面上缺少了这个图标,这可能是由于误操作、系统设置更改或是不小心删除造成…...

【Qt】控件的理解 和 基础控件 QWidget 属性详解(通俗易懂+附源码+思维导图框架)

每日激励:“不设限和自我肯定的心态:I can do all things。 — Stephen Curry” 绪论​: 通过上一章对信号槽的理解相信你对Qt的认识肯定有了很大的进步,下面将通过本篇文章带你深入的认识Widget控件(主窗口&#xff0…...

oracle将表字段逗号分隔的值进行拆分,并替换值

需求背景:需要源数据变动,需要对历史表已存的字段值根据源数据进行更新。如果是单字段存值,直接根据映射表关联修改即可。但字段里面若存的值是以逗号分割,比如旧值:‘old1,old2,old3’,要根据映射关系调整…...

用c语言实现——一个带头节点的链队列,支持用户输入交互界面、初始化、入队、出队、查找、判空判满、显示队列、遍历计算长度等功能

一、知识介绍 带头节点的链队列是一种基于链表实现的队列结构,它在链表的头部添加了一个特殊的节点,称为头节点。头节点不存储实际的数据元素,主要作用是作为链表的起点,简化队列的操作和边界条件处理。 1.节点结构 链队列的每…...

webpack基础使用了解(入口、出口、插件、加载器、优化、别名、打包模式、环境变量、代码分割等)

目录 1、webpack简介2、简单示例3、入口(entry)和输出(output)4、自动生成html文件5、打包css代码6、优化(单独提取css代码)7、优化(压缩过程)8、打包less代码9、打包图片10、搭建开发环境(webpack-dev-server&#xf…...

【项目】基于MCP+Tabelstore架构实现知识库答疑系统

基于MCPTabelstore架构实现知识库答疑系统 整体流程设计(一)Agent 架构(二)知识库存储(1)向量数据库Tablestore(2)MCP Server (三)知识库构建(1&a…...

C语言高频面试题——malloc 和 calloc区别

在 C 语言中,malloc 和 calloc 都是用于动态内存分配的函数,但它们在 内存初始化、参数形式 和 使用场景 上有显著区别。以下是详细的对比分析: 1. 函数原型 malloc void* malloc(size_t size);功能:分配 未初始化 的连续内存块…...

深入探讨JavaScript性能瓶颈与优化实战指南

JavaScript作为现代Web开发的核心语言,其性能直接影响用户体验与业务指标。随着2025年前端应用的复杂性持续增加,性能优化已成为开发者必须掌握的核心技能。本文将从性能瓶颈分析、优化策略、工具使用三个维度,结合实战案例,系统梳理JavaScript性能优化的关键路径。 一、Ja…...

[创业之路-376]:企业法务 - 创业,不同的企业形态,个人承担的风险、收益、税费、成本不同

在企业法务领域,创业时选择不同的企业形态,个人在风险承担、收益分配、税费负担及运营成本方面存在显著差异。以下从个人独资企业、合伙企业、有限责任公司、股份有限公司四种常见形态展开分析: 一、个人承担的风险 个人独资企业 风险类型&…...

【Lua】Lua 入门知识点总结

Lua 入门学习笔记 本教程旨在帮助有编程基础的学习者快速入门Lua编程语言。包括Lua中变量的声明与使用,包括全局变量和局部变量的区别,以及nil类型的概念、数值型、字符串和函数的基本操作,包括16进制表示、科学计数法、字符串连接、函数声明…...

低空经济 WebGIS 无人机配送 | 图扑数字孪生

2024 年,”低空经济” 首次写入政府工作报告,在政策驱动下各地纷纷把握政策机遇,从基建网络、场景创新、产业生态、政策激励等方面,构建 “规划-建设-应用-赋能” 的系统性布局,作为新质生产力的重要体现,推…...

【程序员 NLP 入门】词嵌入 - 如何基于计数的方法表示文本? (★小白必会版★)

🌟 嗨,你好,我是 青松 ! 🌈 希望用我的经验,让“程序猿”的AI学习之路走的更容易些,若我的经验能为你前行的道路增添一丝轻松,我将倍感荣幸!共勉~ 【程序员 NLP 入门】词…...

基于机器学习的多光谱遥感图像分类方法研究与定量评估

多光谱遥感技术通过获取可见光至红外波段的光谱信息,为地质勘探、农业监测、环境调查等领域提供了重要支持。与普通数码相机相比,多光谱成像能记录更丰富的波段数据(如近红外、短波红外等),从而更精准地识别地物特征。…...

BEVDepth: Acquisition of Reliable Depth for Multi-View 3D Object Detection

背景 基于多视角图片的3D感知被LSS证明是可行的,它使用估计的深度将图像特征转化为3D视椎,再将其压缩到BEV平面上。对于这个得到的BEV特征图,它支持端到端训练以及各种下游任务。但是对于深度估计这一块学习的深度质量如何,到目前为止没有相关工作研究。 贡献 本文的贡献…...

【Linux】静态库 动态库

🌻个人主页:路飞雪吖~ 🌠专栏:Linux 目录 一、👑静态库和动态库 静态库: 动态库: 🌠手动制作静态库 && 手动调用一下我们自己写的静态库 1> 安装到系统里面 ✨生成静…...

Java转Go日记(六):TCP黏包

服务端代码如下: // socket_stick/server/main.gofunc process(conn net.Conn) {defer conn.Close()reader : bufio.NewReader(conn)var buf [1024]bytefor {n, err : reader.Read(buf[:])if err io.EOF {break}if err ! nil {fmt.Println("read from client…...

(51单片机)LCD显示温度(DS18B20教程)(LCD1602教程)(延时函数教程)(单总线教程)

演示视频: LCD显示温度 源代码 如上图将9个文放在Keli5 中即可,然后烧录在单片机中就行了 烧录软件用的是STC-ISP,不知道怎么安装的可以去看江科大的视频: 【51单片机入门教程-2020版 程序全程纯手打 从零开始入门】https://www.…...

【通过Docker快速部署Tomcat9.0】

文章目录 前言一、部署docker二、部署Tomcat2.1 创建存储卷2.2 运行tomcat容器2.3 查看tomcat容器2.4 查看端口是否监听2.5 防火墙开放端口 三、访问Tomcat 前言 Tomcat介绍 Tomcat 是由 Apache 软件基金会(Apache Software Foundation)开发的一个开源 …...

云原生--基础篇-3--云原生概述(云、原生、云计算、核心组成、核心特点)

1、什么是云和原生 (1)、什么是云? “云”指的是云计算环境,代表应用运行的基础设施和资源。依赖并充分利用云计算的弹性、分布式和资源池化能力。 核心含义: 1、云计算基础设施 云原生应用的设计和运行完全基于云…...

Spark-Streaming

Spark-Streaming概述 DStream实操 案例一&#xff1a;WordCount案例 需求&#xff1a;使用 netcat 工具向 9999 端口不断的发送数据&#xff0c;通过 SparkStreaming 读取端口数据并统计不同单词出现的次数 实验步骤&#xff1a; 添加依赖 <dependency> <gro…...

乐视系列玩机------乐视2 x620红灯 黑砖刷写教程以及新版刷写工具的详细释义

乐视x620在上期解析了普通黑砖情况下的救砖刷机过程。但在一些例外的情况下。使用上面的步骤会一直刷写报错 。此种情况就需要另外一种强制刷写方法来救砖 通过博文了解💝💝💝 1💝💝💝-----详细解析乐视2 x620系列 红灯 黑砖线刷救砖的步骤 2💝💝💝----图…...

若依SpringCloud项目-定制微服务模块

若依SpringCloud项目-定制微服务模块 关于微服务先不过多介绍&#xff0c;刚开始熟悉并不能讲的很彻底&#xff0c;成熟的微服务项目-若依SpringCloud就是一个典型的微服务架构工程&#xff08;网上有很多教程了&#xff0c;不明白的可以学习一下&#xff09;。 我正在看的视…...

【扫描件批量改名】批量识别扫描件PDF指定区域内容,用识别的内容修改PDF文件名,基于C++和腾讯OCR的实现方案,超详细

批量识别扫描件PDF指定区域内容并重命名文件方案 应用场景 本方案适用于以下场景: 企业档案数字化管理:批量处理扫描的合同、发票等文件,按内容自动分类命名财务票据处理:自动识别票据上的关键信息(如发票号码、日期)用于归档医疗记录管理:从扫描的检查报告中提取患者I…...

学习Docker遇到的问题

目录 1、拉取hello-world镜像报错 1. 检查网络连接 排查: 2. 配置 Docker 镜像加速器(推荐) 具体解决步骤: 1.在服务器上创建并修改配置文件,添加Docker镜像加速器地址: 2. 重启Docker 3. 拉取hello-world镜像 2、删除镜像出现异常 3、 容器内部不能运行ping命令 …...

Docker 数据卷

目录 一、数据卷(Data Volume) 二、使用 1、单独建立数据卷 2、挂载主机数据卷 3、数据卷容器挂载 基本语法: 工作原理: 主要用途: 使用事例: 一、数据卷(Data Volume) 数据卷的使用,类似于 Linux 下对目录或文件进行 mount 数据卷(Data Volume)是一个可供一个或多…...

【数据结构】励志大厂版·初级(二刷复习)双链表

前引&#xff1a;今天学习的双链表属于链表结构中最复杂的一种&#xff08;带头双向循环链表&#xff09;&#xff0c;按照安排&#xff0c;我们会先进行复习&#xff0c;如何实现双链表&#xff0c;如基本的头插、头删、尾删、尾插&#xff0c;掌握每个细节&#xff0c;随后进…...

通过dogssl申请ssl免费证书

SSL证书作为实现HTTPS加密的核心工具&#xff0c;能够确保用户与网站之间的数据传输安全。尤其是在小程序之类的开发时&#xff0c;要求必须通过https发起请求的情况下。学会如何免费申请一个ssl证书就很有必要了。这里我分享一下&#xff0c;我通过dogssl如何申请ssl的。 一&…...

路由与路由器

路由的概念 路由是指在网络通讯中&#xff0c;从源设备到目标设备路径的选择过程。路由器是实现这一过程的关键设备&#xff0c;它通过转发数据包来实现网络的互联。路由工作在OSI参考模型的第三层&#xff0c;‘网络层’。 路由器的基本原理 路由器通过维护一张路由表来决定…...

Docker底层原理浅析 | namespace+cgroups+文件系统

本文目录 1. Linux NamespaceLinux系统里是否只能有一个pid为1的进程&#xff1f;namespace机制查看namespacenamespace机制测试使用Docker验证namespace机制 2. Dcoerk网络模式3.Control groups4.文件系统&#xff08;联合文件系统&#xff09;5. 容器格式 1. Linux Namespace…...

【无人机】使用扩展卡尔曼滤波 (EKF) 算法来处理传感器测量,各传感器的参数设置,高度数据融合、不同传感器融合模式

目录 #1、IMU #2、磁力计 #3、高度 #典型配置 #4、气压计 #静压位置误差修正 #气压计偏置补偿 #5、全球导航系统/全球定位系统--GNSS/GPS #位置和速度测量 #偏航测量 #GPS 速度的偏航 #双接收器 #GNSS 性能要求 #6、测距 #条件范围辅助-Conditional range aidin…...

常见的raid有哪些,使用场景是什么?

RAID&#xff08;Redundant Array of Independent Disks&#xff0c;独立磁盘冗余阵列&#xff09;是一种将多个物理硬盘组合成一个逻辑硬盘的技术&#xff0c;目的是通过数据冗余和/或并行访问提高性能、容错能力和存储容量。不同的 RAID 级别有不同的实现方式和应用场景。以下…...

《 C++ 点滴漫谈: 三十四 》从重复到泛型,C++ 函数模板的诞生之路

一、引言 在 C 编程的世界里&#xff0c;类型是一切的基础。我们为 int 写一个求最大值的函数&#xff0c;为 double 写一个相似的函数&#xff0c;为 std::string 又写一个……看似合理的行为&#xff0c;逐渐堆积成了难以维护的 “函数墙”。这些函数逻辑几乎一致&#xff0…...

EasyRTC打造无人机低延迟高清实时通信监控全场景解决方案

一、方案背景 随着无人机技术的飞速发展&#xff0c;其在航拍、物流配送、农业监测、应急救援等多个领域的应用日益广泛。然而&#xff0c;无人机在实际作业过程中面临着诸多挑战&#xff0c;如通信延迟、数据传输不稳定、监控范围有限等。EasyRTC作为一种高效、低延迟的实时通…...

【MATLAB第117期】#源码分享 | 基于MATLAB的SSM状态空间模型多元时间序列预测方法(多输入单输出)

【MATLAB第117期】#源码分享 | 基于MATLAB的SSM状态空间模型多元时间序列预测方法&#xff08;多输入单输出&#xff09; 引言 本文使用状态空间模型实现失业率递归预测&#xff0c;状态空间模型&#xff08;State Space Model, SSM&#xff09;是一种用于描述动态系统行为的…...

关于大数据的基础知识(三)——数据安全与合规

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///计算机爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于大数据的基础知识&#xff08;三&a…...

从信息泄露到内网控制

0x01 背景 之前常见用rce、文件上传等漏洞获取webshell&#xff0c;偶然遇到一次敏感信息泄露获取权限的渗透&#xff0c;简单记录一下过程。 0x02 信息泄露 发现系统某端口部署了minio服务&#xff0c;经过探测发现存在minio存储桶遍历 使用利用工具把泄露的文件全部整理一…...

【Qt】QDialog类

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;Qt 目录 一&#xff1a;&#x1f525; 对话框 - QDialog &#x1f98b; 基本介绍&#x1f98b; 对话框分类&#x1f98b; Qt 内置对话框&#x1f380; QMessageBox -消息对话框&#x1f380; QColo…...

【Spring Boot基础】MyBatis的基础操作:增删查改、列名和属性名匹配 -- XML实现

MyBatis的基础操作 1. MyBatis XML配置文件1.1 简单介绍1.2 配置连接字符串和MyBatis1.3 XMl文件实现--分层1.4 XMl文件实现--举例 2.增删改查操作2.1 增&#xff08;insert&#xff09;2.1.1 不使用Param2.1.2 用Param2.1.3 返回自增键 2.2 删&#xff08;delete&#xff09;2…...

谷歌推出探索型推荐新范式:双LLM架构重塑用户兴趣挖掘

文章目录 1. 背景1.1 闭环困境1.2 谷歌的两次失败尝试1.2.1 尝试一&#xff1a;轻量微调1.2.2 尝试二&#xff1a;RLHF 强化学习微调 1.3 双LLM范式的提出1.3.1 模型1&#xff1a;Novelty LLM — 负责生成“探索方向”1.3.2 模型2&#xff1a;Alignment LLM — 负责评估“相关性…...

Linux kernel signal原理(下)- aarch64架构sigreturn流程

一、前言 在上篇中写到了linux中signal的处理流程&#xff0c;在do_signal信号处理的流程最后&#xff0c;会通过sigreturn再次回到线程现场&#xff0c;上篇文章中介绍了在X86_64架构下的实现&#xff0c;本篇中介绍下在aarch64架构下的实现原理。 二、sigaction系统调用 #i…...

使用 LangChain + Higress + Elasticsearch 构建 RAG 应用

RAG&#xff08;Retrieval Augmented Generation&#xff0c;检索增强生成&#xff09; 是一种结合了信息检索与生成式大语言模型&#xff08;LLM&#xff09;的技术。它的核心思想是&#xff1a;在生成模型输出内容之前&#xff0c;先从外部知识库或数据源中检索相关信息&…...

【Linux】46.网络基础(3.3)

文章目录 5. 其他重要协议或技术5.1 DNS(Domain Name System)5.1.1 DNS背景5.1.2 域名简介 5.2 ICMP协议5.2.1 ICMP功能5.2.2 ICMP的报文格式5.2.3 ping命令5.2.4 一个值得注意的坑5.2.5 traceroute命令 5.3 NAT技术5.3.1 NAT技术背景5.3.2 NAT IP转换过程5.3.3 NAPT5.3.4 NAT技…...

【Unity笔记】Unity + OpenXR项目无法启动SteamVR的排查与解决全指南

图片为AI生成 一、前言 随着Unity在XR领域全面转向OpenXR标准&#xff0c;越来越多的开发者选择使用OpenXR来构建跨平台的VR应用。但在项目实际部署中发现&#xff1a;打包成的EXE程序无法正常启动SteamVR&#xff0c;或者SteamVR未能识别到该应用。本文将以“Unity OpenXR …...

【sylar-webserver】重构 增加内存池

文章目录 内存池设定结构ThreadCacheCentralCachePageCache allocatedeallocate测试 参考 https://github.com/youngyangyang04/memory-pool 我的代码实现见 https://github.com/star-cs/webserver 内存池 ThreadCache&#xff08;线程本地缓存&#xff09; 每个线程独立的内存…...

云账号安全事件分析:黑客利用RAM子账户发起ECS命令执行攻击

事件背景 某企业云监控系统触发高危告警,提示API请求中包含黑客工具特征(cf_framework),攻击者试图通过泄露的RAM子账户凭据调用ECS高危API。以下是攻击关键信息整理: 字段详情告警原因API请求包含黑客工具特征(cf_framework)攻击实体RAM子账户 mq泄露凭证AccessKey ID…...

Node.js 模块导入的基本流程

Node.js 模块导入的基本流程&#xff0c;主要是 CommonJS 模块加载机制&#xff08;即使用 require()&#xff09;的内部执行步骤。下面我用清晰的结构给你梳理一下这个过程&#xff1a; ✅ Node.js 模块导入的基本流程&#xff08;使用 require()&#xff09; const someModu…...