实战SOP:CodeArts操作FunctionGraph,云端处理本地文件

举报
shuai1021 发表于 2026/11/13 13:10:13 2026/11/13
【摘要】 笔记本处理几百个小文件,依赖装不齐、换台机器就崩。本文记录用码道CodeArts把处理逻辑搬上函数工作流:定输入输出契约、写base64编解码handler、本地CLI循环调用、部署与结果对账。附同步调用体积上限、编码BOM、并发流控、幂等重试等七条避坑要点。

背景:本地处理小文件,环境比算法更麻烦

手上有个很普通的活:一批 CSV 和 Excel,要转成 JSON 落库,几百个小文件,每个几 KB 到几 MB。单机跑不难,难的是环境——依赖装在本机能跑,换到同事电脑上就报编码错、缺库;跑完还得把结果拷回去。三台机器同步一次环境,时间比处理文件本身还长。

换成另一种思路就顺了:把处理逻辑放到云端函数,本地只做三件事——读文件、调函数、写结果。函数的环境是固定的,依赖是打进包的,谁来跑结果都一样。这就是"云端操作本地小文件":文件不出门,处理逻辑统一在云上。

关键是规模要匹配:小文件用同步调用足够;大文件就该走对象存储和异步触发,别硬塞进请求体。

方案:五步,一条命令跑完整批

步骤 人工做的事 CodeArts 做的事
1. 定契约 说清输入、输出、错误约定 生成事件结构与返回规范
2. 写 handler 确认处理逻辑 实现编解码、类型分发、异常兜底
3. 写客户端 指定目录与操作类型 实现遍历、调用、结果落盘
4. 部署 确认地域与函数规格 建函数、传代码、配环境变量
5. 验证 给一批样本 本地与云端结果逐份比对

1. 定契约。 我先约定三件事:请求体是 {op, name, data},data 是文件内容的 base64;响应统一是 {ok, op, data, req};失败也返回 200 但 ok=false,把错误信息带回来。这样本地客户端只有一条判断路径。

2. 写 handler:

import base64, csv, io, json

def handler(event, context):
    try:
        body = json.loads(event.get("body") or "{}") if isinstance(event, dict) else {}
        op = body.get("op", "csv2json")
        raw = base64.b64decode(body.get("data", ""))

        if op == "csv2json":
            # utf-8-sig 吃掉 BOM,否则第一个列名会带上不可见字符
            rows = list(csv.DictReader(io.StringIO(raw.decode("utf-8-sig"))))
            out = json.dumps(rows, ensure_ascii=False).encode("utf-8")
        else:
            out = raw

        return {
            "statusCode": 200,
            "headers": {"Content-Type": "application/json"},
            "body": json.dumps({
                "ok": True, "op": op,
                "data": base64.b64encode(out).decode(),
                "req": context.request_id,
            }),
        }
    except Exception as e:
        return {"statusCode": 200,
                "body": json.dumps({"ok": False, "err": str(e),
                                    "req": context.request_id})}

3. 写本地客户端(遍历、调用、写回):

import base64, json, pathlib, subprocess

FUNC_URN = "<你的函数URN>"

def call_func(path, op="csv2json"):
    payload = json.dumps({
        "op": op, "name": path.name,
        "data": base64.b64encode(path.read_bytes()).decode(),
    })
    r = subprocess.run(
        ["hcloud", "FunctionGraph", "InvokeFunction",
         f"--func_urn={FUNC_URN}", "--body", payload],
        capture_output=True, check=True,
    )
    return json.loads(r.stdout)

out_dir = pathlib.Path("outbox"); out_dir.mkdir(exist_ok=True)
for p in pathlib.Path("inbox").glob("*.csv"):
    res = call_func(p)["body"]          # 外层是 API 响应,内层是函数返回
    inner = json.loads(res)
    if inner["ok"]:
        out_dir.joinpath(p.stem + ".json").write_text(
            base64.b64decode(inner["data"]).decode("utf-8"),
            encoding="utf-8")
    else:
        print("FAIL", p.name, inner["err"], inner["req"])

4. 部署与验证。 函数侧和普通函数一样:建函数、上传 zip、设超时与内存。验证要盯"逐份一致"——拿十份样本同时跑本地实现与云端实现,比对输出差异;第一次跑最容易暴露的就是编码与时区问题。

实践要点

  1. 同步调用有体积上限,小文件才合适。 请求体与响应体都有大小限制,base64 还会让体积再膨胀三分之一。几 MB 以内走同步最省事;更大的文件改成"上传对象存储 → 异步触发函数 → 回写结果",别硬塞。
  2. 编码是第一批必踩的坑。 CSV 带 BOM 时,列名会带一个不可见字符,读出来键名对不上。统一用 utf-8-sig 解码文本文件;二进制文件全程按字节处理,不要中途 decode。
  3. 批量调用要控并发。 本地串行慢,一口气几十个并发又容易撞函数并发配额,收到限流错误。稳妥做法是并发数固定为 4~8,遇到限流退避重试(等 1 秒、2 秒、4 秒)。
  4. 失败要有清单,不能只打印。 一批几百份总有几份失败,把失败的"文件名 + 错误 + request_id"写进单独的清单,处理后单独重跑。request_id 是事后对账的唯一线索。
  5. 输出按文件名幂等。 重试、重复执行是常态,输出命名不要带时间戳,直接用原文件名的 stem,重复跑只会覆盖不会堆出一堆副本。
  6. 敏感文件先想清楚传输路径。 内容 base64 进请求体、进日志,等于明文到处流。脱敏字段在函数里就删掉,日志只记文件名与大小,不记内容。
  7. 算清收益边界。 云端处理的价值是"环境一致、换机器不改代码",不是"更快"。单个文件本就几十毫秒的任务,本地跑更省;只有当依赖重、环境多、批量大时,这套流程才真正划算。

小结

这个方案没有什么高级技巧,它解决的是一个很朴素的问题:同一段处理逻辑,不应该因为换了台电脑就失效。把逻辑放进函数,把文件留在本地,批处理就变成了"读—调—写"三步。

对个人开发者,它还有个额外好处:一次部署,以后所有机器、所有同事都共用同一份处理能力,连环境说明文档都省了。小文件走同步调用,大文件走对象存储,想清楚这条分界线,函数工作流就是随手可用的云端工具台。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。