使用Electron開發跨平臺本地文件管理器:從入門到實踐

在當今數字化時代,文件管理是每個計算機用戶日常工作中不可或缺的一部分。雖然操作系統都提供了自己的文件管理器,但開發一個自定義的文件管理器可以帶來更好的用戶體驗、特定功能的集成以及跨平臺的一致性。本文將詳細介紹如何使用Electron框架構建一個功能完善的本地文件管理器,涵蓋從環境搭建到核心功能實現的全過程。

第一部分:Electron簡介與技術選型

1.1 為什么選擇Electron?

Electron是一個由GitHub開發的開源框架,它允許開發者使用Web技術(HTML、CSS和JavaScript)構建跨平臺的桌面應用程序。其核心優勢在于:

  • 跨平臺支持:一次開發,可打包為Windows、macOS和Linux應用

  • 熟悉的開發棧:前端開發者可以快速上手

  • 強大的生態系統:豐富的npm模塊可供使用

  • 原生API訪問:通過Node.js集成可以訪問系統級功能

1.2 文件管理器的核心功能需求

一個實用的文件管理器通常需要實現以下功能:

  1. 文件瀏覽:查看目錄結構和文件列表

  2. 文件操作:創建、刪除、重命名、復制、移動文件

  3. 文件預覽:查看文件內容和基本信息

  4. 搜索功能:快速定位文件

  5. 多視圖支持:列表視圖、圖標視圖等

  6. 書簽/收藏:快速訪問常用目錄

第二部分:項目初始化與基礎架構

2.1 環境準備

首先確保系統已安裝:

  • Node.js (建議最新LTS版本)

  • npm或yarn

  • Git (可選)

# 創建項目目錄
mkdir electron-file-manager
cd electron-file-manager# 初始化項目
npm init -y# 安裝Electron
npm install electron --save-dev

2.2 項目結構設計

合理的項目結構有助于長期維護:

electron-file-manager/
├── main.js          # 主進程入口文件
├── preload.js       # 預加載腳本
├── package.json
├── src/
│   ├── assets/      # 靜態資源
│   ├── css/         # 樣式文件
│   ├── js/          # 渲染進程腳本
│   └── index.html   # 主界面
└── build/           # 打包配置

2.3 主進程基礎配置

main.js是Electron應用的入口點,負責創建和管理應用窗口:

const { app, BrowserWindow, ipcMain } = require('electron')
const path = require('path')let mainWindowfunction createWindow() {// 創建瀏覽器窗口mainWindow = new BrowserWindow({width: 1024,height: 768,webPreferences: {preload: path.join(__dirname, 'preload.js'),contextIsolation: true,enableRemoteModule: false},title: 'Electron文件管理器',icon: path.join(__dirname, 'src/assets/icon.png')})// 加載應用界面mainWindow.loadFile('src/index.html')// 開發模式下自動打開開發者工具if (process.env.NODE_ENV === 'development') {mainWindow.webContents.openDevTools()}
}// Electron初始化完成后調用
app.whenReady().then(createWindow)// 所有窗口關閉時退出應用(macOS除外)
app.on('window-all-closed', () => {if (process.platform !== 'darwin') app.quit()
})// macOS點擊dock圖標時重新創建窗口
app.on('activate', () => {if (BrowserWindow.getAllWindows().length === 0) createWindow()
})

第三部分:核心功能實現

3.1 文件系統交互

Electron通過Node.js的fs模塊與文件系統交互。我們需要在主進程和渲染進程之間建立安全的通信橋梁。

預加載腳本(preload.js):

const { contextBridge, ipcRenderer } = require('electron')
const path = require('path')// 安全地暴露API給渲染進程
contextBridge.exposeInMainWorld('electronAPI', {readDir: (dirPath) => ipcRenderer.invoke('read-dir', dirPath),getStats: (filePath) => ipcRenderer.invoke('get-file-stats', filePath),createDir: (dirPath) => ipcRenderer.invoke('create-dir', dirPath),deletePath: (path) => ipcRenderer.invoke('delete-path', path),renamePath: (oldPath, newPath) => ipcRenderer.invoke('rename-path', oldPath, newPath),joinPaths: (...paths) => path.join(...paths),pathBasename: (filePath) => path.basename(filePath),pathDirname: (filePath) => path.dirname(filePath)
})

主進程文件操作處理(main.js補充):

