[問題解法紀錄] electron 拖曳檔案取得路徑問題

問題解法紀錄 · 19 天前 · 119 人瀏覽過

現在 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

  1. 非標準性path 屬性並非 Web 標準的一部分,而是 Electron 團隊早期為便利開發者所做的客製化擴充,導致與瀏覽器端 API 規範不一致,增加未來維護與相容性的風險。
  2. 安全考量:直接在渲染程序中公開完整檔案路徑,可能使惡意網頁或第三方程式碼窺探使用者的檔案系統結構,對桌面應用的安全造成潛在威脅 。
  3. 統一 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) 會接收一個 Web File 物件,並回傳其對應的檔案系統路徑;若傳入的不是實際存在的檔案物件,則會拋出例外或傳回空字串citeturn0search2。

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

Electron
Theme Jasmine by Kent Liao