使用Node.js分片上传大文件到阿里云OSS
阿里云OSS的分片上传(Multipart Upload)是一种针对大文件优化的上传方式,其核心流程和关键特性如下:
1. 核心流程
分片上传分为三个步骤:
- 初始化任务:调用InitiateMultipartUpload接口创建上传任务,获取唯一标识(Upload ID)。
- 上传分片:通过UploadPart接口并发上传切分后的文件块(Part),每个Part需指定序号(partNumber),但上传顺序不影响最终合并。
- 合并文件:调用CompleteMultipartUpload接口将所有Part按序号合并为完整文件。若中途放弃上传,需调用AbortMultipartUpload清理未完成的Part。
2. 适用场景
- 大文件加速上传:文件超过5GB时,分片上传通过并行传输显著提升速度。
- 网络环境波动:单个Part失败仅需重传该部分,避免全文件重传。
- 暂停与恢复:任务无过期时间,可随时中断后继续上传。
- 未知文件大小:如视频监控等场景,可在文件生成过程中开始上传。
3. 技术细节与建议
- 分片大小:官方建议文件大于100MB时采用分片上传,过小分片可能导致进度显示异常。
- 并发控制:并非并发数越多越快,需权衡网络带宽和设备负载。
- 安全实践:浏览器端使用时应通过临时访问凭证(STS)授权,避免暴露主账号密钥。
- 跨域配置:浏览器直接上传需预先设置OSS的CORS规则。
4. 操作方式
- 工具支持:OSS控制台不支持分片上传,需使用ossbrowser(图形化工具)、ossutil(命令行)或SDK(如Browser.js、阿里云SDK)。
- 代码示例:Vue项目中可通过ali-oss SDK实现分片上传,结合进度条组件(如Element UI)提升用户体验。
5. 注意事项
- 碎片清理:未完成的分片会占用存储空间,需手动调用AbortMultipartUpload或配置生命周期规则自动清理。
- 费用影响:未清理的分片会产生存储费用,每月费用 = 分片大小 × 分片数量 × 存储单价 × 存储天数。始终对放弃的上传调用
abortMultipartUpload - 自动清理机制:OSS 默认 7 天后清理未完成的分片。可通过生命周期规则缩短:
xml:
<LifecycleConfiguration><Rule><AbortIncompleteMultipartUpload><DaysAfterInitiation>1</DaysAfterInitiation> <!-- 1天后清理 --></AbortIncompleteMultipartUpload></Rule>
</LifecycleConfiguration>
- API灵活性:若需高度定制化,可直接调用REST API(需自行计算签名)。
一:阿里官方文档:Node.js用一个步骤实现分片上传
二:阿里官方文档:Node.js分三个步骤实现分片上传,可扩展暂停/继续上传和取消上传操作
一个步骤实现:
以上分片上传完整示例调用的方法multipartUpload中封装了初始化分片上传、上传分片以及完成分片上传三个API接口。如果您希望分步骤实现分片上传,请依次调用.initMultipartUpload、.uploadPart以及.completeMultipartUpload方法。
<template><div class="upload-container"><el-upload ref="upload" drag action="" :auto-upload="false" :on-change="handleFileChange" :limit="1" :on-exceed="handleExceed"><i class="el-icon-upload"></i><div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div></el-upload><el-button style="margin-top: 20px;" type="primary" @click="uploadFile" :disabled="!isupload">开始上传</el-button><el-progress v-if="uploading" :text-inside="true" :stroke-width="26" :percentage="uploadPercentage"></el-progress></div>
</template><script>
import OSS from 'ali-oss'; // npm install ali-oss
import md5 from 'js-md5'; // npm install js-md5
export default {data() {return {file: null,uploading: false,isupload: false,uploadPercentage: 0,};},methods: {handleFileChange(file) {this.file = file.raw;this.isupload = true;},handleExceed() {this.$message.warning('只能上传一个文件');},async uploadFile() {if (!this.file) {this.$message.error('请先选择文件');return;}this.uploading = true;try {let content = this.setConfig(this.$ossConfig);//这里涉及身份识别,我这里是取接口返回在解密获得(如上图),下同,不再解释console.log(content)const client = new OSS({region: content.region,bucket: content.bucket,accessKeyId: content.accessKeyId,accessKeySecret: content.accessKeySecret,stsToken: content.securityToken,});const fileName = `uploads/${this.file.name}`;const options = {partSize: 1024 * 1024 * 5, // 分片大小为 5MBparallel: 3, // 并发上传的分片数量progress: (p, cpt, res) => {this.uploadPercentage = Math.round(p * 100);},};const result = await client.multipartUpload(fileName, this.file, options);this.$message.success('文件上传成功');console.log('Upload result:', result);} catch (error) {this.$message.error('文件上传失败');console.error('Upload error:', error);} finally {this.uploading = false;this.uploadPercentage = 0;}},setConfig(content) {let Base64 = require('js-base64').Base64let str1 = Base64.decode(content.substr(1, 3) + content.substr(5, 3) + content.substr(9, content.length - 9));let contentN = Base64.decode(str1.substr(1, 3) + str1.substr(5, 3) + str1.substr(9, str1.length - 9));return JSON.parse(contentN);},},
};
</script><style scoped>
.upload-container {max-width: 600px;margin: 20px auto;padding: 20px;border: 1px solid #ebeef5;border-radius: 4px;
}
</style>
效果:
扩展:一个步骤扩展暂停/继续上传、取消上传
<template><div class="upload-container"><!-- <el-upload ref="uploadRef" class="upload-demo" drag :action="''" :auto-upload="false" :show-file-list="false" accept="*" :limit="2" :on-change="handleChange" :on-exceed="handleExceed"> --><el-upload ref="uploadRef" class="upload-demo" drag :action="''" :auto-upload="true" :show-file-list="false" accept="*" :limit="1" :before-upload="handleChange"><i class="el-icon-upload"></i><div class="el-upload__text">将文件拖到此处,或<em>点击上传3</em></div></el-upload><div v-if="file" class="file-info"><div>文件名称: {{ file.name }}</div><div>文件大小: {{ formatFileSize(file.size) }}</div><div>分片大小: {{ (chunkSize / 1024 / 1024).toFixed(2) }} MB</div><div>分片数量: {{ chunks }}</div></div><el-progress v-if="showProgress && !isUploaded" :percentage="percentage" :status="uploadStatus" :stroke-width="16" style="margin: 20px 0"></el-progress><el-button v-if="isUploaded" type="text">上传成功</el-button><div v-if="showButtons"><!-- <el-button type="primary" @click="submitForm" v-if="!isUploading && !isUploaded && !tempCheckpoint">开始上传</el-button> --><el-button type="primary" icon="el-icon-refresh-left" @click="submitForm" v-if="!isUploading && !isUploaded && !tempCheckpoint">重试</el-button><el-button type="text" @click="shareclearn" v-if="isUploaded">分享 | 清除记录</el-button><el-button type="primary" icon="el-icon-video-pause" @click="pauseUpload()" v-if="paused && isUploading && !isUploaded">暂停上传</el-button><el-button type="primary" icon="el-icon-video-play" @click="resumeUpload()" v-if="!paused && !isUploaded">继续上传</el-button><el-button type="danger" icon="el-icon-circle-close" @click="cancelUpload" v-if="!isUploaded && tempCheckpoint">取消上传</el-button></div></div>
</template><script>
import OSS from 'ali-oss'
import md5 from 'js-md5'export default {data() {return {client: null,parallel: 3, // 并发上传的分片数量chunkSize: 2 * 1024 * 1024, // 分片大小 2MBchunks: 0,// 总分片数量file: null,ossKey: '',uploadStatus: null, // 进度条上传状态percentage: 0, // 进度条百分比showProgress: false,// 是否显示进度条showButtons: false,// 是否显示按钮tempCheckpoint: null, // 用于缓存当前切片内容uploadId: '',isUploading: false,// 是否上传中isUploaded: false,//是否上传完毕paused: true,//是否暂停};},created() {this.initOSSClient();},methods: {async initOSSClient() {try {let content = this.setConfig(this.$ossConfig);console.log(content)this.client = new OSS({region: content.region,bucket: content.bucket,accessKeyId: content.accessKeyId,accessKeySecret: content.accessKeySecret,stsToken: content.securityToken,});} catch (error) {console.error('OSS初始化失败:', error);this.$message.error('上传服务初始化失败');}},setConfig(content) {let Base64 = require('js-base64').Base64let str1 = Base64.decode(content.substr(1, 3) + content.substr(5, 3) + content.substr(9, content.length - 9));let contentN = Base64.decode(str1.substr(1, 3) + str1.substr(5, 3) + str1.substr(9, str1.length - 9));return JSON.parse(contentN);},// 选择文件async handleChange(file) {// console.log(file)this.resetState();this.file = file//.raw;this.ossKey = `uploads/${Date.now()}_${this.file.name}`;this.chunks = Math.ceil(this.file.size / this.chunkSize);this.showButtons = true;this.submitForm();},// 开始上传submitForm() {this.initMultipartUpload();this.multipartUpload();},// 初始化分片上传async initMultipartUpload() {const result = await this.client.initMultipartUpload(this.ossKey);this.uploadId = result.uploadId;console.log(`初始化分片上传成功, uploadId: ${this.uploadId}`);},// 开始分片上传async multipartUpload() {if (!this.file) {this.$message.error('请选择文件');return;}this.uploadStatus = null;this.percentage = 0;this.showProgress = true;this.isUploading = true;try {const result = await this.client.multipartUpload(this.file.name, this.file, {progress: (p, checkpoint) => {this.tempCheckpoint = checkpoint;// this.uploadId = checkpoint.uploadId;this.percentage = Math.floor(p * 100);},parallel: this.parallel,partSize: this.chunkSize,});this.uploadStatus = 'success';this.isUploading = false;this.isUploaded = true;this.$message.success('上传成功');console.log(result, '切片上传完毕');} catch (e) {if (e.code === 'ConnectionTimeoutError') {this.uploadStatus = 'exception';window.addEventListener('online', this.resumeUpload);}this.isUploading = false;console.error(e);}},// 取消上传cancelUpload() {this.pauseUpload();this.paused = true;this.showProgress = false;this.percentage = 0;this.uploadStatus = null;this.tempCheckpoint = null;this.uploadId = '';},// 暂停上传pauseUpload() {this.isUploading = false;this.paused = false;this.client.cancel();},// 继续上传async resumeUpload() {if (!this.tempCheckpoint) {this.$message.error('请先上传');return;}this.uploadStatus = null;this.isUploading = true;this.paused = true;try {const result = await this.client.multipartUpload(this.file.name, this.file, {progress: (p) => {this.percentage = Math.floor(p * 100);},checkpoint: this.tempCheckpoint,});this.uploadStatus = 'success';this.isUploading = false;this.isUploaded = true;this.$message.success('续传成功');console.log(result, '恢复上传完毕');} catch (e) {this.isUploading = false;console.error(e);}},// 重置resetState() {this.file = null;this.ossKey = '';this.uploadStatus = null;this.percentage = 0;this.showProgress = false;this.showButtons = false;this.tempCheckpoint = null;this.uploadId = '';this.isUploading = false;this.isUploaded = false;this.paused = true;},// 限制上传一个文件,重新选择文件替换原来的文件handleExceed(files) {// console.log(this.$refs.uploadRef)if (this.$refs.uploadRef.uploadFiles.length > 1) {this.$refs.uploadRef.uploadFiles.shift();}},// 格式化文件大小formatFileSize(size) {const units = ['B', 'KB', 'MB', 'GB'];let unitIndex = 0;while (size >= 1024 && unitIndex < units.length - 1) {size /= 1024;unitIndex++;}return `${size.toFixed(2)} ${units[unitIndex]}`;},shareclearn() {console.log('分享 | 清除记录')},},
};
</script><style scoped>
.upload-container {width: 100%;max-width: 600px;margin: 0 auto;
}.el-button {margin-right: 10px;
}.file-info {margin: 20px 0;padding: 15px;background-color: #f8f9fa;border-radius: 4px;
}.file-info div {margin-bottom: 10px;color: #606266;
}
</style>
效果:
一个步骤分片上传(扩展暂停继续取消)
三个步骤实现:
<template><div class="upload-container"><!-- multiple drag on-change --><el-upload class="upload-demo" action="" :show-file-list="false" :auto-upload="true" :before-upload="handleFileChange" :accept="'*'"><!-- <i class="el-icon-upload"></i><div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div> --><el-button size="small" type="primary">选择文件</el-button></el-upload><div class="file-info" v-if="file"><div>文件名: {{ file.name }}</div><div>文件大小: {{ formatFileSize(file.size) }}</div><div>分片大小: {{ (chunkSize / 1024 / 1024).toFixed(2) }} MB</div><div>分片数量: {{ chunks }}</div><el-progress v-if="isUploading" :percentage="progressPercent" :status="uploadStatus" :stroke-width="14"></el-progress><div><el-button type="primary" :icon="isPaused?'el-icon-video-play':'el-icon-video-pause'" @click="togglePause" v-if="isUploading">{{ isPaused ? '继续' : '暂停' }}</el-button><el-button type="danger" icon="el-icon-circle-close" @click="cancelUpload" v-if="isUploading">取消</el-button><el-button type="primary" icon="el-icon-refresh-left" @click="startUpload" v-if="!isUploading && !uploaded">重试</el-button><el-button type="text" v-if="!isUploading && uploaded">分享 | 清除记录</el-button></div></div></div>
</template><script>
import OSS from 'ali-oss'
import md5 from 'js-md5'export default {data() {return {file: null,chunkSize: 2 * 1024 * 1024, // 5MB 分片chunks: 0, // 分片数量uploadedChunks: [], // 已上传的分片索引uploadId: '', // OSS 分片上传 IDclient: null, // OSS 客户端isPaused: false, // 是否暂停isUploading: false, // 上传中uploaded: false, // 上传完成progressPercent: 0, // 进度条-百分比uploadStatus: null, // 进度条-状态concurrency: 3, // 并发数activeChunks: 0, // 当前活跃的分片数cancelTokens: {}, // 取消令牌parts: [], // 分片列表};},created() {this.initOSSClient();},methods: {async initOSSClient() {// 这里应该从后端获取临时凭证(安全考虑)// 以下是模拟数据,实际使用需替换为真实接口const credentials = await this.getSTSToken();this.client = new OSS({region: credentials.region,accessKeyId: credentials.accessKeyId,accessKeySecret: credentials.accessKeySecret,stsToken: credentials.securityToken,bucket: credentials.bucket,refreshSTSToken: async () => {const newCredentials = await this.getSTSToken();return {accessKeyId: newCredentials.accessKeyId,accessKeySecret: newCredentials.accessKeySecret,stsToken: newCredentials.securityToken};}});},async getSTSToken() {// 实际项目中从这里获取STS token// 示例返回结构:var content = this.setConfig(this.$ossConfig);console.log(content)return {region: content.region,bucket: content.bucket,accessKeyId: content.accessKeyId,accessKeySecret: content.accessKeySecret,securityToken: content.securityToken,};},setConfig(content) {let Base64 = require('js-base64').Base64let str1 = Base64.decode(content.substr(1, 3) + content.substr(5, 3) + content.substr(9, content.length - 9));let contentN = Base64.decode(str1.substr(1, 3) + str1.substr(5, 3) + str1.substr(9, str1.length - 9));return JSON.parse(contentN);},// 格式化文件大小formatFileSize(size) {const units = ['B', 'KB', 'MB', 'GB'];let unitIndex = 0;while (size >= 1024 && unitIndex < units.length - 1) {size /= 1024;unitIndex++;}return `${size.toFixed(2)} ${units[unitIndex]}`;},// 标准化ETag格式normalizeETag(etag) {let cleanETag = String(etag).replace(/^"+/, '').replace(/"+$/, '').replace(/\\/g, '');if (!/^[0-9A-F]{32}$/i.test(cleanETag)) {throw new Error(`无效的ETag格式: ${etag}`);}return `"${cleanETag}"`;},// 重置上传resetState() {this.uploadedChunks = [];this.uploadId = '';this.isPaused = false;this.isUploading = false;this.uploaded = false;this.progressPercent = 0;this.uploadStatus = null;this.activeChunks = 0;this.cancelTokens = {};// // 取消所有进行中的请求// Object.values(this.cancelTokens).forEach(ctrl => ctrl.abort());// this.cancelTokens = {};// // 强制更新视图// this.$nextTick();},handleFileChange(file) {// console.log(file)this.resetState();this.file = file;//file.raw;this.chunks = Math.ceil(this.file.size / this.chunkSize);this.startUpload();},// 1、初始化分片上传async startUpload() {if (!this.file) return;try {this.isUploading = true;this.isPaused = false;if (!this.uploadId) {const result = await this.client.initMultipartUpload(this.file.name,{ timeout: 60000 });this.uploadId = result.uploadId;console.log(`初始化分片上传成功, uploadId: ${this.uploadId}`);}// 获取未上传的分片const unuploaded = Array.from({ length: this.chunks }, (_, i) => i).filter(i => !this.uploadedChunks.includes(i));// 并发控制const uploadNext = async () => {if (this.isPaused || !this.isUploading) return;// 上传完,合并分片if (unuploaded.length === 0 && this.activeChunks === 0) {await this.completeUpload();return;}if (this.activeChunks < this.concurrency && unuploaded.length > 0) {const chunkIndex = unuploaded.shift();this.activeChunks++;// 取消步骤一:创建取消令牌,取消正在进行的 HTTP 请求(分片上传请求)const controller = new AbortController();this.cancelTokens[chunkIndex] = controller;// 2、调用上传分片-sthis.uploadChunk(chunkIndex, controller.signal).then((res) => {// console.log(res)// console.log(chunkIndex)// this.parts.push({ partNumber: chunkIndex + 1, etag: res.etag });this.uploadedChunks.push(chunkIndex);this.updateProgress();}).catch(err => {if (err.name !== 'AbortError') {console.error(`分片 ${chunkIndex} 上传失败:`, err);}}).finally(() => {this.activeChunks--;delete this.cancelTokens[chunkIndex];uploadNext();});// 调用上传分片-euploadNext();}};// 启动并发上传for (let i = 0; i < this.concurrency; i++) {uploadNext();}} catch (err) {console.error('上传初始化失败:', err);this.$message.error('上传初始化失败');this.resetState();}},// 2、分片上传async uploadChunk(index, signal) {const start = index * this.chunkSize;const end = Math.min(start + this.chunkSize, this.file.size);const chunk = this.file.slice(start, end);return this.client.uploadPart(this.file.name,this.uploadId,index + 1,chunk,signal,);},// 3、完成上传async completeUpload() {try {// 获取已上传分片列表const listParts = await this.client.listParts(this.file.name,this.uploadId,);// console.log(listParts)// 按分片号排序const sortedParts = listParts.parts.sort((a, b) => a.partNumber - b.partNumber);// console.log(sortedParts)this.parts = await sortedParts.map(p => ({number: Number(p.PartNumber),//!!!注意!!!键名只能用numberetag: this.normalizeETag(p.ETag),//!!!注意!!!键名只能用etag}))// console.log(this.parts)// 完成上传const completeResult = await this.client.completeMultipartUpload(this.file.name,this.uploadId,this.parts,);console.log(completeResult)this.$message.success('完成上传-成功');this.uploadStatus = 'success';this.uploaded = true;this.isUploading = false;} catch (err) {this.uploadStatus = 'exception';console.error('完成上传-失败:', err);this.$message.error('完成上传-失败');}},// 暂停/继续 上传togglePause() {this.isPaused = !this.isPaused;if (!this.isPaused) {this.startUpload();}},// 取消上传 cancelUpload() {this.isUploading = false;// 取消所有正在上传的分片Object.values(this.cancelTokens).forEach(controller => {controller.abort();});// 取消步骤二:清理OSS上传记录,清理掉OSS上存储的所有已经上传的分片,终止本次分片上传过程,并删除所有已上传的分片数据。// 这样确保不会在 OSS 上留下无效的分片数据,避免产生不必要的存储费用。if (this.uploadId && this.file) {try {this.client.abortMultipartUpload(this.file.name, this.uploadId).finally(() => {this.resetState();}).catch((err) => {console.error('取消上传失败:', err)});} catch (err) {console.error('OSS清理异常:', err);}}this.$message.info('上传已取消');// 使用Vue的强制更新视图(针对Element UI进度条)this.$nextTick(() => {this.$forceUpdate();});},// 更新进度条updateProgress() {// 如果不在上传状态,不更新进度if (!this.isUploading) return;const percent = Math.round((this.uploadedChunks.length / this.chunks) * 100);this.progressPercent = Math.min(percent, 100);},}
};
</script><style scoped>
.upload-container {max-width: 600px;margin: 20px auto;padding: 20px;border: 1px solid #ebeef5;border-radius: 4px;
}
.upload-demo {width: 300px;margin: 0 auto;
}
.file-info {margin-top: 20px;padding: 15px;background-color: #f8f9fa;border-radius: 4px;
}
.file-info div {margin-bottom: 10px;color: #606266;
}
.el-progress {margin-top: 20px;
}
</style>
效果:
三个步骤分片上传
相关文章:

使用Node.js分片上传大文件到阿里云OSS
阿里云OSS的分片上传(Multipart Upload)是一种针对大文件优化的上传方式,其核心流程和关键特性如下: 1. 核心流程 分片上传分为三个步骤: 初始化任务:调用InitiateMultipartUpload接口创建上传任务…...
高性能分布式消息队列系统(四)
八、客户端模块的实现 客户端实现的总体框架 在 RabbitMQ 中,应用层提供消息服务的核心实体是 信道(Channel)。 用户想要与消息队列服务器交互时,通常不会直接操作底层的 TCP 连接,而是通过信道来进行各种消息的发布…...
C#异步编程:从线程到Task的进化之路
一、没有异步编程之前的时候 在异步编程出现之前,程序主要采用同步编程模型。这种模型下,所有操作按顺序执行,当一个操作(如I/O读写、网络请求)阻塞时,整个程序会被挂起,导致资源利用率低和响应延迟高。具体问题包括: 阻塞执行:同步代码在执行耗时操作时(如文件读取…...
[论文阅读] 人工智能+软件工程 | 用大模型优化软件性能
用大模型优化软件性能?这篇论文让代码跑出新速度! arXiv:2506.01249 SysLLMatic: Large Language Models are Software System Optimizers Huiyun Peng, Arjun Gupte, Ryan Hasler, Nicholas John Eliopoulos, Chien-Chou Ho, Rishi Mantri, Leo Deng, K…...

复变函数中的对数函数及其MATLAB演示
复变函数中的对数函数及其MATLAB演示 引言 在实变函数中,对数函数 ln x \ln x lnx定义在正实数集上,是一个相对简单的概念。然而,当我们进入复变函数领域时,对数函数展现出更加丰富和复杂的性质。本文将介绍复变函数中对数函…...

【Linux】Linux程序地址基础
参考博客:https://blog.csdn.net/sjsjnsjnn/article/details/125533127 一、地址空间的阐述 1.1 程序地址空间 下面的图片展示了程序地址空间的组成结构 我们通过代码来验证一下 int g_unval; int g_val 100;int main(int argc, char *argv[]);void test1() {i…...
React 项目初始化与搭建指南
React 项目初始化有多种方式,可以选择已有的脚手架工具快速创建项目,也可以自定义项目结构并使用构建工具实现项目的构建打包流程。 1. 脚手架方案 1.1. Vite 通过 Vite 创建 React 项目非常简单,只需一行命令即可完成。Vite 的工程初始化…...

将图形可视化工具的 Python 脚本打包为 Windows 应用程序
前文我们已经写了一个基于python的tkinter库和matplotlib库的图形可视化工具。 基于Python的tkinter库的图形可视化工具(15种图形的完整代码):基于Python的tkinter库的图形可视化工具(15种图形的完整代码)-CSDN博客 在前文基础上&…...
AWS DocumentDB vs MongoDB:数据库的技术抉择
随着非关系型数据库在现代应用中的广泛应用,文档型数据库因其灵活的结构与出色的扩展性,逐渐成为企业开发与架构设计中的核心选择。在众多文档数据库中,MongoDB 凭借其成熟生态与社区支持占据主导地位;与此同时,AWS 提…...

无人机军用与民用技术对比分析
一、材料区别 军用无人机: 1. 高强度特种材料: 大量使用钛合金、碳纤维复合材料,兼顾轻量化与高强度,提升抗冲击性和隐身性能。 关键部件依赖进口材料。 2. 隐身涂层: 采用雷达吸波材料和低红外特征涂料…...

刷leetcode hot100--矩阵6/1
1.螺旋矩阵【很久】6/1【感觉就是思路的搬运工,没完全理解】 54. 螺旋矩阵 - 力扣(LeetCode) 原来想 但是如果是奇数矩阵,遍历不到中间 解决思路: 用left,right,top,down标记/限定每次遍历的元素,每次从…...
Qt 中实现文本截断(ellipsis)的功能。Qt 提供了此方法来处理过长的文本显示问题,例如在界面中限制文本长度并添加省略号(...)
QElidedText 并不是 Qt 中的标准类名或功能名称,但根据你的描述,你可能是指 QFontMetrics::elidedText() 方法。这是一个用于在 Qt 中实现文本截断(ellipsis)的功能。Qt 提供了此方法来处理过长的文本显示问题,例如在界…...
Cisco IOS XE WLC 任意文件上传漏洞复现(CVE-2025-20188)
免责申明: 本文所描述的漏洞及其复现步骤仅供网络安全研究与教育目的使用。任何人不得将本文提供的信息用于非法目的或未经授权的系统测试。作者不对任何由于使用本文信息而导致的直接或间接损害承担责任。如涉及侵权,请及时与我们联系,我们将尽快处理并删除相关内容。 前…...
基于ResNet残差网络优化梯度下降算法实现图像分类
文章目录 题 目: 基于ResNet残差网络优化梯度下降算法实现图像分类基于ResNet残差神经网络优化梯度下降算法实现海贼王图像分类引言1.ResNet残差神经网络介绍1.1 ResNet残差神经网络的研究现状1.2 ResNet残差神经网络的原理1.3 ResNet残差神经网络的实现步骤1.3.1导入必要的库…...
群晖NAS套件历史版本资源
有时候需要下载历史的群晖套件,可以通过以下地址前往 Synology Archive Download Site - Index of /download 该页面汇集了各类群晖应用程序的过往版本,方便用户根据需要选择特定版本的软件进行下载安装。这种方式适用于需要旧版软件兼容性或进行版本回…...

Docker轻松搭建Neo4j+APOC环境
Docker轻松搭建Neo4jAPOC环境 一、简介二、Docker部署neo4j三、Docker安装APOC插件四、删除数据库/切换数据库 一、简介 Neo4j 是一款高性能的 原生图数据库,采用 属性图模型 存储数据,支持 Cypher查询语言,适用于复杂关系数据的存储和分析。…...

定制开发开源AI智能名片S2B2C商城小程序在无界零售中的应用与行业智能升级示范研究
摘要:本文聚焦无界零售背景下京东从零售产品提供者向零售基础设施提供者的转变,探讨定制开发开源AI智能名片S2B2C商城小程序在这一转变中的应用。通过分析该小程序在商业运营成本降低、效率提升、用户体验优化等方面的作用,以及其与京东AI和冯…...
CppCon 2015 学习:CLANG/C2 for Windows
Visual Studio 2015 引入了基于 CLANG/LLVM 的新代码生成器,及其背景和意义。简单理解如下: 理解要点: VS2015 中引入了全新的代码生成技术,性能和质量都很棒。这套新技术基于 Clang,微软展示了相关新工具。Clang 和…...
Spring中@Primary注解的作用与使用
在 Spring 框架中,Primary 注解用于解决依赖注入时的歧义性(Ambiguity)问题。当 Spring 容器中存在多个相同类型的 Bean 时,通过 Primary 标记其中一个 Bean 作为默认的首选注入对象。 核心作用: 解决多个同类型 Bean …...
Spring Boot + Elasticsearch + HBase 构建海量数据搜索系统
Spring Boot Elasticsearch HBase 构建海量数据搜索系统 📖 目录 1. 系统需求分析2. 系统架构设计3. Elasticsearch 与 HBase 集成方案4. Spring Boot 项目实现5. 大规模搜索系统最佳实践 项目概述 本文档提供了基于 Spring Boot、Elasticsearch 和 HBase 构建…...
[zynq] Zynq Linux 环境下 AXI BRAM 控制器驱动方法详解(代码示例)
Zynq Linux 环境下 AXI BRAM 控制器驱动方法详解 文章目录 Zynq Linux 环境下 AXI BRAM 控制器驱动方法详解1. UIO (Userspace I/O) 驱动方法完整示例代码 2. /dev/mem 直接内存映射方法完整示例代码 3. 自定义字符设备驱动方法完整示例代码 4. 方法对比总结5. 实战建议 在 Zyn…...

【大模型:知识图谱】--5.neo4j数据库管理(cypher语法2)
目录 1.节点语法 1.1.CREATE--创建节点 1.2.MATCH--查询节点 1.3.RETURN--返回节点 1.4.WHERE--过滤节点 2.关系语法 2.1.创建关系 2.2.查询关系 3.删除语法 3.1.DELETE 删除 3.2.REMOVE 删除 4.功能补充 4.1.SET (添加属性) 4.2.NULL 值 …...
六、数据库的安全性
六、数据库的安全性 数据库的安全问题 数据库中的数据是可以共享的数据共享必然带来数据库的安全性问题 数据库系统中的数据共享不能是无条件的共享数据库中数据的共享是在 DBMS 统一的严格控制之下的共享,即:只允许有合法使用权限的用户访其被授权的数…...

贪心算法应用:装箱问题(BFD算法)详解
贪心算法应用:装箱问题(BFD算法)详解 1. 装箱问题与BFD算法概述 1.1 装箱问题定义 装箱问题(Bin Packing Problem)是组合优化中的经典问题,其定义为: 给定n个物品,每个物品有大小wᵢ (0 < wᵢ ≤ C)无限数量的箱子…...
C#学习第27天:时间和日期的处理
时间和日期的核心概念 1. UTC 和 本地时间 UTC(Coordinated Universal Time): 是一种不受时区影响的世界标准时间。在网络通信和全球协作中,用于统一时间度量 本地时间(Local Time): 是根据所…...

编程技能:格式化打印05,格式控制符
专栏导航 本节文章分别属于《Win32 学习笔记》和《MFC 学习笔记》两个专栏,故划分为两个专栏导航。读者可以自行选择前往哪个专栏。 (一)WIn32 专栏导航 上一篇:编程技能:格式化打印04,sprintf 回到目录…...

MPLAB X IDE 软件安装与卸载
1、下载MPLAB X IDE V6.25 MPLAB X IDE | Microchip Technology 正常选Windows,点击Download,等待自动下载完成; MPLAB X IDE 一台电脑上可以安装多个版本; 2、安装MPLAB X IDE V6.25 右键以管理员运行;next; 勾选 I a…...

windows编程实现文件拷贝
项目源码链接: 实现文件拷贝功能(限制5GB大小) 81c57de 周不才/cpp_linux study - Gitee.com 知识准备: 1.句柄 句柄是一个用于标识和引用系统资源(如文件、窗口、进程、线程、位图等)的值。它不是资…...

[6-01-01].第12节:字节码文件内容 - 属性表集合
JVM学习大纲 二、属性表集合: 2.1.属性计数器: 2.2.属性表: 2.3.字节码文件组成5 -> 属性: 1.属性主要指的是类的属性,比如源码的文件名、内部类的列表等 2.4.字节码文件组成3 -> 字段: 1.字段中…...

基于机器学习的水量智能调度研究
摘要:随着城市化进程的加速和水资源供需矛盾的日益突出,传统的水量调度模式因缺乏精准预测和动态调控能力,难以满足现代供水系统对高效性、稳定性和节能性的要求。本文针对供水系统中用水峰谷预测不准确、能耗高、供需失衡等核心问题…...