调用 API 前必须先获取 API Key

所有 API 请求均需通过 X-API-Key 请求头进行身份认证。没有有效的 API Key,任何端点都无法调用。

环境要求

  • Node.js 18+
  • image-size 库(获取图片尺寸)

安装依赖

npm install image-size

完整示例代码

import fs from "node:fs";
import path from "node:path";
import sizeOf from "image-size";

const API_KEY = process.env.IMGROUTE_API_KEY || "sk-ir-your-key-here";
const BASE_URL = "https://imgroute.com/api/v1";

async function getImageInfo(filePath) {
  const stats = fs.statSync(filePath);
  const dimensions = sizeOf(filePath);
  const name = path.basename(filePath);
  const ext = path.extname(filePath).slice(1).toLowerCase();
  const mimeTypes = { jpg: "image/jpeg", jpeg: "image/jpeg", png: "image/png", gif: "image/gif", webp: "image/webp" };
  return { name, size: stats.size, width: dimensions.width, height: dimensions.height, mimeType: mimeTypes[ext] || "image/jpeg" };
}

async function uploadImage(filePath, albumId) {
  const info = await getImageInfo(filePath);

  const presignResponse = await fetch(BASE_URL + "/upload", {
    method: "POST",
    headers: { "X-API-Key": API_KEY, "Content-Type": "application/json" },
    body: JSON.stringify({
      file_name: info.name,
      file_size: info.size,
      mime_type: info.mimeType,
      width: info.width,
      height: info.height,
      ...(albumId && { album_id: albumId })
    })
  });

  const presignData = await presignResponse.json();
  if (!presignData.success) throw new Error("预签名失败: " + presignData.error);

  const { upload_url, cdn_url } = presignData.data;

  const fileBuffer = fs.readFileSync(filePath);
  const formData = new FormData();
  formData.append("file", new Blob([fileBuffer], { type: info.mimeType }), info.name);

  const uploadResponse = await fetch(upload_url, { method: "POST", body: formData });
  const uploadData = await uploadResponse.json();
  if (!uploadData.success) throw new Error("上传失败: " + uploadData.error);

  return cdn_url;
}

async function main() {
  try {
    const cdnUrl = await uploadImage("./cat.jpg");
    console.log("上传成功: " + cdnUrl);
  } catch (error) {
    console.error("上传失败: " + error);
  }
}

main();

使用 axios 的示例

import axios from "axios";
import fs from "node:fs";
import sizeOf from "image-size";

const API_KEY = process.env.IMGROUTE_API_KEY;
const BASE_URL = "https://imgroute.com/api/v1";

async function uploadImage(filePath) {
  const stats = fs.statSync(filePath);
  const dimensions = sizeOf(filePath);
  const name = path.basename(filePath);

  const presign = await axios.post(BASE_URL + "/upload", {
    file_name: name,
    file_size: stats.size,
    mime_type: "image/jpeg",
    width: dimensions.width,
    height: dimensions.height
  }, { headers: { "X-API-Key": API_KEY, "Content-Type": "application/json" } });

  const { upload_url, cdn_url } = presign.data.data;

  const fileStream = fs.createReadStream(filePath);
  await axios.post(upload_url, fileStream, { headers: { "Content-Type": "image/jpeg" } });

  return cdn_url;
}

批量上传示例

import fs from "node:fs";
import path from "node:path";

async function batchUpload(directory) {
  const files = fs.readdirSync(directory).filter(f => [".jpg", ".jpeg", ".png"].some(ext => f.endsWith(ext)));
  const results = [];
  for (const file of files) {
    try {
      const url = await uploadImage(path.join(directory, file));
      results.push({ file, url, status: "success" });
    } catch (error) {
      results.push({ file, error: String(error), status: "failed" });
    }
  }
  return results;
}

const results = await batchUpload("./images");
results.forEach(r => console.log(r.status === "success" ? "[OK] " + r.file + ": " + r.url : "[FAIL] " + r.file + ": " + r.error));

常见问题

Q: image-size 获取 SVG 尺寸失败?

SVG 等特殊格式可能需要特殊处理:

import { readFileSync } from "node:fs";

function getSvgSize(filePath) {
  const content = readFileSync(filePath, "utf-8");
  const match = content.match(/width="(\d+)" height="(\d+)"/);
  if (match) return { width: parseInt(match[1]), height: parseInt(match[2]) };
  throw new Error("无法解析 SVG 尺寸");
}

Q: 如何处理大文件上传?

使用流式上传避免内存问题:

async function uploadLargeFile(filePath) {
  const fileStream = fs.createReadStream(filePath);
  const formData = new FormData();
  formData.append("file", fileStream);
  const response = await fetch(upload_url, { method: "POST", body: formData });
  return response.json();
}