現在 AI vibe coding 最常遇到的問題就是重大版本更新重大版本更新,在寫 electron 的時候發現拖曳的物件怎麼都是 undefind ,經過排查,發現在 v29 可以使用到了 v34 就失效了
摘要
本文將介紹為何在 Electron v32 之後,原先可用的 File.path
屬性不再可用,以及如何改用官方推薦的 webUtils.getPathForFile(file)
方法來取得拖放或檔案輸入所對應的本機檔案路徑。文章涵蓋歷史沿革、移除原因、安全考量,以及完整的示範程式碼,幫助你在 v34 中順利升級。
背景說明
在 Electron v29(及更早版本),開發者可以直接在渲染程序或預加載腳本中透過 file.path
來取得使用者拖放或透過 input type="file"
選取的檔案絕對路徑,這種做法曾大大簡化桌面端檔案操作的流程。
不過,隨著應用安全性需求提升,Electron 團隊自 v32 起即標記 File.path
為已棄用(Deprecated),並在 v34 中完全移除此屬性。
File.path
?
為何移除 -
非標準性:
path
屬性並非 Web 標準的一部分,而是 Electron 團隊早期為便利開發者所做的客製化擴充,導致與瀏覽器端 API 規範不一致,增加未來維護與相容性的風險。 - 安全考量:直接在渲染程序中公開完整檔案路徑,可能使惡意網頁或第三方程式碼窺探使用者的檔案系統結構,對桌面應用的安全造成潛在威脅 。
-
統一 API:為了與標準化的 Web API 保持一致,Electron 團隊提供了
webUtils
這層橋接物件,將檔案系統存取集中管理,並要求透過預加載腳本明確暴露給渲染程序,進一步提升安全邊界控制。
webUtils.getPathForFile(file)
解決方案:改用 官方推薦的做法如下:
1. 預加載腳本 (preload.js)
在 preload.js
中透過 contextBridge
將安全的檔案路徑取得方法暴露給渲染程序:
// preload.js
const { contextBridge, webUtils } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
// 回傳傳入 File 物件所對應的本機路徑
getPathForFile: (file) => webUtils.getPathForFile(file)
});
以上程式碼中,
webUtils.getPathForFile(file)
會接收一個 WebFile
物件,並回傳其對應的檔案系統路徑;若傳入的不是實際存在的檔案物件,則會拋出例外或傳回空字串citeturn0search2。
2. 渲染程序 (renderer.js)
在真實的 DOM 事件中調用該 API:
// renderer.js
document.addEventListener('drop', async (event) => {
event.preventDefault();
const files = event.dataTransfer?.files;
if (files && files.length > 0) {
const file = files[0];
try {
const path = await window.electronAPI.getPathForFile(file);
console.log('Dropped file path:', path);
} catch (err) {
console.error('無法取得檔案路徑:', err);
}
}
});
// 避免拖放時開啟檔案
document.addEventListener('dragover', (event) => {
event.preventDefault();
});
使用
await window.electronAPI.getPathForFile(file)
可獲得實際檔案絕對路徑,並且整個流程透過contextBridge
嚴格限制了 Node.js API 的暴露範圍,提高應用的安全性。
3. 關於舊版兼容
若仍需在 v31 甚至更早版本支援相同 API,可在檢測到 webUtils.getPathForFile
不存在時,回退到自訂的 IPC 橋接或直接使用 file.path
(不建議,僅供非常特殊案例)。
範例程式碼整合
以下是一個完整的示範倉庫目錄結構與主要程式碼:
my-electron-app/
├── package.json
├── main.js
├── preload.js
└── renderer.js
// main.js
const { app, BrowserWindow } = require('electron');
const path = require('path');
function createWindow() {
const win = new BrowserWindow({
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
});
win.loadFile('index.html');
}
app.whenReady().then(createWindow);
// preload.js
const { contextBridge, webUtils } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
getPathForFile: (file) => webUtils.getPathForFile(file)
});
// renderer.js
window.addEventListener('DOMContentLoaded', () => {
document.body.innerHTML = '拖放檔案到此';
const dropZone = document.getElementById('drop-zone');
dropZone.addEventListener('dragover', (e) => e.preventDefault());
dropZone.addEventListener('drop', async (e) => {
e.preventDefault();
const file = e.dataTransfer.files[0];
if (file) {
const path = await window.electronAPI.getPathForFile(file);
dropZone.textContent = `檔案路徑:${path}`;
}
});
});
小結
-
核心變更:Electron 自 v32 起移除非標準的
File.path
,並在 v34 中完全移除該屬性,改以webUtils.getPathForFile(file)
替代,以提升安全性並維持 Web API 標準一致性。 -
實作方式:透過
contextBridge
在預加載腳本中安全地暴露webUtils.getPathForFile
,再於渲染程序中呼叫,確保不直接暴露 Node.js API,並避免惡意程式碼濫用。 -
升級建議:請務必將應用程式依循上述範例進行 refactor,以避免在升級到 v34 時因
file.path
不可用而導致功能異常。
參考連結
https://www.electronjs.org/docs/latest/breaking-changes#removed-filepath