前端 大文件分片下載上傳

前端 大文件分片下載上傳

背景介紹:

當前項目是給投行部門做系統,業務方需要有專門的文檔中心去管理文件,包括但是不限于文件的上傳和下載等等。筆者本來就是采用的瀏覽器表單上傳的方式進行文件上傳,但是誰曾想在進行稍微大一點的文件上傳時,因為正式環境nginx 限制了請求體大小導致文件傳不上去,所以不得不重新調整文件上傳的策略。

解決方案:

采用分片上傳

設計思路:

1. 文件選擇與校驗

  1. 用戶通過前端界面選擇文件,觸發 fileChange方法
  2. 校驗文件合法性:

? 檢查文件大小是否超過配置閾值(例如默認限制 500MB),超限則提示警告

? 驗證文件擴展名是否在黑名單(如 exesh等),禁止上傳危險類型文件

  1. 若校驗失敗,終止流程并提示用戶。
this.fileChange = (options: any) => {const file = options.file;// 1.1 校驗文件大小if (file.size > Number(this.constants.UPLOAD_SIZE) * 1024 * 1024) {ElMessage.warning(`${`文件大小限制${this.constants.UPLOAD_SIZE}`}m`)return}// 1.2 校驗文件類型const fileName = file.name.split('.')const fileSuffix = fileName[fileName.length - 1]if (this.prohibitArr.indexOf(fileSuffix.toLowerCase()) !== -1) {ElMessage.warning('文件類型限制')return}
}

2. 任務初始化與切片生成

創建唯一任務標識

通過文件名稱、大小生成 MD5 哈希值作為 `taskId`,確保任務唯一性

文件切片

調用 `sliceFile`方法,按配置的切片大小(如默認 1MB)將文件分割為多個 `Blob`對象記錄切片總數、文件總大小等元數據。

初始化任務對象