const fs = require('fs').promises
const path = require('path')// 讀取目錄內容
ipcMain.handle('read-dir', async (event, dirPath) => {try {const files = await fs.readdir(dirPath, { withFileTypes: true })return files.map(file => ({name: file.name,isDirectory: file.isDirectory(),path: path.join(dirPath, file.name)}))} catch (err) {console.error('讀取目錄錯誤:', err)throw err}
})// 獲取文件狀態信息
ipcMain.handle('get-file-stats', async (event, filePath) => {try {const stats = await fs.stat(filePath)return {size: stats.size,mtime: stats.mtime,isFile: stats.isFile(),isDirectory: stats.isDirectory()}} catch (err) {console.error('獲取文件狀態錯誤:', err)throw err}
})// 創建目錄
ipcMain.handle('create-dir', async (event, dirPath) => {try {await fs.mkdir(dirPath)return { success: true }} catch (err) {console.error('創建目錄錯誤:', err)throw err}
})// 刪除文件或目錄
ipcMain.handle('delete-path', async (event, targetPath) => {try {const stats = await fs.stat(targetPath)if (stats.isDirectory()) {await fs.rmdir(targetPath, { recursive: true })} else {await fs.unlink(targetPath)}return { success: true }} catch (err) {console.error('刪除路徑錯誤:', err)throw err}
})// 重命名文件或目錄
ipcMain.handle('rename-path', async (event, oldPath, newPath) => {try {await fs.rename(oldPath, newPath)return { success: true }} catch (err) {console.error('重命名錯誤:', err)throw err}
})

3.2 用戶界面實現

HTML結構(index.html):

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Electron文件管理器</title><link rel="stylesheet" href="css/main.css">
</head>
<body><div class="app-container"><!-- 頂部工具欄 --><div class="toolbar"><button id="back-btn" title="返回上級目錄">←</button><button id="forward-btn" title="前進" disabled>→</button><button id="home-btn" title="主目錄">?</button><div class="path-display" id="current-path"></div><button id="refresh-btn" title="刷新">?</button><button id="new-folder-btn" title="新建文件夾">+ 文件夾</button></div><!-- 文件瀏覽區 --><div class="file-browser"><div class="sidebar"><div class="quick-access"><h3>快速訪問</h3><ul id="quick-access-list"></ul></div></div><div class="main-content"><div class="view-options"><button class="view-btn active" data-view="list">列表視圖</button><button class="view-btn" data-view="grid">網格視圖</button></div><div class="file-list" id="file-list"></div></div></div><!-- 狀態欄 --><div class="status-bar"><span id="status-info">就緒</span></div></div><!-- 上下文菜單 --><div class="context-menu" id="context-menu"></div><script src="js/renderer.js"></script>
</body>
</html>

樣式設計(main.css):

/* 基礎樣式 */
* {margin: 0;padding: 0;box-sizing: border-box;
}body {font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;color: #333;background-color: #f5f5f5;
}.app-container {display: flex;flex-direction: column;height: 100vh;overflow: hidden;
}/* 工具欄樣式 */
.toolbar {padding: 8px 12px;background-color: #2c3e50;color: white;display: flex;align-items: center;gap: 8px;
}.toolbar button {background-color: #34495e;color: white;border: none;padding: 6px 12px;border-radius: 4px;cursor: pointer;transition: background-color 0.2s;
}.toolbar button:hover {background-color: #3d566e;
}.toolbar button:disabled {opacity: 0.5;cursor: not-allowed;
}.path-display {flex-grow: 1;background-color: white;color: #333;padding: 6px 12px;border-radius: 4px;font-family: monospace;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;
}/* 文件瀏覽區 */
.file-browser {display: flex;flex-grow: 1;overflow: hidden;
}.sidebar {width: 220px;background-color: #ecf0f1;padding: 12px;overflow-y: auto;
}.main-content {flex-grow: 1;display: flex;flex-direction: column;overflow: hidden;
}.view-options {padding: 8px 12px;background-color: #dfe6e9;
}.view-btn {background: none;border: none;padding: 4px 8px;cursor: pointer;
}.view-btn.active {background-color: #b2bec3;border-radius: 4px;
}.file-list {flex-grow: 1;overflow-y: auto;padding: 8px;
}/* 文件項樣式 */
.file-item {padding: 8px;display: flex;align-items: center;cursor: pointer;border-radius: 4px;
}.file-item:hover {background-color: #e0f7fa;
}.file-icon {width: 24px;height: 24px;margin-right: 8px;
}.file-name {flex-grow: 1;
}.file-size {color: #7f8c8d;font-size: 0.9em;margin-left: 12px;
}.file-date {color: #7f8c8d;font-size: 0.9em;margin-left: 12px;
}/* 狀態欄 */
.status-bar {padding: 4px 12px;background-color: #2c3e50;color: #ecf0f1;font-size: 0.9em;
}/* 上下文菜單 */
.context-menu {position: absolute;background-color: white;border: 1px solid #ddd;box-shadow: 0 2px 10px rgba(0,0,0,0.2);z-index: 1000;display: none;
}.context-menu-item {padding: 8px 16px;cursor: pointer;
}.context-menu-item:hover {background-color: #f0f0f0;
}

