Appearance
自定义协议加载本地文件
以下两种方法都可行的(自行选择一种就行)
在electron中是有一个安全机制的,他不能直接访问电脑本地的文件,但是可以使用自定义协议来加载,使用 local:// 前缀(这个前缀可以自定义)
前提:如果加载很大的文件就要小心了,因为自定义协议的一次性读取可能压力很大。可以考虑在协议处理中实现流式传输,不过会比较复杂。
自定义协议
主要使用protocol模块,在主进程文件main/index.js文件的app.whenReady()中注册处理函数
js
// main.js (主进程文件)
const { app, BrowserWindow, protocol } = require('electron')
const path = require('path')
const fs = require('fs')
// 1. 在应用准备就绪前,声明协议的权限
app.whenReady().then(() => {
protocol.registerFileProtocol('local', (request, callback) => {
try {
// 去除协议头,获取文件路径
let filePath = request.url.replace('local://', '')
// 对路径进行解码和规范化处理
filePath = decodeURIComponent(filePath)
filePath = path.normalize(filePath)
// 安全检查:确保文件存在
if (fs.existsSync(filePath)) {
callback(filePath)
} else {
callback({ error: -6 }) // FILE_NOT_FOUND
}
} catch (error) {
console.error('Protocol error:', error)
callback({ error: -2 }) // FAILED
}
})
// 创建窗口等后续操作
createWindow()
})// main.js (主进程文件)
const { app, BrowserWindow, protocol } = require('electron')
const path = require('path')
const fs = require('fs')
// 1. 在应用准备就绪前,声明协议的权限
app.whenReady().then(() => {
protocol.registerFileProtocol('local', (request, callback) => {
try {
// 去除协议头,获取文件路径
let filePath = request.url.replace('local://', '')
// 对路径进行解码和规范化处理
filePath = decodeURIComponent(filePath)
filePath = path.normalize(filePath)
// 安全检查:确保文件存在
if (fs.existsSync(filePath)) {
callback(filePath)
} else {
callback({ error: -6 }) // FILE_NOT_FOUND
}
} catch (error) {
console.error('Protocol error:', error)
callback({ error: -2 }) // FAILED
}
})
// 创建窗口等后续操作
createWindow()
})vue组件中注册自定义协议
vue
<script setup>
const imageURL = 'local://C:/Users/luoluo/Desktop/640.png'//window电脑路径
//如果是mac电脑路径:local:///Users/luoluo/Desktop/640.png
</script>
<template>
<img alt="logo" class="logo" :src="imageURL" />
</template><script setup>
const imageURL = 'local://C:/Users/luoluo/Desktop/640.png'//window电脑路径
//如果是mac电脑路径:local:///Users/luoluo/Desktop/640.png
</script>
<template>
<img alt="logo" class="logo" :src="imageURL" />
</template>index.html文件的内容安全策略也要加上自定义协议local:;
html
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src 'self' data: local:;"><meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src 'self' data: local:;">文件选择器加载
结合主进程、渲染进程和 Vue 组件,动态加载用户选择的图片。
js
// main.js (主进程文件)
const { app, BrowserWindow, protocol, ipcMain } = require('electron')
const path = require('path')
const fs = require('fs')
// 1. 在应用准备就绪前,声明协议的权限
app.whenReady().then(() => {
protocol.registerFileProtocol('local', (request, callback) => {
try {
// 去除协议头,获取文件路径
let filePath = request.url.replace('local://', '')
// 对路径进行解码和规范化处理
filePath = decodeURIComponent(filePath)
filePath = path.normalize(filePath)
// 安全检查:确保文件存在
if (fs.existsSync(filePath)) {
callback(filePath)
} else {
callback({ error: -6 }) // FILE_NOT_FOUND
}
} catch (error) {
console.error('Protocol error:', error)
callback({ error: -2 }) // FAILED
}
})
ipcMain.handle('select-image', async () => {
return 'C:/Users/luoluo/Desktop/640.png' // 返回选择的图片路径
})
// 创建窗口等后续操作
createWindow()
})// main.js (主进程文件)
const { app, BrowserWindow, protocol, ipcMain } = require('electron')
const path = require('path')
const fs = require('fs')
// 1. 在应用准备就绪前,声明协议的权限
app.whenReady().then(() => {
protocol.registerFileProtocol('local', (request, callback) => {
try {
// 去除协议头,获取文件路径
let filePath = request.url.replace('local://', '')
// 对路径进行解码和规范化处理
filePath = decodeURIComponent(filePath)
filePath = path.normalize(filePath)
// 安全检查:确保文件存在
if (fs.existsSync(filePath)) {
callback(filePath)
} else {
callback({ error: -6 }) // FILE_NOT_FOUND
}
} catch (error) {
console.error('Protocol error:', error)
callback({ error: -2 }) // FAILED
}
})
ipcMain.handle('select-image', async () => {
return 'C:/Users/luoluo/Desktop/640.png' // 返回选择的图片路径
})
// 创建窗口等后续操作
createWindow()
})预加载脚本中暴露 IPC 通信方法
预加载脚本 (preload.js) 中,暴露一个安全的 selectImage 方法给渲染进程。
js
// 在预加载脚本中 (preload.js)
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
selectImage: () => ipcRenderer.invoke('select-image')
})// 在预加载脚本中 (preload.js)
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
selectImage: () => ipcRenderer.invoke('select-image')
})vue组件
vue
<script setup>
import { ref } from 'vue'
const image = ref('')
const imageShow = ref(false)
const loadImage = async () => {
imageShow.value = true
const imagePath = await window.electronAPI.selectImage()
image.value = `local://${imagePath}`
}
</script>
<template>
<button @click="loadImage">加载图片</button>
<img :src="image" v-if="imageShow" alt="选择的图片" />
</template><script setup>
import { ref } from 'vue'
const image = ref('')
const imageShow = ref(false)
const loadImage = async () => {
imageShow.value = true
const imagePath = await window.electronAPI.selectImage()
image.value = `local://${imagePath}`
}
</script>
<template>
<button @click="loadImage">加载图片</button>
<img :src="image" v-if="imageShow" alt="选择的图片" />
</template>
小洛的前端技术博客