自建技术博客实战(三):工具专栏——地图定位、声音复刻与 rembg 抠图

一、工具专栏的设计思路

博客解决「读」,工具解决「用」。统一放在 /tools 下,但实现上分两类:

类型 示例 实现方式
纯前端 地图定位 浏览器直连 Mapbox,Token 用 NEXT_PUBLIC_*
需服务端 声音复刻、抠图 Next.js API 转发,密钥不落前端

共同约定:

  1. src/config/tools.ts 注册元数据;
  2. 页面壳:PageHeader + ToolsSubnav + glass-card
  3. 交互组件放 src/components/tools/*,业务逻辑放 src/lib/*

二、地图定位(Mapbox + 中文标注)

路径:/tools/map,组件 MapLocatorTool.tsx

2.1 能力

  • 输入经纬度或点击地图选点;
  • 标注 Marker、飞行定位;
  • 深浅色下切换 Mapbox 样式;
  • 使用 @mapbox/mapbox-gl-language 将标注改为 简体中文
typescript 复制代码
import MapboxLanguage from "@mapbox/mapbox-gl-language";

map.addControl(
  new MapboxLanguage({ defaultLanguage: "zh-Hans" }),
);

2.2 环境变量

bash 复制代码
NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN=pk.xxx

2.3 经纬度工具函数

src/lib/geo.ts 提供 DMS 与十进制度互转、范围校验,表单与地图联动,减少无效坐标。


三、声音复刻合成

路径:/tools/voice-clone,API:POST /api/tools/voice/synthesize

3.1 双服务商架构

用户在前端选择:

  • 阿里云百炼(默认) :DashScope 声音复刻 + qwen3-tts-vc 合成;
  • MiniMax 国内:上传参考音频 + 文本合成。
typescript 复制代码
// route.ts 伪代码
if (provider === "aliyun") {
  const { buffer, contentType } = await aliyunClone(common);
  return new NextResponse(buffer, { headers: { "Content-Type": contentType } });
}
const buffer = await minimaxClone({ ...common, groupId });

3.2 BYOK(用户自带密钥)

密钥由用户在页面输入,可选 localStorage 记忆;不落服务端磁盘 。也支持服务端 .env 默认 Key(个人部署用)。

src/lib/voice/storage.ts 统一管理多服务商凭证结构。

3.3 错误处理

自定义 VoiceServiceError,将厂商业务码与 HTTP 状态分离。例如 MiniMax 2038(无复刻权限)映射为 403 + 中文说明,避免误返回 502。

3.4 阿里云音频时长

百炼要求参考音频 5~60 秒 。前端 audio-utils.ts

  • 上传后检测时长;
  • 超过 60 秒自动截取前 30 秒并转 WAV;
  • 提交时确保 FormData 上传的是处理后文件。

避免接口返回 Audio duration exceeds maximum allowed limit


四、rembg 一键抠图

路径:/tools/rembg,API:POST /api/tools/rembg/remove

4.1 为何用 Python 服务

rembg 基于 ONNX 模型,生态在 Python。Next.js 侧采用 HTTP 代理

复制代码
浏览器 → /api/tools/rembg/remove → rembg s (127.0.0.1:7000) → 透明 PNG

.env.local

bash 复制代码
REMBG_SERVER_URL=http://127.0.0.1:7000

启动命令:

bash 复制代码
pip install "rembg[cpu,cli]"
rembg s --host 127.0.0.1 --port 7000 --no-ui
# 或 npm run rembg:server
# 或 docker compose up rembg -d

访问 http://127.0.0.1:7000/ 返回 {"detail":"Not Found"} 是正常的,API 在 /api/remove,文档在 /api

4.2 服务端逻辑

src/lib/rembg/remove.ts

  1. 优先 fetch rembg HTTP 服务;
  2. 连接失败时 降级为 rembg i CLI(若本机已安装);
  3. 超时默认 10 分钟REMBG_TIMEOUT_MS),首次会下载模型;
  4. 路由设置 export const maxDuration = 600

4.3 前端优化

  • 上传前 prepareImageForRembg():最长边缩至 1920px,减轻 CPU 压力;
  • 模型默认 u2netp(轻量);
  • 提交前调用 /api/tools/rembg/status 检测服务是否就绪。

4.4 常见问题

现象 原因 处理
503 无法连接 rembg 未启动 npm run rembg:server
504 超时 大图 / 首次下模型 缩小图片、选 u2netp、耐心等待
根路径 404 无首页路由 忽略,用网站 /tools/rembg

五、API 路由通用模式

工具类 API 统一约定:

typescript 复制代码
export const runtime = "nodejs";

export async function POST(request: Request) {
  try {
    const form = await request.formData();
    // 校验大小、MIME、字段
    const result = await libCall(...);
    return new NextResponse(result, {
      headers: { "Content-Type": "...", "Cache-Control": "no-store" },
    });
  } catch (err) {
    if (err instanceof XxxServiceError) {
      return NextResponse.json(
        { error: err.message, code: err.code },
        { status: mapStatus(err) },
      );
    }
    return NextResponse.json({ error: "服务器处理失败" }, { status: 500 });
  }
}

好处:前端只需 fetch + blob() / json(),错误信息对用户友好。


六、静态导出与工具共存

命令 博客 工具 API
npm run build
npm run build:static ✓ (out/) ×(API 被移走)

若部署到 Gitee Pages,工具页 UI 仍在,但抠图/语音需另备 Node 服务或外链 API。


七、系列回顾

  1. (一) 架构、主题、配置、双模式构建;Next.js 架构与深浅色主题设计
  2. (二) CSDN 迁移、Markdown、SSG、阅读体验;CSDN 批量迁移与 Markdown 渲染管线
  3. (三)本文 地图、语音、抠图三大工具的实现与踩坑。
相关推荐
朦胧之2 小时前
AI 编程-老项目改造篇
java·前端·后端
swipe5 小时前
从 0 到 1 实现大文件上传:分片、秒传、断点续传、暂停、重试与服务端合并
前端·javascript·面试
爱勇宝5 小时前
我做了一个只用来搜歌词的小 App
android·前端·后端
甲维斯5 小时前
用AI还原《坦克大战》并3D化升级!
前端·人工智能·游戏开发
IT_陈寒6 小时前
SpringBoot自动配置坑了我一晚上,原来问题出在这
前端·人工智能·后端
kyriewen6 小时前
AI 生成的代码能跑就行?这 5 个坑迟早炸
前端·javascript·ai编程
谷子在生长7 小时前
纯血鸿蒙自定义弹窗最佳实践:从「到处复制」到「一行调用」
前端·harmonyos
壹方秘境7 小时前
我用Go语言开发了一个跨平台的HTTPS抓包和调试工具
前端·后端·ios
神秘面具男7 小时前
HarmonyOS 6.0跨端远程控制
前端·后端
枫树下x7 小时前
NestJS基础框架
前端