3.3 渲染進程邏輯(renderer.js)

class FileManager {constructor() {this.currentPath = process.platform === 'win32' ? 'C:\\' : '/'this.history = []this.historyIndex = -1this.initElements()this.initEventListeners()this.loadQuickAccess()this.navigateTo(this.currentPath)}initElements() {this.elements = {fileList: document.getElementById('file-list'),currentPath: document.getElementById('current-path'),backBtn: document.getElementById('back-btn'),forwardBtn: document.getElementById('forward-btn'),homeBtn: document.getElementById('home-btn'),refreshBtn: document.getElementById('refresh-btn'),newFolderBtn: document.getElementById('new-folder-btn'),quickAccessList: document.getElementById('quick-access-list'),statusInfo: document.getElementById('status-info'),contextMenu: document.getElementById('context-menu')}}initEventListeners() {// 導航按鈕this.elements.backBtn.addEventListener('click', () => this.goBack())this.elements.forwardBtn.addEventListener('click', () => this.goForward())this.elements.homeBtn.addEventListener('click', () => this.goHome())this.elements.refreshBtn.addEventListener('click', () => this.refresh())this.elements.newFolderBtn.addEventListener('click', () => this.createNewFolder())// 視圖切換按鈕document.querySelectorAll('.view-btn').forEach(btn => {btn.addEventListener('click', () => this.switchView(btn.dataset.view))})// 上下文菜單document.addEventListener('contextmenu', (e) => {e.preventDefault()this.showContextMenu(e)})document.addEventListener('click', () => {this.hideContextMenu()})}async navigateTo(path) {try {this.updateStatus(`正在加載: ${path}`)// 添加到歷史記錄if (this.historyIndex === -1 || this.history[this.historyIndex] !== path) {this.history = this.history.slice(0, this.historyIndex + 1)this.history.push(path)this.historyIndex++this.updateNavigationButtons()}this.currentPath = paththis.elements.currentPath.textContent = pathconst files = await window.electronAPI.readDir(path)this.displayFiles(files)this.updateStatus(`已加載: ${path}`)} catch (error) {console.error('導航錯誤:', error)this.updateStatus(`錯誤: ${error.message}`, true)}}displayFiles(files) {this.elements.fileList.innerHTML = ''// 添加返回上級目錄選項if (this.currentPath !== '/' && !this.currentPath.match(/^[A-Z]:\\?$/)) {const parentPath = window.electronAPI.pathDirname(this.currentPath)this.createFileItem({name: '..',isDirectory: true,path: parentPath})}// 添加文件和目錄files.forEach(file => {this.createFileItem(file)})}createFileItem(file) {const item = document.createElement('div')item.className = 'file-item'item.dataset.path = file.path// 文件圖標const icon = document.createElement('div')icon.className = 'file-icon'icon.innerHTML = file.isDirectory ? '📁' : '📄'// 文件名const name = document.createElement('div')name.className = 'file-name'name.textContent = file.nameitem.appendChild(icon)item.appendChild(name)// 如果是文件,添加大小信息if (!file.isDirectory) {window.electronAPI.getStats(file.path).then(stats => {const size = document.createElement('div')size.className = 'file-size'size.textContent = this.formatFileSize(stats.size)item.appendChild(size)const date = document.createElement('div')date.className = 'file-date'date.textContent = stats.mtime.toLocaleDateString()item.appendChild(date)})}// 點擊事件item.addEventListener('click', () => {if (file.isDirectory) {this.navigateTo(file.path)} else {this.showFileInfo(file.path)}})this.elements.fileList.appendChild(item)}// 其他方法實現...goBack() {if (this.historyIndex > 0) {this.historyIndex--this.navigateTo(this.history[this.historyIndex])}}goForward() {if (this.historyIndex < this.history.length - 1) {this.historyIndex++this.navigateTo(this.history[this.historyIndex])}}goHome() {const homePath = process.platform === 'win32' ? 'C:\\Users\\' + require('os').userInfo().username : require('os').homedir()this.navigateTo(homePath)}refresh() {this.navigateTo(this.currentPath)}async createNewFolder() {const folderName = prompt('輸入新文件夾名稱:')if (folderName) {try {const newPath = window.electronAPI.joinPaths(this.currentPath, folderName)await window.electronAPI.createDir(newPath)this.refresh()this.updateStatus(`已創建文件夾: ${folderName}`)} catch (error) {console.error('創建文件夾錯誤:', error)this.updateStatus(`錯誤: ${error.message}`, true)}}}updateNavigationButtons() {this.elements.backBtn.disabled = this.historyIndex <= 0this.elements.forwardBtn.disabled = this.historyIndex >= this.history.length - 1}formatFileSize(bytes) {if (bytes < 1024) return `${bytes} B`if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`}updateStatus(message, isError = false) {this.elements.statusInfo.textContent = messagethis.elements.statusInfo.style.color = isError ? '#e74c3c' : '#2ecc71'}loadQuickAccess() {const quickAccessPaths = [{ name: '桌面', path: require('os').homedir() + '/Desktop' },{ name: '文檔', path: require('os').homedir() + '/Documents' },{ name: '下載', path: require('os').homedir() + '/Downloads' }]quickAccessPaths.forEach(item => {const li = document.createElement('li')li.textContent = item.nameli.dataset.path = item.pathli.addEventListener('click', () => this.navigateTo(item.path))this.elements.quickAccessList.appendChild(li)})}showContextMenu(e) {// 實現上下文菜單邏輯}hideContextMenu() {this.elements.contextMenu.style.display = 'none'}async showFileInfo(filePath) {try {const stats = await window.electronAPI.getStats(filePath)alert(`文件信息:
路徑: ${filePath}
大小: ${this.formatFileSize(stats.size)}
修改時間: ${stats.mtime.toLocaleString()}
類型: ${stats.isDirectory ? '目錄' : '文件'}`)} catch (error) {console.error('獲取文件信息錯誤:', error)this.updateStatus(`錯誤: ${error.message}`, true)}}switchView(viewType) {// 實現視圖切換邏輯document.querySelectorAll('.view-btn').forEach(btn => {btn.classList.toggle('active', btn.dataset.view === viewType)})this.elements.fileList.className = `file-list ${viewType}-view`}
}// 初始化文件管理器
document.addEventListener('DOMContentLoaded', () => {new FileManager()
})

第四部分:功能擴展與優化

4.1 添加文件預覽功能

可以在右側添加一個預覽面板,當用戶選擇文件時顯示預覽內容:

// 在renderer.js中添加
class FileManager {// ...其他代碼...async previewFile(filePath) {try {const stats = await window.electronAPI.getStats(filePath)if (stats.isDirectory) returnconst previewPanel = document.getElementById('preview-panel')const ext = filePath.split('.').pop().toLowerCase()if (['jpg', 'jpeg', 'png', 'gif'].includes(ext)) {previewPanel.innerHTML = `<img src="${filePath}" alt="預覽" style="max-width: 100%; max-height: 100%;">`} else if (['txt', 'json', 'js', 'html', 'css', 'md'].includes(ext)) {const content = await window.electronAPI.readFile(filePath, 'utf-8')previewPanel.innerHTML = `<pre>${content}</pre>`} else {previewPanel.innerHTML = `<p>不支持預覽此文件類型</p>`}} catch (error) {console.error('預覽文件錯誤:', error)}}
}

4.2 實現文件搜索功能

添加一個搜索框和搜索功能:

// 在HTML中添加搜索框
<input type="text" id="search-input" placeholder="搜索文件...">
<button id="search-btn">搜索</button>// 在renderer.js中添加搜索功能
class FileManager {// ...其他代碼...initElements() {// ...其他元素...this.elements.searchInput = document.getElementById('search-input')this.elements.searchBtn = document.getElementById('search-btn')}initEventListeners() {// ...其他監聽器...this.elements.searchBtn.addEventListener('click', () => this.searchFiles())this.elements.searchInput.addEventListener('keyup', (e) => {if (e.key === 'Enter') this.searchFiles()})}async searchFiles() {const query = this.elements.searchInput.value.trim()if (!query) returntry {this.updateStatus(`正在搜索: ${query}`)// 這里需要實現遞歸搜索目錄的功能// 可以使用Node.js的fs模塊遞歸遍歷目錄// 或者使用第三方庫如fast-globconst results = await this.recursiveSearch(this.currentPath, query)this.displaySearchResults(results)this.updateStatus(`找到 ${results.length} 個結果`)} catch (error) {console.error('搜索錯誤:', error)this.updateStatus(`搜索錯誤: ${error.message}`, true)}}async recursiveSearch(dirPath, query) {// 實現遞歸搜索邏輯// 返回匹配的文件列表}displaySearchResults(results) {// 顯示搜索結果}
}

4.3 添加拖放功能

實現文件拖放操作:

class FileManager {// ...其他代碼...initEventListeners() {// ...其他監聽器...// 拖放支持this.elements.fileList.addEventListener('dragover', (e) => {e.preventDefault()e.dataTransfer.dropEffect = 'copy'})this.elements.fileList.addEventListener('drop', async (e) => {e.preventDefault()const files = e.dataTransfer.filesif (files.length === 0) returntry {this.updateStatus(`正在復制 ${files.length} 個文件...`)for (let i = 0; i < files.length; i++) {const file = files[i]const destPath = window.electronAPI.joinPaths(this.currentPath, file.name)// 實現文件復制邏輯await window.electronAPI.copyFile(file.path, destPath)}this.refresh()this.updateStatus(`已復制 ${files.length} 個文件`)} catch (error) {console.error('拖放錯誤:', error)this.updateStatus(`錯誤: ${error.message}`, true)}})}
}

第五部分:打包與分發

5.1 使用electron-builder打包

安裝electron-builder:

npm install electron-builder --save-dev

配置package.json:

{"name": "electron-file-manager","version": "1.0.0","main": "main.js","scripts": {"start": "electron .","pack": "electron-builder --dir","dist": "electron-builder","dist:win": "electron-builder --win","dist:mac": "electron-builder --mac","dist:linux": "electron-builder --linux"},"build": {"appId": "com.example.filemanager","productName": "Electron文件管理器","copyright": "Copyright ? 2023","win": {"target": "nsis","icon": "build/icon.ico"},"mac": {"target": "dmg","icon": "build/icon.icns"},"linux": {"target": "AppImage","icon": "build/icon.png"}}
}

運行打包命令:

npm run dist

5.2 自動更新功能

實現自動更新功能可以讓用戶始終使用最新版本:

// 在主進程(main.js)中添加
const { autoUpdater } = require('electron-updater')// 在app.whenReady()中添加
autoUpdater.checkForUpdatesAndNotify()autoUpdater.on('update-available', () => {mainWindow.webContents.send('update-available')
})autoUpdater.on('update-downloaded', () => {mainWindow.webContents.send('update-downloaded')
})// 在渲染進程中監聽更新事件
ipcRenderer.on('update-available', () => {// 通知用戶有可用更新
})ipcRenderer.on('update-downloaded', () => {// 提示用戶重啟應用以完成更新
})

第六部分:安全最佳實踐

開發Electron應用時,安全性至關重要:

  1. 啟用上下文隔離:防止惡意網站訪問Node.js API

  2. 禁用Node.js集成:在不必要的渲染進程中禁用Node.js集成

  3. 驗證所有輸入:特別是文件路徑和URL

  4. 使用最新Electron版本:及時修復安全漏洞

  5. 限制權限:只請求應用所需的最小權限

  6. 內容安全策略(CSP):防止XSS攻擊

結語

通過本文的指導,你已經學會了如何使用Electron開發一個功能完善的本地文件管理器。從基礎的文件瀏覽到高級功能如搜索、預覽和拖放操作,我們覆蓋了文件管理器的核心功能。Electron的強大之處在于它讓Web開發者能夠利用已有的技能構建跨平臺的桌面應用。

這個項目還有很多可以擴展的方向:

  • 添加標簽頁支持

  • 實現文件壓縮/解壓功能

  • 集成云存儲服務

  • 添加自定義主題支持

  • 實現文件批量操作

希望這個項目能夠成為你Electron開發之旅的良好起點,鼓勵你繼續探索和擴展這個文件管理器的功能!

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

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

相關文章

JBHI 2025 | 潛在擴散模型賦能胸部X射線骨抑制

Abstract: 肺部疾病是全球健康面臨的一項重大挑戰&#xff0c;胸部 X 光檢查&#xff08;CXR&#xff09;因其方便性和經濟性而成為一種重要的診斷工具。 然而&#xff0c;CXR 圖像中重疊的骨結構往往會阻礙肺部病變的檢測&#xff0c;從而導致潛在的誤診。 為解決這一問題&am…

408第三季part2 - 計算機網絡 - 計算機網絡基本概念

理解然后區分一下這2個區別特點是建立連接存儲轉發的意思是A先發給B&#xff0c;B再發給C&#xff0c;就這樣這里缺點比如A很大&#xff0c;你給B緩存開銷大還需要排序然后形象的圖題目分組頭部要放一些源地址和目的地址這些東西以后發數據只會往近的發&#xff0c;不可能往下面…

互補功率放大器Multisim電路仿真——硬件工程師筆記

目錄 1 互補功率放大器基礎知識 1.1 工作原理 1.2 電路結構 1.3 優點 1.4 缺點 1.5 應用 1.6 總結 2 OCL乙類互補功率放大電路 2.1 電路結構 2.2 工作原理 2.3 優點 2.4 缺點 2.5 總結 3 OCL甲乙類互補功率放大電路 3.1 電路結構 3.2 工作原理 3.3 優點 3.4 …

【1】確認安裝 Node.js 和 npm版本號

搭建前端項目時需要安裝 Node.js 和 npm&#xff0c;主要是因為它們提供了一些重要的功能和工具&#xff0c;幫助開發者高效地開發、構建和管理項目。一、具體原因如下&#xff1a; Node.js&#xff1a;JavaScript 運行環境 Node.js 是一個基于 Chrome V8 引擎的 JavaScript 運…

7、從網絡中獲取數據

目錄 訂閱網絡狀態變化創建網絡對象獲取默認激活網絡及其能力可訂閱事件可訂閱事件——網絡可用事件可訂閱事件——網絡阻塞狀態事件可訂閱事件——網絡能力變化事件可訂閱事件——網絡連接信息變化事件可訂閱事件——網絡丟失事件常見事件訂閱場景 開發流程 使用HTTP訪問網絡發…

搭建個人博客系列--docker

因為后續所有的組件都會在docker上安裝&#xff0c;所以要先安裝docker。一、安裝docker1.配置yumyum install -y yum-utilsyum makecache fast2.卸載老dockeryum remove docker3.配置鏡像地址yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos…

【Note】《Kafka: The Definitive Guide》 第5章:深入 Kafka 內部結構,理解分布式日志系統的核心奧秘

《Kafka: The Definitive Guide》 第5章&#xff1a;深入 Kafka 內部結構&#xff0c;理解分布式日志系統的核心奧秘 Apache Kafka 在表面上看似只是一個“分布式消息隊列”&#xff0c;但其背后的存儲架構、分區機制、復制策略與高性能設計&#xff0c;才是它在千萬級 TPS 場景…

當“漏洞”成為雙刃劍——合法披露與非法交易的生死線在哪里?

首席數據官高鵬律師數字經濟團隊創作&#xff0c;AI輔助 一、一場“漏洞”的博弈&#xff1a;從“手術刀”到“毒藥”的分界 2025年夏&#xff0c;某電商平臺因系統漏洞被曝光&#xff0c;引發輿論風暴。白帽子甲在發現漏洞后&#xff0c;第一時間聯系平臺技術團隊&#xff0…

Hadoop 分布式存儲與計算框架詳解

Hadoop開發實戰:https://www.borimooc.com/course/1004.htm hadoop是適合海量數據的分布式存儲&#xff0c;和分布式計算的框架 hadoop有三大組件: mapreduce&#xff1a;適合海量數據的分布式計算&#xff0c;分為map階段、shuffle階段和reduce階段hdfs&#xff1a;分布式文…

LeetCode 2099.找到和最大的長度為 K 的子序列:自定義排序

【LetMeFly】2099.找到和最大的長度為 K 的子序列&#xff1a;自定義排序 力扣題目鏈接&#xff1a;https://leetcode.cn/problems/find-subsequence-of-length-k-with-the-largest-sum/ 給你一個整數數組 nums 和一個整數 k 。你需要找到 nums 中長度為 k 的 子序列 &#x…

循環移位網絡設計

總體架構 模塊描述 循環移位網絡模塊&#xff08;模塊名&#xff1a;VAL_CS_PROC&#xff09;&#xff0c;對輸入數據&#xff08;in_data&#xff09;做循環移位處理&#xff0c;兩個cycle即可輸出數據。 Fig 1 循環移位模塊頂層 設計要求 00】 支持對data_num個有效數據做…

IO進程線程(IPC通訊)

目錄 一、IPC通訊機制 1&#xff09;傳統的通訊機制&#xff1a; 2&#xff09;systemV 的通訊機制&#xff1a; 3&#xff09;跨主機的通訊機制&#xff1a; 1、無名管道 1&#xff09;無名管道的概念 2&#xff09;無名管道的函數 3&#xff09;無名管道通訊&#xf…

Webpack 5 核心機制詳解與打包性能優化實踐

&#x1f916; 作者簡介&#xff1a;水煮白菜王&#xff0c;一個web開發工程師 &#x1f47b; &#x1f440; 文章專欄&#xff1a; 前端專欄 &#xff0c;記錄一下平時在博客寫作中&#xff0c;總結出的一些開發技巧和知識歸納總結?。 感謝支持&#x1f495;&#x1f495;&am…

Manus AI與多語言手寫識別

技術文章大綱&#xff1a;Manus AI與多語言手寫識別 引言 手寫識別技術的發展背景與市場需求Manus AI的定位與核心技術優勢多語言場景下的挑戰與機遇 Manus AI的核心技術架構 基于深度學習的端到端手寫識別模型多模態數據融合&#xff08;筆跡壓力、書寫軌跡等&#xff09;…

Go與Python爬蟲對比及模板實現

go語言和Python語言都可選作用來爬蟲項目&#xff0c;因為python經過十幾年的累積&#xff0c;各種庫是應有盡有&#xff0c;學習也相對比較簡單&#xff0c;相比GO起步較晚還是有很大優勢的&#xff0c;么有對比就沒有傷害&#xff0c;所以我利用一個下午&#xff0c;寫個Go爬…

Vidwall: 支持將 4K 視頻設置為動態桌面壁紙,兼容 MP4 和 MOV 格式

支持將 4K 視頻設置為動態桌面壁紙&#xff0c;兼容 MP4 和 MOV 格式。只需將視頻拖入應用界面&#xff0c;點擊即可立即應用為桌面背景。 為桌面增添生動趣味的動態壁紙效果&#xff01;錄制視頻時設置動態背景&#xff0c;也能讓畫面更吸引人。 &#x1f4e5; https://apps.…

【LeetCode 熱題 100】234. 回文鏈表——快慢指針+反轉鏈表

Problem: 234. 回文鏈表 題目&#xff1a;給你一個單鏈表的頭節點 head &#xff0c;請你判斷該鏈表是否為回文鏈表。如果是&#xff0c;返回 true &#xff1b;否則&#xff0c;返回 false 。 文章目錄 整體思路完整代碼時空復雜度時間復雜度&#xff1a;O(N)空間復雜度&#…

【源力覺醒 創作者計劃】開源、易用、強中文:文心一言4.5或是 普通人/非AI程序員 的第一款中文AI?

前言 你有沒有發現&#xff0c;AI 正在悄悄滲透進我們的生活&#xff1a;寫文案、畫插圖、做PPT、答作業&#xff0c;它幾乎無所不能&#x1f60d; &#xff01;但很多人可能會問&#xff1a; AI&#xff0c;我能用嗎&#xff1f;用得起嗎&#xff1f;適合我嗎&#xff1f;特別…

【保姆級喂飯教程】Git圖形化客戶端Sourcetree安裝及使用教程

目錄 前言一、SourceTree簡介二、安裝教程三、使用教程1. 添加倉庫 四、評價總結后記參考文獻 前言 在查找Git Flow實現工具的時候&#xff0c;看到了SourceTree&#xff0c;支持Git Flow、GitHub Flow等多種Git工作流&#xff0c;安裝簡單學習一下。 一、SourceTree簡介 Git的…

【kafka】kafka3.3.2常用命令

查看kafka服務版本 [rootlocalhost eicar]# kafka-server-start.sh --version [2025-06-23 11:10:54,106] INFO Registered kafka:typekafka.Log4jController MBean (kafka.utils.Log4jControllerRegistration$) 3.3.2 (Commit:b66af662e61082cb) [rootlocalhost eicar]#查看消…