代码
最近试了试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!");
}
}
解读
有三个部分:
- 通过
multiparty
解析req请求,提取到文件。 - 把文件构造进
FormData
。 - 携带form表单,发送post请求。
需要注意的是:构造的form表单,文件字段要带上文件信息。header里必须有Content-Length。