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

vue-h5:在h5中实现相机拍照加上身份证人相框和国徽框

方案1:排出来照片太糊了,效果不好

1.基础功能

参考:
https://blog.csdn.net/weixin_45148022/article/details/135696629

https://juejin.cn/post/7327353533618978842?searchId=20241101133433B2BB37A081FD6A02DA60

https://www.freesion.com/article/67641324321/

https://github.com/AlexKratky/vue-camera-lib

效果:在这里插入图片描述

调用组件的

主要组件方法:openCamera,closeCamera

Upload.vue组件

<template><div id="cameraContainer"><div ref="takePhotoDiv" class="take-photo" style="display: none"><video ref="video"  id="video-fix" :width="width" :height="height" autoplay   webkit-playsinline playsinline></video><div class="frame-container"><div class="mask" >
<!--                  头像页图标--><img v-if="props.currPhotoType=='head'" class="img-head" src="../assets/image/idcard1.svg">
<!--                  国徽页图标--><img v-if="props.currPhotoType=='mark'" class="img-mark" src="../assets/image/idcard2.svg"><div class="tips">请将{{props.currPhotoType=='head'?'身份证人像面':'身份证国徽面'}}完全置于取景框内</div></div></div></div>
<!--      拍照按钮--><div id="captureButton"  @click="takePhoto"><div class="cap-inner"></div></div></div><canvas ref="canvas" style="display: none"></canvas><img ref="photo" id="photo" alt="入职文件" style="display: none" /></template>
<script setup lang="ts">
import { showToast } from "vant/lib/toast";
import { nextTick, onMounted, ref,inject } from "vue";
import {base64ToBlob, base64ToFile, putFile} from "@/common/services/OSSFile.ts";
import {FileUploadType} from "@/common/enum/FileUploadType.ts";
import {ElLoading} from "element-plus";
const props=defineProps({currPhotoType:String
})
const emit=defineEmits(['okUploadImg'])
const video = ref<HTMLVideoElement | null>(null);
// const frame = ref<HTMLDivElement | null>(null);
const photo = ref<HTMLImageElement | null>(null);
const canvas = ref<HTMLCanvasElement | null>(null);
const mediaStream = ref<any>();
const takePhotoDiv = ref<HTMLDivElement | null>(null);const width=ref()
const height=ref()
onMounted(()=>{//设置摄像头宽高width.value=window.innerHeightheight.value=window.innerWidth})const getVideoMedia = () => {if (video.value) {// ----------兼容性代码------------// 老的浏览器可能根本没有实现 mediaDevices,所以我们可以先设置一个空的对象if (navigator.mediaDevices === undefined) {navigator.mediaDevices = {};}// 一些浏览器部分支持 mediaDevices。我们不能直接给对象设置 getUserMedia
// 因为这样可能会覆盖已有的属性。这里我们只会在没有 getUserMedia 属性的时候添加它。if (navigator.mediaDevices.getUserMedia === undefined) {navigator.mediaDevices.getUserMedia = function (constraints) {// 首先,如果有 getUserMedia 的话,就获得它var getUserMedia =navigator.webkitGetUserMedia || navigator.mozGetUserMedia;// 一些浏览器根本没实现它 - 那么就返回一个 error 到 promise 的 reject 来保持一个统一的接口if (!getUserMedia) {return Promise.reject(new Error("getUserMedia is not implemented in this browser"),);}// 否则,为老的 navigator.getUserMedia 方法包裹一个 Promisereturn new Promise(function (resolve, reject) {getUserMedia.call(navigator, constraints, resolve, reject);});};}// ----------兼容性代码------------// 获取用户媒体设备权限navigator.mediaDevices// 强制使用后置摄像头.getUserMedia({ video: { facingMode: { exact: "environment" } }, audio: false })//前置// .getUserMedia({ video: true, audio: false }).then((stream) => {// if (video.value) {//     video.value.srcObject = stream;//     mediaStream.value = stream;// }//兼容性写法if ("srcObject" in video.value) {video.value.srcObject = stream;} else {// 防止在新的浏览器里使用它,应为它已经不再支持了video.value.src = window.URL.createObjectURL(stream);}video.value.onloadedmetadata = function (e) {video.value.play();};}).catch((error) => {console.error("获取相机权限失败:", error);showToast('获取相机权限失败');});}
}const takePhoto = () => {nextTick(async () => {console.log(video.value)if (canvas.value && video.value && photo.value) {const context = canvas.value.getContext("2d");// 设置画布尺寸与取景框相同canvas.value.width = video.value.videoWidth;canvas.value.height = video.value.videoHeight;// 绘制取景框内的画面到画布if (context) {context.drawImage(video.value, 0, 0);// 将画布内容转为图片并显示photo.value.src = canvas.value.toDataURL();photo.value.style.display = "block";// 关闭videoconsole.log('video', video.value);video.value.pause();// 关闭摄像头mediaStream.value?.getTracks().forEach((track: any) => track.stop());video.value=null}}console.log(photo.value)// console.log(photo.value.src)   将文件流传给后台上传,下列代码根据实际情况自定let file:any=photo.value.srclet idtype=props.currPhotoType=='head'?FileUploadType.BIZ_TYPE_IDCARD2:FileUploadType.BIZ_TYPE_IDCARD1//文件名:时间戳+1000以内的随机数let  fileName=new Date().getTime()+ Math.floor(Math.random()*1000)+'.jpg'const loadingInstance = ElLoading.service({ fullscreen: true, background: 'rgba(0,0,0,0.1)', text: '请求中...' });let data = await putFile(fileName,idtype, base64ToFile(file,fileName));if(data){loadingInstance.close()sendValue({file:file,type:props.currPhotoType,url:data})showToast('上传成功!')emit('okUploadImg',{status:1})}else{loadingInstance.close()showToast('上传失败!')emit('okUploadImg',{status:2})}})
}const passValue:any = inject("getIdFile")
//3.孙组件在函数中调用爷爷传递过来的函数,并在()中传递要传递的数据
const sendValue = (file) => {passValue(file)
}
//4.调用这个函数(也可以使用点击事件等方式触发)//关闭相机
const closeCamera=()=>{// 关闭摄像头mediaStream.value?.getTracks().forEach((track: any) => track.stop());video.value=null
}
//dakai相机
const openCamera=()=>{console.log('打开相机')//打开相机if (takePhotoDiv.value) {takePhotoDiv.value.style.display = 'block'getVideoMedia()}
}defineExpose({openCamera,closeCamera
})
</script>
<style scoped lang="less"></style>
#cameraContainer {position: relative;//width: 324px;//height: 216px;width:100vw;height: 100vh;background: #000;overflow: hidden;.take-photo{//height:85.6*6px;//width: 53.98*6px;height: 70%;width: 90%;overflow: hidden;background: #000;position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%) ;}#video-fix{position: absolute;top: 50%;left: 50%;//transform: translate(-50%, -50%) rotate(90deg);transform: translate(-50%, -50%);}
}#video {object-fit: cover;}.frame-container {position: absolute;top: 0;left: 0;width: 100%;height: 100%;
}.mask {position: absolute;height:85.6*5px;width: 53.98*5px;border: 1px solid #fdfdfd;border-radius: 5px;top: 50%;left: 50%;transform: translate(-50%, -50%);.img-head{position: absolute;bottom: 4.5%;right: 13.7%;height: 28%;width: 53%;transform: rotate(90deg);}.img-mark{position: absolute;top:7%;right: 9%;width: 37%;height: 22.5%;transform: rotate(90deg);}.tips{position: absolute;left: -50%;top: 50%;color: #fff;transform: rotate(90deg);font-size: 14px;background: #555657;border-radius: 5px;}}#frame {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);width: 200px;height: 90px;z-index: 10;background-color: transparent;
}#photo {display: none;}
#captureButton{width: 100px;height: 100px;border-radius: 50%;background: #ffffff;position: absolute;bottom: 50px;left: 50%;transform: translateX(-50%);display: flex;justify-content: center;align-items: center;.cap-inner{background: #fff;width: 85%;height: 85%;border-radius: 50%;border: 3px solid #000;}
}

base64转文件流

/*** @description: Base64 转 File* @param {string} base64 base64格式的字符串* @param {string} fileName 文件名* @return {File}*/
export const base64ToFile = (base64: string, fileName: string): File => {const arr: string[] = base64.split(',');const type = (arr[0].match(/:(.*?);/) as string[])[1];const bstr = atob(arr[1]);let n = bstr.length;const u8arr = new Uint8Array(n);while (n--) {u8arr[n] = bstr.charCodeAt(n);}return new File([u8arr], fileName, { type });
};

调用组件:

<script setup lang="ts">
import {onMounted, ref} from "vue";
import Upload from "@/components/Upload.vue";const props=defineProps({currPhotoType:String
})
const _show=ref(false)
const uploadRef=ref()const goBack =()=> {// window.history.back() // 删掉van-popup打开时添加的history_show.value = false//关闭相机uploadRef.value.closeCamera()
}const openModal=()=>{_show.value=truesetTimeout(()=>{//打开相机uploadRef.value.openCamera()},500)}
onMounted(()=>{})const  okUpload=(e)=>{if(e.status==1){//上传成功,关闭弹框,关闭相机goBack()}if(e.status==2){//上传失败,关闭弹框,关闭相机goBack()}
}defineExpose({openModal
})
</script><template>
<!--全屏弹框组件--><!--  @close="selectProjectCloseHandler"   @open="selectProjectOpenHandler"--><van-popup v-model:show="_show"    :overlay="false"  position="bottom" :style="{ width: '100%', height: '100%'}"><div class="header"><van-nav-bar class="title" left-arrow title="身份证头像页上传" :safe-area-inset-top="true" :fixed="true"@click-left="goBack" /></div><div style="color: red">{{props}}</div><Upload  ref="uploadRef" @okUploadImg="okUpload" :currPhotoType="props.currPhotoType"></Upload></van-popup>
</template><style scoped lang="less"></style>

2.问题及方案

2.1 ios游览器打开video相机默认是全屏的

安卓可以正常用video打开相机,ios有问题,打开时全屏的。

在iOS端的Web控件上使用video标签播放视频时,视频会自动全屏播放。

解决方案
ios端video标签必须加webkit-playsinline、playsinline属性。

android端部分视频也会存在自动全屏问题,添加webkit-playsinline属性。

 <video ref="video"  id="video-fix" :width="width" :height="height" autoplay   webkit-playsinline playsinline></video>

2.2 拍出来的图片角度有问题

拍出来图片是顺时针旋转了90度,所以需要在canvas中给图片转正
下面是一个旋转的demo

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body>
<script type="text/javascript">function drawBeauty(beauty){var mycv = document.getElementById("cv");  var myctx = mycv.getContext("2d");myctx.translate(beauty.width / 2, beauty.height / 2);//调整这里90*3 旋转至正确角度myctx.rotate(((90+90*3) * Math.PI) / 180);myctx.drawImage(beauty, -beauty.width / 2, -beauty.height / 2);}function load(){var beauty = new Image();  //获取本题图片beauty.src = "./asset/WechatIMG134.jpg"; if(beauty.complete){drawBeauty(beauty);}else{beauty.onload = function(){drawBeauty(beauty);};beauty.onerror = function(){window.alert('美女加载失败,请重试');};};   }//loadif (document.all) {window.attachEvent('onload', load);  }else {  window.addEventListener('load', load, false);}</script><canvas id="cv"  width="600" height="300" style="border:1px solid #ccc;margin:20px auto;display: block;">当前浏览器不支持canvas<!-- 如果浏览器支持canvas,则canvas标签里的内容不会显示出来 -->
</canvas>
</body>
</html>

参考:
https://blog.csdn.net/qq_30100043/article/details/106355667
https://www.cnblogs.com/html5test/archive/2012/03/01/2375558.html
https://jelly.jd.com/article/6006b1045b6c6a01506c87e6
https://www.cnblogs.com/Joe-and-Joan/p/10957818.html

2.3 拍出来的照片默认是640*480 ,照片不清晰

简而言之:video宽高要设置成 4:3或16:9才行,这里我设置成了1280*720

<video ref="video"  id="video-fix" width="1280" height="720" autoplay   webkit-playsinline playsinline></video>
<canvas ref="canvas" style="display: none" width="1280" height="720"></canvas>
   var constraints = {audio: false,video: {width: { min: 1280, max: 1560 }, height: { min: 720, max: 1440 },facingMode: { exact: "environment" }//设置后置,注释掉就是前置}};navigator.mediaDevices.getUserMedia(constraints).then(gotStream).catch(handleError)

https://stackoverflow.com/questions/15849724/capture-high-resolution-video-image-html5

2.4 本地local能打开电脑前置,不是最终效果

2.5 部分手机打开相机默认是放大的

设置焦距:
https://www.dynamsoft.com/codepool/camera-zoom-control-on-web.html
https://github.com/webrtc/samples/blob/gh-pages/src/content/getusermedia/pan-tilt-zoom/js/main.js

方案2: 最终方案

本方案参考: https://juejin.cn/post/6955036353931247629
本文博主是用react写的,有源码但是本地启动不了,在方案1的基础上对其代码进行简单改写为vue3版本

相关文章:

vue-h5:在h5中实现相机拍照加上身份证人相框和国徽框

方案1&#xff1a;排出来照片太糊了&#xff0c;效果不好 1.基础功能 参考&#xff1a; https://blog.csdn.net/weixin_45148022/article/details/135696629 https://juejin.cn/post/7327353533618978842?searchId20241101133433B2BB37A081FD6A02DA60 https://www.freesio…...

免费HTML模板和CSS样式网站汇总

HTML模板&#xff1a;&#xff08;注意版权&#xff0c;部分不可商用&#xff09; 1、Tooplate&#xff0c;免费HTML模板下载 Download 60 Free HTML Templates for your websitesDownload 60 free HTML website templates or responsive Bootstrap templates instantly from T…...

Mac打开time machine(时间机器)备份特殊文件

Mac 打开time machine&#xff08;时间机器&#xff09;备份特殊文件 设置“时间机器”的作用具体操作办法 前言&#xff1a;今天在使用Nas同步文件时发现有部分重要文件没有同步&#xff0c;为了省事手动拖拽复制文件&#xff0c;导致其中一份非常重要的文件丢失&#xff0c;尝…...

Qt 学习第十六天:文件和事件

一、创建widget对象&#xff08;文件&#xff09; 二、设计ui界面 放一个label标签上去&#xff0c;设置成box就可以显示边框了 三、新建Mylabel类 四、提升ui界面的label标签为Mylabel 五、修改mylabel.h&#xff0c;mylabel.cpp #ifndef MYLABEL_H #define MYLABEL_H#incl…...

nvm 切换 Node.js 版本

nvm 切换 Node.js 版本 0. nvm 安装1. 查看装了哪些 Node.js 版本2. 安装 Node.js 版本安装最新稳定版本.安装个18 3. 切换 Node.js 版本4. 设置默认 Node.js 版本5. 卸载 Node.js 版本6.与项目的配合使用参考资料 0. nvm 安装 安装教程就不写了&#xff0c;直接看别人的。 脚…...

AI绘图最强软件stable diffusion,一文带你迅速了解!

有需要stable diffusion整合包可以扫描下方&#xff0c;免费获取 01 — 什么是 SD ​ Stable Difusion(简称 SD) 其三种概念。 1.用来指代稳定扩散(Stable Diffusion) 技术,如 Midjourney是基于Stable Difusion技术实现的就是指它运用了 Stable Diffusion 的技术原理。 …...

VMware重磅官宣!Workstation和Fusion彻底全部免费:支持商用

VMware 官网宣布&#xff1a;VMware Workstation Pro: Now Available Free for Personal Use 别问&#xff0c;问就是正版用户&#xff01;&#xff01;&#xff01; VMware宣布&#xff0c;其桌面虚拟化产品VMware Workstation和VMware Fusion将对所有用户彻底免费&#xff0…...

CCS 学习记录

1.导入项目 在CCS菜单中选择Project->Import Existing CCS Eclipse Project&#xff0c;点击Browse找到CCS workspace所在文件夹&#xff0c;点击OK&#xff0c;CCS会自动将所选文件夹及其子文件夹下所有的CCS Projects列出。从列表中找到所要导入的项目文件夹&#xff0c;…...

241112.学习日志——[CSDIY] Cpp零基础速成 [01]

CSDIY&#xff1a;这是一个非科班学生的努力之路&#xff0c;从今天开始这个系列会长期更新&#xff0c;&#xff08;最好做到日更&#xff09;&#xff0c;我会慢慢把自己目前对CS的努力逐一上传&#xff0c;帮助那些和我一样有着梦想的玩家取得胜利&#xff01;&#xff01;&…...

94.【C语言】数据结构之双向链表的初始化,尾插,打印和尾删

目录 1.双向链表 2.结构体的定义 3.示意图 3.代码示例 1.双向链表的尾插 示意图 代码 main.c List.h List.c 详细分析代码的执行过程 双向链表的初始化 2.双向链表的打印 代码 3.双向链表的尾删 1.双向链表 以一种典型的双向链表为例:带头双向循环链表(带头:带…...

learn-F12 Performance(性能)前端性能分析(LCP,CLS,INP)

1.前言 在浏览器开发者工具&#xff08;F12&#xff09;中&#xff0c;本地指标&#xff08;Local Metrics&#xff09;包括LCP&#xff08; Largest Contentful Paint&#xff09;、CLS&#xff08; Cumulative Layout Shift&#xff09;和INP&#xff08; Interaction to Nex…...

XCZU47DR-2FSVE1156

XCZU47DR-2FSVE1156 芯片概述 XCZU47DR-2FSVE1156 是一款由 Xilinx 公司生产的 Zynq UltraScale™ RFSoC 芯片。该芯片集成了多种高性能组件&#xff0c;包括四核 ARM Cortex-A53 MPCore™ 和双核 ARM Cortex™-R5&#xff0c;提供了强大的计算能力和灵活性。它还具备丰富的连…...

物联网低功耗广域网LoRa开发(一):LoRa物联网行业解决方案

一、LoRa的优势以及与其他无线通信技术对比 &#xff08;一&#xff09;LoRa的优势 1、164dB链路预算 、距离>15km 2、快速、灵活的基础设施易组网且投资成本较少 3、LoRa节点模块仅用于通讯电池寿命长达10年 4、免牌照的频段 网关/路由器建设和运营 、节点/终端成本低…...

【LeetCode】【算法】23. 合并K个升序链表

LeetCode 23. 合并K个升序链表 题目描述 给你一个链表数组&#xff0c;每个链表都已经按升序排列。 请你将所有链表合并到一个升序链表中&#xff0c;返回合并后的链表。 思路 思路&#xff1a;用小根堆解&#xff0c;很强 创建一个小根堆&#xff0c;排序规则为小根堆排序…...

python3的基本数据类型:Dictionary(字典)的创建

一. 简介 本文开始简单学习一下 python3中的一种基本数据类型&#xff1a;Dictionary&#xff08;字典&#xff09;。 字典&#xff08;dictionary&#xff09;是Python中另一个非常有用的内置数据类型。 二. python3的基本数据类型&#xff1a;Dictionary&#xff08;字典&…...

【C++】string模拟实现

各位读者老爷好&#xff0c;俺最近在学习string的一些知识。为了更好的了解string的结构&#xff0c;俺模拟实现了一个丐版string&#xff0c;有兴趣的老爷不妨垂阅&#xff01;&#xff01;&#xff01; 目录 1.string类的定义 2.模拟实现成员函数接口 2.1.constructor&am…...

Springboot 使用EasyExcel导出含图片并设置样式的Excel文件

Springboot 使用EasyExcel导出含图片并设置样式的Excel文件 Excel导出系列目录&#xff1a;★★★★尤其注意&#xff1a;引入依赖创建导出模板类逻辑处理controllerservice 导出效果总结 Excel导出系列目录&#xff1a; 【Springboot 使用EasyExcel导出Excel文件】 【Springb…...

技术分享:《越南语翻译通》App高效学习越南语的智能助手,是怎么实现高精度语音识别翻译功能的呢?

在数字化时代&#xff0c;语言学习和跨文化交流变得日益重要。对于那些计划前往越南工作、旅游或学习的人来说&#xff0c;掌握越南语无疑是一个巨大的优势。然而&#xff0c;对于非越南语母语者来说&#xff0c;语言障碍可能会成为一大难题。幸运的是&#xff0c;《越南语翻译…...

工业互联网实验实训解决方案核心优势

工业互联网实验实训解决方案旨在通过模拟真实的工业环境&#xff0c;提供给学生或从业人员一个实践学习的平台&#xff0c;它结合了理论教学与实际操作&#xff0c;旨在培养具备工业互联网相关技能的专业人才。 工业互联网实验室必备的软件工具包括&#xff1a; 仿…...

Ceph client 写入osd 数据的两种方式librbd 和kernel rbd

在Ceph存储系统中&#xff0c;客户端&#xff08;Ceph client&#xff09;写入OSD&#xff08;Object Storage Daemon&#xff09;数据确实可以通过两种主要方式&#xff1a;librbd和kernel rbd。这两种方式各有特点和适用场景&#xff0c;下面将分别进行详细介绍。 librbd方式…...

工业防爆监控技术解析与选型方案 —— 以吉林高危场景为例

一、引言在石油化工、矿山开采、海上作业等工业高危场景中&#xff0c;环境存在易燃易爆、高腐蚀、高粉尘、极端温度等特性&#xff0c;普通安防设备无法满足安全要求。防爆监控作为场景安全防控的核心终端&#xff0c;其技术设计、硬件选型、功能配置直接决定高危场景的风险防…...

抖音小店搜索排名规则及优化方法

一、抖音商城搜索排名规则1.商品相关性:商品标题、关键词与用户搜索词的匹配程度是重要因素。精准匹配的商品会在搜索结果中更靠前展示。例如&#xff0c;用户搜索"夏季连衣裙”&#xff0c;标题中明确包含该关键词且商品属性也相符的连衣裙&#xff0c;会优先被展示。商品…...

OBS直播教程:OBS多路推流在哪里设置?如何安装?OBS多路推流教程

OBS直播教程&#xff1a;OBS多路推流在哪里设置&#xff1f;如何安装&#xff1f;OBS多路推流教程 具体如何下载&#xff1f;如何安装&#xff1f;如何使用&#xff1f;我写了一个保姆级教程&#xff0c;请往下看&#xff0c;步骤很详细的&#xff0c;你一定看得懂 第一步&…...

SSE流式响应:从Reactor Flux到生产级AI聊天的工程实践——5分钟超时、线程隔离、背压处理全解析

大家好&#xff0c;我是程序员小策。 首先给大家去一个例子&#xff1a;凌晨两点&#xff0c;P0 告警炸了。 AI 聊天接口全部超时&#xff0c;用户消息发出去转圈转了 120 秒然后报错。你打开监控一看&#xff1a;Tomcat 线程池满了&#xff0c;200 个工作线程全部卡在"…...

CV产线MLOps平台:图像原生处理与硬件感知交付

1. 项目概述&#xff1a;这不是又一个“模型训练平台”&#xff0c;而是一套能真正跑通CV产线的MLOps工作流“Streamline Your Computer Vision Stack with an End-to-End MLOps Platform”——这个标题里藏着三个被太多团队长期忽视的关键事实&#xff1a;第一&#xff0c;“C…...

AI动态认知地图:从Llama 4传闻到MCIP验证的闭环实践

1. 这不是一份普通 newsletter&#xff1a;它是一张AI领域的动态认知地图“This AI newsletter is all you need #91”——光看标题&#xff0c;你可能以为这只是又一份堆砌链接的AI资讯合集。但作为连续追踪该系列超过两年、亲手拆解过前87期原始内容、并用其指导过6个真实AI产…...

通过Taotoken的CLI工具一键配置开发环境与API密钥

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 通过Taotoken的CLI工具一键配置开发环境与API密钥 对于需要接入多个大模型服务的开发团队而言&#xff0c;统一管理API密钥和端点配…...

终极MQTT客户端快速入门指南:5分钟掌握跨平台物联网通信

终极MQTT客户端快速入门指南&#xff1a;5分钟掌握跨平台物联网通信 【免费下载链接】mqttclient A high-performance, high-stability, cross-platform MQTT client, developed based on the socket API, can be used on embedded devices (FreeRTOS / LiteOS / RT-Thread / T…...

在不同网络环境下测试Taotoken API端点的连接稳定性与路由表现

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 在不同网络环境下测试Taotoken API端点的连接稳定性与路由表现 对于依赖大模型API进行开发的团队而言&#xff0c;服务的连接稳定性…...

3步快速清理Windows驱动存储:DriverStore Explorer终极使用指南

3步快速清理Windows驱动存储&#xff1a;DriverStore Explorer终极使用指南 【免费下载链接】DriverStoreExplorer Driver Store Explorer 项目地址: https://gitcode.com/gh_mirrors/dr/DriverStoreExplorer 你是否发现Windows系统盘空间不断减少&#xff0c;却找不到原…...