存儲切片列表(`todoList`)、任務狀態(`isPause`)、進度值(`progressValue`)等
// 2.1 創建任務對象
this.addTask = (data) => {const taskId = `${hex_md5(`${data.fileName}_${data.fileSize}`)}`const task = {taskId, // 文件哈希IDfileName: data.fileName,fileSize: data.fileSize,todoList: [],   // 待上傳分片errorList: [],  // 失敗分片flag: 0,        // 當前分片指針progressValue: 0}this.tasksList.push(task)
}// 2.2 文件切片處理
this.sliceFile = (file) => {const piece = 1024 * 1000 * Number(this.constants.UPLOAD_CHUNK_SIZE)const chunks = []let start = 0while (start < file.size) {const blob = file.slice(start, start + piece)chunks.push(blob)start += piece}return chunks
}

3. 切片上傳與進度管理

構建分片請求:

? 為每個切片創建 FormData,包含切片內容、文件名、索引、任務 ID 等元數據

? 設置請求頭(如 Authorization攜帶用戶令牌)。

并發控制與斷點續傳

? 通過遞歸調用 handleUpload逐個上傳切片(非并行),但支持從斷點索引(task.flag)繼續上傳

? 上傳前檢查 isPause狀態,支持用戶暫停/繼續操作

實時進度計算

? 基于切片索引和當前切片上傳進度,綜合計算整體進度(避免進度條回退)

? 通過 $emit('changeProgressValue')通知前端更新進度條

this.handleChunksUpload = (chunks, taskId) => {const task = this.getTask(taskId)chunks.forEach((chunk, index) => {const fd = new FormData()fd.append('file', chunk)             // 分片二進制fd.append('fileName', task.fileName) // 原文件名fd.append('chunk', index.toString()) // 分片序號fd.append('taskId', taskId)          // 任務IDtask.todoList.push({index,fd,config: {headers: { Authorization: `Bearer ${token}` },onUploadProgress: (progress) => { /* 進度處理 */ }}})})
}

4. 錯誤處理與重試機制

失敗切片處理:

? 若某切片上傳失敗,將其加入 errorList并繼續上傳后續切片

自動重試:

? 當所有切片上傳完成但 errorList非空時,觸發重試(最多 UPLOAD_TRY_TIME次)

? 超過重試次數則觸發 handleUploadError事件,通知上傳失敗

this.handleUpload = (taskId) => {const task = this.getTask(taskId)const item = task.todoList[task.flag]axios.post(this.chunkUploadUrl, item.fd, item.config).then(() => {// 4.1 上傳成功處理task.flag++ // 移動分片指針task.progressValue = Math.round((task.flag / task.todoList.length) * 100)this.$emit('changeProgressValue', { task })// 4.2 遞歸上傳下一分片if (task.flag < task.todoList.length) {this.handleUpload(taskId)}}).catch((err) => {// 4.3 失敗分片處理task.errorList.push(item)  // 加入失敗列表task.flag++ // 繼續后續分片// 4.4 自動重試機制if (task.flag >= task.todoList.length) {if (task.errorList.length > 0) {task.todoList = [...task.errorList] // 準備重試task.errorList = []task.flag = 0this.handleUpload(taskId) // 重啟失敗分片}} else {this.handleUpload(taskId) // 繼續后續分片}})
}

5.合并切片與完成回調

后端自動合并

? 所有切片成功上傳后,前端無需顯式請求合并(與部分方案不同),由后端根據 taskId自動合并切片

前端完成邏輯

? 設置進度為 100%,觸發最終進度更新事件。

? 重置任務狀態(清空 flagerrorList)。

? 執行用戶定義的 onSuccess回調,傳遞文件及響應數據。

? 觸發 handleUploadSuccess事件,通知上傳完成

onUploadProgress: (progress) => {// 5.1 當前分片進度比const currentChunkProgress = progress.loaded / progress.total// 5.2 已完成分片的比例const completedRatio = task.flag / totalChunks// 5.3 整體進度合成(避免進度回退)const overallProgress = (completedRatio + (1 / totalChunks) * currentChunkProgress) * 100// 5.4 進度更新(前端顯示)task.progressValue = Math.min(Math.max(task.progressValue, Math.round(overallProgress)), 99)
}

6.任務清理

? 用戶可通過 removeTask主動移除任務,釋放內存資源。

? 任務隊列(tasksList)管理所有進行中的任務,支持多文件并行上傳

// 6.1 全部分片成功上傳
if (task.flag > task.todoList.length - 1 && task.errorList.length === 0) {task.progressValue = 100 // 最終進度this.$emit('changeProgressValue', { task })// 6.2 執行成功回調if (this.onSuccess) {this.onSuccess(response, task.file) }// 6.3 觸發完成事件this.$emit('handleUploadSuccess', { task })// 6.4 重置任務狀態task.flag = 0task.lastFileLoaded = 0
}

完整的代碼如下:

useChunkUpload.ts

import { ElMessage } from 'element-plus'
import { hex_md5 } from './md5'const useChunkUpload = function (props: any, emit: any, instance: any) {this.isSendErrorMessage = falsethis.tryTime = 0this.flag = 0this.fileLoaded = 0this.lastFileLoaded = 0 // 上一次加載大小this.progressValue = 0 // 進度值this.persistentFailureTime = 0 // 連續失敗次數this.isPause = falsethis.taskType = 'upload'this.taskId = ''this.tasksList = []this.prohibitArr = ['exe', 'c', 'java', 'jar', 'py', 'php', 'sh', 'bat']this.uploadInputDisabled = falsethis.$emit = emitthis.maxPersistentFailureTime = 8 // 最大連續失敗次數this.onUploadProgress = nullthis.chunkUploadUrl = 'file/resumeUpload'this.chunkUploadProgress = ''this.constants = {UPLOAD_SIZE: 500,UPLOAD_CHUNK_SIZE: 1,DOWNLOAD_CHUNK_SIZE: 1,UPLOAD_TRY_TIME: 3,DOWNLOAD_TRY_TIME: 3,TIMEOUT: 60000,}this.onSuccess = null // 添加成功回調屬性if (props.chunkUploadProps) {this.chunkUploadUrl =props.chunkUploadProps.chunkUploadUrl ?? this.chunkUploadUrlthis.chunkUploadProgress =props.chunkUploadProps.chunkUploadProgress ?? this.chunkUploadProgressif (props.chunkUploadProps.constants) {this.constants = {...this.constants,...props.chunkUploadProps.constants,}}if (props.chunkUploadProps.prohibitArr) {this.prohibitArr = [...this.prohibitArr,...props.chunkUploadProps.prohibitArr,]}if (props.onSuccess) {this.onSuccess = props.onSuccess}}this.getTasksList = () => {return (this.tasksList = [])}this.initTasksList = () => {this.tasksList = []}this.clearTasksList = () => {this.tasksList = []}this.addTask = (data) => {const taskId = `${hex_md5(`${data.fileName}_${data.fileSize}`)}`const task = {taskId, // 任務idtaskType: this.taskType, // 任務類型md5: data.md5 || '',blobs: [],todoList: [], // 待辦分片列表errorList: [], // 失敗列表tryTime: 0, // 失敗嘗試次數flag: 0, // 指針isSendErrorMessage: false, // 是否已發送錯誤信息isPause: false, // 暫停狀態handleUpload: null, // 下載方法fileName: data.fileName, // 文件名fileSize: data.fileSize, // 文件大小file: data.file,fileLoaded: 0, // 當前加載大小lastFileLoaded: 0, // 上一次加載大小progressValue: 0, // 進度值persistentFailureTime: 0, // 連續失敗次數}// 任務去重const sameTaskIndex = this.tasksList.findIndex((item) => {return item.taskId === taskId})if (sameTaskIndex === -1) {this.tasksList.push(task)} else {this.tasksList[sameTaskIndex] = task}const taskIndex = this.tasksList.length - 1this.tasksList[taskIndex]['taskIndex'] = taskIndexthis.$emit('changeCurrentTaskIndex', taskIndex, this)this.$emit('changeCurrentTaskId', task.taskId, this)this.$emit('changeCurrentTask', task, this)return task}this.getTask = (taskId) => {return this.tasksList.find((item) => item.taskId === taskId)}this.getTaskIndex = (taskId) => {return this.tasksList.findIndex((task) => task.taskId === taskId)}this.pauseTask = (taskId) => {const task = this.getTask(taskId)const taskIndex = this.getTaskIndex(taskId)if (task) {this.tasksList[taskIndex]['isPause'] = true}}this.continueTask = (taskId) => {const task = this.getTask(taskId)const taskIndex = this.getTaskIndex(taskId)if (task) {this.tasksList[taskIndex]['isPause'] = falsethis.handleUpload(taskId)}}this.removeTask = (taskId) => {const task = this.getTask(taskId)const taskIndex = this.getTaskIndex(taskId)if (task) {this.tasksList[taskIndex]['isPause'] = truethis.tasksList.splice(taskIndex, 1)}}this.handleChunksUpload = (chunks, taskId) => {const task = this.getTask(taskId)if (chunks.length === 0) {ElMessage.warning('文件太小')return null}// 計算每個分片的大小const chunkSize = chunks[0].sizeconst totalChunks = chunks.lengthconsole.log(`文件分片信息: 總大小=${task.fileSize}, 分片數=${totalChunks}, 每片大小=${chunkSize}`)const config = {headers: {contentType: false,processData: false,'X-Chunk-Upload': 'true', // 標記這是分片上傳請求},config: {timeout: this.constants.TIMEOUT,// 指示該請求的錯誤將由組件內部處理,不需要全局錯誤攔截器顯示錯誤skipGlobalErrorHandler: true,onUploadProgress: (progress) => {if (this.onUploadProgress) {this.onUploadProgress(progress, taskId)} else {// 修復進度計算邏輯console.log(`當前分片進度: loaded=${progress.loaded}, total=${progress.total}, 當前分片=${task.flag}, 總分片數=${totalChunks}`)// 當前分片的進度百分比 (0-1之間)const currentChunkProgress = progress.loaded / progress.total// 計算已完成部分的比例 (當前分片之前的所有完成分片)// 注意: 分片索引是從0開始,所以已完成的分片是task.flagconst completedChunksProgress = task.flag / totalChunks// 當前分片貢獻的總體進度比例const currentChunkContribution = (1 / totalChunks) * currentChunkProgress// 總體進度 = 已完成分片的進度 + 當前分片的貢獻const overallProgress = (completedChunksProgress + currentChunkContribution) * 100// 確保進度只會增加不會減少const previousProgress = task.progressValue || 0const newProgress = Math.min(Math.max(previousProgress, Math.round(overallProgress)), 99) // 保留最后1%給最終合并操作// 只有當進度有實質性變化時才更新if (newProgress > previousProgress) {task.progressValue = newProgress}console.log(`計算進度: 已完成分片=${task.flag}, 總分片數=${totalChunks}, 當前分片進度=${Math.round(currentChunkProgress * 100)}%, 總體進度=${task.progressValue}%`)// 觸發進度更新事件this.$emit('changeProgressValue',{task,progressValue: task.progressValue,},this)// 如果有外部的進度回調函數,也調用它if (task.onProgress && typeof task.onProgress === 'function') {task.onProgress({loaded: task.flag * chunkSize + progress.loaded,total: task.fileSize}, task.file)}}},},}if (localStorage.getItem('token') !== null &&localStorage.getItem('token') !== '') {const Authorization = `Bearer ${localStorage.getItem('token') || ''}`config.headers['Authorization'] = Authorization}chunks.forEach((chunk, index) => {const fd = new FormData()fd.append('file', chunk)fd.append('fileName', task.fileName)fd.append('chunk', index.toString())fd.append('taskId', taskId)fd.append('size', chunk.size)fd.append('totalSize', task.fileSize)fd.append('chunkTotal', chunks.length)const item = {index: index.toString(),fd,config,}task.todoList.push(item)})this.$emit('beforeHandleUpload',{task,},this)if (this.chunkUploadProgress) {instance.appContext.config.globalProperties?.$http.post(this.chunkUploadProgress, {taskId,}).then((res) => {if (res && res.data) {const startChunkIndex = res.data.chunkconst task = this.getTask(taskId)if (startChunkIndex) {task.flag = Number(startChunkIndex)// 更新已完成的進度task.progressValue = Math.round((task.flag / totalChunks) * 100)}this.handleUpload(taskId)}})} else {this.handleUpload(taskId)}}this.handleUpload = (taskId) => {const task = this.getTask(taskId)this.$emit('handleUpload', { task }, this)const item = task.todoList[task.flag]if (task.isPause) {return null}console.log(`開始上傳分片 ${task.flag + 1}/${task.todoList.length}`)instance.appContext.config.globalProperties?.$http.post(this.chunkUploadUrl, item.fd, item.config).then((res) => {const token = res.response.headers['authorization']if (token) {localStorage.setItem('token', token)}task.persistentFailureTime = 0task.flag += 1console.log(`分片 ${task.flag}/${task.todoList.length} 上傳完成`)// 更新進度:當前分片完成// 確保進度只會增加不會減少,避免UI跳動const newProgress = Math.round((task.flag / task.todoList.length) * 100)task.progressValue = Math.max(task.progressValue || 0, newProgress)// 立即更新UI進度this.$emit('changeProgressValue',{task,progressValue: task.progressValue,},this)// 任務暫停if (task.isPause) returnif (task.flag > task.todoList.length - 1) {// 所有分片上傳完成if (task.errorList.length === 0) {// 錯誤列表為空,上傳成功task.progressValue = 100 // 確保最終進度為100%// 最后一次更新進度this.$emit('changeProgressValue',{task,progressValue: task.progressValue,},this)console.log('所有分片上傳完成,調用成功回調')// 重置任務狀態task.flag = 0task.lastFileLoaded = 0task.fileLoaded = 0// 調用成功回調if (this.onSuccess && typeof this.onSuccess === 'function') {this.onSuccess(res, task.file)}// 發出成功事件this.$emit('handleUploadSuccess', { task, response: res }, this)} else {// 有錯誤,需要重試if (task.tryTime >= this.constants.UPLOAD_TRY_TIME) {// 超過重試次數,上傳失敗if (!task.isSendErrorMessage) {this.isSendErrorMessage = truethis.$emit('handleUploadError', { task }, this)}return null} else {// 重試task.tryTime = task.tryTime + 1task.todoList = task.errorListtask.errorList = []task.flag = 0this.$emit('handleBeforeUploadNextChunk', { task }, this)this.handleUpload(task.taskId)}}} else {// 繼續上傳下一個分片this.$emit('handleBeforeUploadNextChunk', { task }, this)this.handleUpload(task.taskId)}}).catch((err) => {console.error(`分片 ${task.flag + 1} 上傳失敗:`, err)// 將失敗的分片添加到錯誤列表task.errorList.push(item)task.flag += 1// 繼續處理下一個分片或重試if (task.flag > task.todoList.length - 1) {if (task.tryTime >= this.constants.UPLOAD_TRY_TIME) {this.$emit('handleUploadError', { task }, this)} else {task.tryTime += 1task.todoList = task.errorListtask.errorList = []task.flag = 0this.handleUpload(task.taskId)}} else {this.handleUpload(task.taskId)}})}this.fileChange = (options: any) => {this.$emit('beforeUploadFile', options, this)// 如果options中傳入了onSuccess,則使用它if (options.onSuccess && typeof options.onSuccess === 'function') {this.onSuccess = options.onSuccess}// 保存進度回調函數if (options.onProgress && typeof options.onProgress === 'function') {this.onProgress = options.onProgress}const file = options.fileif (file) {if (file.size > Number(this.constants.UPLOAD_SIZE) * 1024 * 1024) {ElMessage.warning(`${`文件大小限制${this.constants.UPLOAD_SIZE}`}m`)return}const fileName = file.name.split('.')if (fileName.length) {const fileSuffix = fileName[fileName.length - 1]if (this.prohibitArr.indexOf(fileSuffix.toLowerCase()) !== -1) {ElMessage.warning('文件類型限制')return}}const data = {fileName: file.name,fileSize: file.size,file,}const task = this.addTask(data)// 將回調函數保存到任務中if (options.onProgress) {task.onProgress = options.onProgress}const chunks = this.sliceFile(file)this.handleChunksUpload(chunks, task.taskId)}}this.sliceFile = (file,piece = 1024 * 1000 * Number(this.constants.UPLOAD_CHUNK_SIZE)) => {const totalSize = file.size // 文件總大小let start = 0 // 每次上傳的開始字節let end = start + piece // 每次上傳的結尾字節const chunks = []while (start < totalSize) {// 根據長度截取每次需要上傳的數據// File對象繼承自Blob對象,因此包含slice方法const blob = file.slice(start, end)chunks.push(blob)start = endend = start + piece}return chunks}this.clearInputFile = (f) => {// f = f || this.$refs.uploadif (f.value) {try {f.value = '' // for IE11, latest Chrome/Firefox/Opera...// eslint-disable-next-line no-empty} catch (err) {}if (f.value) {// for IE5 ~ IE10const form = document.createElement('form')const ref = f.nextSiblingform.appendChild(f)form.reset()ref.parentNode.insertBefore(f, ref)}}}this.createTaskId = (file, time) => {return `${time}-${file.size}-${file.name.length > 100? file.name.substring(file.name.length - 100, file.name.length): file.name}`}this.randomString = (len) => {len = len || 32const $chars ='ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678' /** **默認去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/const maxPos = $chars.lengthlet pwd = ''for (let i = 0; i < len; i++) {pwd += $chars.charAt(Math.floor(Math.random() * maxPos))}return pwd}
}export { useChunkUpload }

使用方式如下:

import { useChunkUpload } from "@/utils/useChunkUpload";...
// 初始化分片上傳
const chunkUpload = new useChunkUpload(
{headers: { Authorization: token },data: { blockName: "uploadTest" },
},
emit,
instance,
);
......
// 大文件分片上傳處理
const handleChunkUpload = (options) => {
const file = options.file;// 調用分片上傳
const uploadPromise = chunkUpload.fileChange(options);// 存儲上傳任務,用于暫停/恢復
uploadTasks.set(file.uid, uploadPromise);// 返回 abort 對象,符合 el-upload 的要求
return {abort() {console.log("分片上傳已中止");// 中止上傳邏輯const task = uploadTasks.get(file.uid);if (task && task.abort) {task.abort();}},
};
};...

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/96038.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/96038.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/96038.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Python練習】097. 編寫一個函數,實現簡單的版本控制工具

097. 編寫一個函數,實現簡單的版本控制工具 097. 編寫一個函數,實現簡單的版本控制工具 示例代碼 功能說明 使用方法 注意事項 實現方法 基于文件快照的實現方法 基于差異存儲的實現方法 基于命令模式的實現方法 基于Git-like的實現方法 097. 編寫一個函數,實現簡單的版本控…

嵌入式硬件篇---Tof

TOF 的原理與本質TOF&#xff08;Time of Flight&#xff0c;飛行時間&#xff09;是一種通過測量信號&#xff08;通常是光&#xff09;在空間中傳播時間來計算距離的技術。其本質是利用 “距離 速度 時間” 的物理公式&#xff1a;通過發射信號&#xff08;如激光、紅外光&…

Vue diff簡介

Vue3 diff 最長遞增子序列雙端diff 理念 相同的前置和后置元素的預處理&#xff0c;考慮邊界情況&#xff0c;減少移動&#xff1b;最長遞增子序列&#xff08;動態規劃、二分法&#xff09;&#xff0c;判斷是否需要移動 操作 前置與后置預處理判斷是否需要移動 遞增法&#x…

羅技MX Anywhere 2S鼠標修復記錄

【現象】單擊時總是出現雙擊的現象 【問題原因】從網絡收集&#xff1a; 說法1&#xff0c;歐姆龍微動損壞&#xff1b;說法2&#xff0c;按鍵磨損導致按鍵和微動開關接觸不良&#xff1b; 【問題排查】 微動損壞&#xff1a;拆掉殼子后&#xff0c;用手按住左鍵的微動開關&…

常見IP模塊的仲裁策略和實現

在一個 Message Unit 中包含兩個 Core&#xff08;處理器核心&#xff09;&#xff0c;它們之間訪問共享資源&#xff08;如寄存器、FIFO、buffer 等&#xff09;時&#xff0c;仲裁機制&#xff08;Arbitration&#xff09; 是確保系統穩定性和正確性的關鍵。以下是常見的仲裁…

上周60+TRO案件,波比的游戲時間/丹迪世界/飛盤/迪奧/多輪維權,手表/汽車品牌持續發力,垃圾桶專利等新增侵權風險!

賽貝整理上周&#xff08;2025年8月11日-8月15日&#xff09;的TRO訴訟案件發案情況&#xff0c;根據賽貝TRO案件查詢系統了解到&#xff0c;上周合計發起了超60起訴訟案件&#xff0c;涵蓋 IP /品牌、版權、專利等多個領域&#xff0c;涉及影視、奢侈品、汽車、游戲、日常用品…

用 Python 在 30 分鐘內搭一個「可回放的實時日志」——把攻擊流量變成可視化劇情

業務背景 我們運營一款 FPS 端游&#xff0c;外掛作者常把 DDoS 偽裝成「玩家掉線」來騙客服。以前排查要撈 CDN 日志、對時間戳、人工比對&#xff0c;平均 2 小時才能定位。現在用一條 30 行的 Python 腳本把邊緣節點日志實時打到 Kafka&#xff0c;再回放到 Grafana&#xf…

如何將 LM Studio 與 ONLYOFFICE 結合使用,實現安全的本地 AI 文檔編輯

人工智能正在改變我們的工作方式——但如今大多數 AI 工具都存在弊端&#xff1a;速度和便利性雖有所提升&#xff0c;但也意味著數據需要發送到外部服務器。對于教育工作者、企業、非政府組織以及任何處理敏感信息的人來說&#xff0c;這都是不可接受的風險。 LM Studio 和 O…

超市電商銷售分析項目:從數據分析到業務決策

國際超市電商銷售數據分析實戰&#xff1a;從數據清洗到業務決策的完整流程 在電商行業&#xff0c;數據是驅動業務增長的核心引擎。本文將以國際超市電商銷售數據為研究對象&#xff0c;完整拆解從數據準備 → 深度分析 → 策略輸出的實戰流程&#xff0c;涵蓋數據清洗、多維度…

GitHub 熱榜項目 - 日榜(2025-08-17)

GitHub 熱榜項目 - 日榜(2025-08-17) 生成于&#xff1a;2025-08-17 統計摘要 共發現熱門項目&#xff1a;12 個 榜單類型&#xff1a;日榜 本期熱點趨勢總結 本期GitHub熱榜呈現三大技術趨勢&#xff1a;1) AI基礎設施持續爆發&#xff0c;Archon OS和Parlant聚焦AI任務管…

記憶翻牌游戲 greenfoot 開發

記憶翻牌游戲是一種經典的益智游戲&#xff0c;玩家需要翻開卡片并記住它們的位置&#xff0c;然后找到所有匹配的卡片對。 核心玩法 游戲開始時&#xff0c;所有卡片都是背面朝上玩家每次可以翻開兩張卡片如果兩張卡片圖案相同&#xff0c;則保持翻開狀態&#xff08;匹配成功…

【lucene】SegmentInfos

SegmentInfos 類中文說明 ———————————— **一句話** SegmentInfos 是 segments_N 文件的**內存表示**。它把磁盤上的 segments_N 讀進來&#xff0c;變成一堆 SegmentInfo 的集合&#xff1b;當你增刪改索引、合并段、提交時&#xff0c;再把它寫回磁盤&#x…

Read Frog:一款開源AI瀏覽器語言學習擴展

Read Frog&#xff1a;一款開源AI瀏覽器語言學習擴展 來源&#xff1a;Poixe AI Read Frog&#xff08;中文名&#xff1a;陪讀蛙&#xff09;是一款開源的瀏覽器擴展&#xff0c;旨在通過人工智能技術&#xff0c;將常規網頁瀏覽轉化為一種沉浸式的語言學習體驗。該工具通過…

天地圖應用篇:增加全屏、圖層選擇功能

天地圖應用篇&#xff1a;增加全屏、圖層選擇功能本節說明&#xff1a; 目的&#xff1a; 實現地圖的圖層切換全屏顯示 / 退出全屏案例截圖 示下&#xff1a;案例代碼示例代碼&#xff1a; <template><div class"tianditu-map-container"><!-- 頂部搜…

從零開始,系統學習AI與機器學習:一份真誠的學習路線圖

人工智能&#xff08;AI&#xff09;和機器學習&#xff08;ML&#xff09;正在深刻改變眾多行業的面貌&#xff0c;掌握這些技術已成為許多技術從業者提升競爭力的重要路徑。無論你是希望進入這個充滿潛力的領域&#xff0c;還是期望在現有技術基礎上進行拓展&#xff0c;一份…

NVIDIA CWE 2025 上海直擊:從 GPU 集群到 NeMo 2.0,企業 AI 智能化的加速引擎

前言 8 月 8 日&#xff0c;我受邀參加了在上海舉辦的 NVIDIA CWE 大會。作為一個正在企業內部推動 AI 落地的從業者&#xff0c;這場會議對我來說不僅是“充電”&#xff0c;更像是一場“解題會”。參會感受 在分享干貨之前&#xff0c;我先談談這次參會的不同感受。給我感受特…

Web攻防-大模型應用LLM安全提示詞注入不安全輸出代碼注入直接間接數據投毒

知識點&#xff1a; 1、WEB攻防-LLM安全-API接口安全&代碼注入 2、WEB攻防-LLM安全-提示詞注入&不安全輸出 Web LLM&#xff08;Large Language Model&#xff09;攻擊指針對部署在Web端的AI大語言模型的攻擊行為。攻擊者通過惡意提示詞注入、訓練數據竊取、模型逆向工…

docker compose再阿里云上無法使用的問題

最原始的Dokcerfile # 使用官方Python 3.6.8鏡像 FROM python:3.6.8-slimWORKDIR /app# 復制依賴文件 COPY requirements.txt .RUN pip install --upgrade pip # 檢查并安裝依賴&#xff08;自動處理未安裝的包&#xff09; RUN pip install --no-cache-dir -r requirements.tx…

C++STL容器List的模擬實現

一、引言list的實現&#xff0c;還是比較簡單的&#xff0c;大家只要想著土家樓的形狀&#xff0c;畫出圖來就好了&#xff0c;不需要過多擔心。本次的博客會發出一個完整的實現List的List.hpp&#xff0c;以后也會這樣&#xff0c;主要是分段發被說孩子分段生。二、模擬List由…

區塊鏈 + 域名Web3時代域名投資的新風口(上)

關于Dynadot Dynadot是通過ICANN認證的域名注冊商&#xff0c;自2002年成立以來&#xff0c;服務于全球108個國家和地區的客戶&#xff0c;為數以萬計的客戶提供簡潔&#xff0c;優惠&#xff0c;安全的域名注冊以及管理服務。 Dynadot平臺操作教程索引&#xff08;包括域名郵…