返回

解决Cloudflare Worker上传报错:Main module name is not present

javascript

解决 Cloudflare API 上传 Worker 脚本报错:Main module name is not present in bundle

最近在尝试通过 Cloudflare API 上传一个 worker 脚本,带有一些元数据,却遇到了个麻烦事儿:10021 - Uncaught TypeError: Main module name is not present in bundle。反复检查了代码和官方文档,也没找到头绪。手动在 Cloudflare Dashboard 粘贴同样的代码倒是能正常工作。经过一番折腾,终于找到了问题所在和多种解决办法,分享出来给大家避坑。

一、 问题原因分析

报错信息“Main module name is not present in bundle”明确指出,上传的 bundle 中缺少了主模块的名称。问题很可能出在通过 API 上传 worker 的方式上。对比手动在 Dashboard 中粘贴代码,API 上传时需要将代码和元数据以特定的 multipart/form-data 格式发送,并且对文件的命名和内容都有要求。 仔细看,我当时的代码犯了以下错误:

  1. FormData 的使用方式不对 : 使用 form.append('main.js', workerCode) 直接将代码字符串作为文件内容附加,这种方式生成的 FormData 对象在发送到 Cloudflare API 时可能无法正确识别为脚本文件。
  2. 可能缺少 Content-Type : 即便最外层指定了,但每一个form.append内部的参数也有要求,有可能要分别设定Content-Type.

二、 解决方案

针对以上问题,我整理了几个可靠的解决办法:

1. 使用 Blob 封装 worker 代码

原理:Blob 对象代表了一段二进制数据,可以用来表示各种类型的文件,包括 JavaScript 脚本。通过将 worker 代码封装成 Blob 对象,再将其添加到 FormData 中,可以确保 Cloudflare API 正确识别脚本文件的类型。

代码示例:

import axios from 'axios';
import FormData from 'form-data';

const accountId = 'YOUR_ACCOUNT_ID';
const workerName = 'YOUR_WORKER_NAME';
const myToken = 'YOUR_API_TOKEN';

const api = axios.create({
    baseURL: 'https://api.cloudflare.com/client/v4',
    headers: {
        Authorization: `Bearer ${myToken}`,
        // 'Content-Type': 'multipart/form-data', // 可以不用显式设置,FormData 会自动处理
    },
});

const workerCode = `export default {
    async fetch(request, env, ctx) {
        return new Response("Hello World!");
    },
};`;

const form = new FormData();

// 使用 Blob 封装 worker 代码,并指定文件类型为 'application/javascript'
const workerBlob = new Blob([workerCode], { type: 'application/javascript' });
form.append('main.js', workerBlob, 'main.js');

form.append(
    'metadata',
    JSON.stringify({
        main_module: 'main.js',
        compatibility_date: '2025-02-04',
        compatibility_flags: ['nodejs_compat'],
    }),
     { contentType: 'application/json' }
);

api.put(
    `/accounts/${accountId}/workers/scripts/${workerName}`,
    form,
      { headers: form.getHeaders() } // 这一步是必须的! form.getHeaders() 很重要!
).then(response => {
    console.log('Worker 上传成功:', response.data);
}).catch(error => {
    console.error('Worker 上传失败:', error.response.data);
});

安全建议:

  • 妥善保管好 YOUR_API_TOKEN,不要泄露。
  • accountIdworkerName 也要确认无误.

进阶技巧:
可以针对不同环境编写不同配置,再通过读取环境变量的方式选择不同的Blob.

2. 调整 FormData 中文件的添加方式(Node.js 环境)

如果是在 Node.js 环境下,直接使用 form.append() 有时会出问题,可以使用 form.append() 的第三个参数,显式指定文件名和类型。

代码示例:

import axios from 'axios';
import FormData from 'form-data';
import { Readable } from 'stream';

const accountId = 'YOUR_ACCOUNT_ID';
const workerName = 'YOUR_WORKER_NAME';
const myToken = 'YOUR_API_TOKEN';

const api = axios.create({
    baseURL: 'https://api.cloudflare.com/client/v4',
    headers: {
        Authorization: `Bearer ${myToken}`,
        //'Content-Type': 'multipart/form-data', // FormData 会自动设置
    },
});

const workerCode = `export default {
  async fetch(request, env, ctx) {
    return new Response("Hello World!");
  },
};`;

const form = new FormData();

// 将字符串转换为流
const workerStream = new Readable();
workerStream.push(workerCode);
workerStream.push(null); // 表示流结束

form.append('main.js', workerStream, {
    filename: 'main.js',
    contentType: 'application/javascript',
});

form.append(
    'metadata',
    JSON.stringify({
        main_module: 'main.js',
        compatibility_date: '2025-02-04',
        compatibility_flags: ['nodejs_compat'],
    }),
    { contentType: 'application/json' }
);

api.put(
    `/accounts/${accountId}/workers/scripts/${workerName}`,
    form,
     { headers: form.getHeaders() }
)
.then(response => {
        console.log('Worker 上传成功:', response.data);
    })
    .catch(error => {
        console.error('Worker 上传失败:', error.response.data);
    });

原理:

Node.js 环境下,FormData 内部处理文件上传的方式略有不同。form.append() 第三个参数允许显式指定文件名(filename)和内容类型(contentType),帮助 Cloudflare API 更准确地解析上传的文件。将代码转成 stream,也更有利于传输。

进阶:
可以通过此方式添加多个文件,适合需要模块化设计的 Worker.

3. 使用 Wrangler CLI(官方推荐)

如果觉得 API 调用太麻烦,Cloudflare 官方提供了命令行工具 Wrangler,上传 worker 非常方便。

操作步骤:

  1. 安装 Wrangler:

    npm install -g wrangler
    
  2. 登录 Cloudflare 账户:

    wrangler login
    
  3. 创建项目:

    wrangler init my-worker
    
  4. 编写 wrangler.toml 配置文件,并添加元数据信息

在项目的根目录下,wrangler.toml 文件里,需要配置 worker 的基本信息,包括名称、入口文件、兼容性日期等,示例如下:

```toml
name = "my-worker" #worker名字
main = "src/index.js" # worker 入口
compatibility_date = "2025-02-04" # 兼容性日期
compatibility_flags = ["nodejs_compat"]

[build.upload]
format = "modules"
main = "./src/index.js"

```

关键就是这个 [build.upload] 。 format 的值设为 "modules". main 设置为入口文件.

  1. 编写 worker 代码 (src/index.js):
    这个不用多说了, 和前面的示例一致.

        export default {
        async fetch(request, env, ctx) {
        return new Response("Hello World!");
         },
       };
    
  2. 发布 worker:

    wrangler deploy
    

原理:Wrangler CLI 内部封装了与 Cloudflare API 交互的逻辑,自动处理 multipart/form-data 格式、文件类型等细节,使用起来更简单、更不容易出错。强烈推荐!

安全性:

  • wrangler 会处理你的认证过程, 它会保证token安全.

进阶技巧:

  • wrangler 有很多子命令, 可以用 wrangler -h 查看。
  • 可以方便管理多个 worker.
  • 可以用 wrangler dev 在本地开发和测试.

这几种方法都能解决上传 Worker 脚本报错的问题。具体用哪个,看你自己的喜好。要是图省事,就直接用 Wrangler CLI;要是想深入了解 API 的工作方式,就自己封装 FormData。 总之,上传时一定要注意文件的格式、类型以及元数据的配置。