如何在Remix.run中配置多个公共文件夹(Electron场景)
解决Remix + Electron双公共资源目录加载问题
方案1:注册Electron自定义协议加载本地资源
Electron默认对file://协议有安全限制,直接使用容易出问题,我们可以注册一个自定义协议来代理加载用户上传的assets资源,同时保留Remix默认的/build/资源路径。
- 在Electron主进程注册自定义协议
在你的Electron主入口文件(比如main.ts)中,添加协议注册逻辑:
import { app, protocol } from 'electron'; import fs from 'fs'; import path from 'path'; app.whenReady().then(() => { // 注册自定义协议,比如命名为app-resource protocol.handle('app-resource', async (request) => { // 获取用户assets目录路径 const userAssetsPath = path.join(app.getPath('userData'), 'assets'); // 解析请求的文件名 const url = new URL(request.url); const filePath = path.join(userAssetsPath, url.pathname); try { // 读取文件内容 const fileBuffer = await fs.promises.readFile(filePath); // 根据文件后缀设置Content-Type const ext = path.extname(filePath).slice(1); let contentType = 'application/octet-stream'; if (ext === 'jpg' || ext === 'jpeg') contentType = 'image/jpeg'; if (ext === 'png') contentType = 'image/png'; if (ext === 'gif') contentType = 'image/gif'; return new Response(fileBuffer, { headers: { 'Content-Type': contentType } }); } catch (err) { return new Response('File not found', { status: 404 }); } }); // 其他主进程逻辑... });
- 前端使用自定义协议加载图片
在Remix组件中,直接使用自定义协议路径:
export default function UserAsset() { return <img src="app-resource://my-uploaded-image.jpg" alt="用户上传资源" />; }
这个方法完全不影响Remix默认的/build/资源加载,两者互不干扰。
方案2:通过Remix路由代理本地资源请求
如果希望前端用统一的相对路径加载资源,可以在Remix中添加一个代理路由,把/assets/*的请求转发到本地用户assets目录。
- 创建Remix代理路由
在app/routes/assets/$filename.tsx中创建路由:
import { LoaderFunctionArgs } from '@remix-run/node'; import fs from 'fs'; import path from 'path'; import { app } from '@electron/remote'; // 需确保Electron启用remote模块,或通过主进程传递路径 export async function loader({ params }: LoaderFunctionArgs) { const { filename } = params; if (!filename) return new Response('Missing filename', { status: 400 }); // 防止路径遍历攻击,只允许合法文件名 const safeFilename = path.basename(filename); const userAssetsPath = path.join(app.getPath('userData'), 'assets'); const filePath = path.join(userAssetsPath, safeFilename); try { const fileBuffer = await fs.promises.readFile(filePath); const ext = path.extname(filePath).slice(1); let contentType = 'application/octet-stream'; if (ext === 'jpg' || ext === 'jpeg') contentType = 'image/jpeg'; if (ext === 'png') contentType = 'image/png'; if (ext === 'gif') contentType = 'image/gif'; return new Response(fileBuffer, { headers: { 'Content-Type': contentType } }); } catch (err) { return new Response('File not found', { status: 404 }); } }
- 前端使用相对路径加载
在组件中直接用/assets/开头的路径:
export default function UserAsset() { return <img src="/assets/my-uploaded-image.jpg" alt="用户上传资源" />; }
这个方法让前端资源路径更统一,Remix默认的/build/资源正常加载,/assets/*的请求由我们的代理路由处理。
注意事项
- 路径安全:无论哪种方案,都要对用户传入的文件名做校验(比如用
path.basename过滤),防止路径遍历攻击。 - Electron权限:确保主进程有权限读取
Application Support目录,默认情况下Electron应用是有权限的,但打包后如果有特殊权限设置需要检查。 - 开发/生产区分:可以用
app.isPackaged判断当前环境,调整assets路径(比如开发时用本地测试目录,生产时用userData路径)。
内容的提问来源于stack exchange,提问作者Suraj Air
相关产品推荐
相关产品推荐

