代码

最近试了试vercel的serverless function,用它实现上传图片到sm.ms图床,选择sm.ms的原因是它原生支持api上传,但不能跨域,所以serverless function的作用是代理api请求,绕过跨域。 serverless function的使用方式很简单,只需在项目根目录下新建一个/api文件夹,在里面写httpHandler就行了,支持多种语言,我这里还是使用Nodejs。创建/api/smms/upload.ts文件如下:

import fs from "fs";
import axios from "axios";
import FormData from "form-data";
import multiparty from "multiparty";
import type { VercelRequest, VercelResponse } from "@vercel/node";

export default async function (req: VercelRequest, res: VercelResponse) {
  if (req.method.toUpperCase() === "POST") {
    try {
      const form = new multiparty.Form();
      const data = await new Promise<{fields, files}>((resolve, reject) => {
        form.parse(req, function (err, fields, files) {
          if (err) { reject(err); }
          resolve({ fields, files });
        });
      });
      const token = data.fields.token[0];
      const file = data.files.file[0];
      const formData = new FormData();

      formData.append("smfile", fs.createReadStream(file.path), {
        knownLength: file.size,
        filepath: file.path,
        filename: file.originalFilename
      });
      const len = await new Promise((resolve, reject) => {
        formData.getLength((err, len) => {
          if (err) {
            reject(err);
            return;
          }
          resolve(len);
        });
      });
      const response = await axios({
        url: "https://sm.ms/api/v2/upload",
        method: "post",
        headers: {
          Authorization: token,
          "Content-Length": len.toString(),
          "Content-Type": `multipart/form-data; boundary=${formData.getBoundary()}`
        },
        data: formData
      });
      res.status(response.status).send(response.data);
    } catch (e) {
      res.status(503).send(e.toString());
    }
  } else {
    res.status(405).send("Post only!");
  }
}

解读

有三个部分:

  1. 通过multiparty解析req请求,提取到文件。
  2. 把文件构造进FormData
  3. 携带form表单,发送post请求。

需要注意的是:构造的form表单,文件字段要带上文件信息。header里必须有Content-Length