使用serverless function上传图片

2022-08-16344

代码

  最近试了试 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

评论