You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Remix.run中配置多个公共文件夹(Electron场景)

解决Remix + Electron双公共资源目录加载问题

方案1:注册Electron自定义协议加载本地资源

Electron默认对file://协议有安全限制,直接使用容易出问题,我们可以注册一个自定义协议来代理加载用户上传的assets资源,同时保留Remix默认的/build/资源路径。

  1. 在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 });
    }
  });

  // 其他主进程逻辑...
});
  1. 前端使用自定义协议加载图片
    在Remix组件中,直接使用自定义协议路径:
export default function UserAsset() {
  return <img src="app-resource://my-uploaded-image.jpg" alt="用户上传资源" />;
}

这个方法完全不影响Remix默认的/build/资源加载,两者互不干扰。

方案2:通过Remix路由代理本地资源请求

如果希望前端用统一的相对路径加载资源,可以在Remix中添加一个代理路由,把/assets/*的请求转发到本地用户assets目录。

  1. 创建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 });
  }
}
  1. 前端使用相对路径加载
    在组件中直接用/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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 19:15:34