平时使用typora时,一般都会直接插入截图,但是在把md文档直接复制到博客时发现,由于截图的路径为本机地址,所以线上无法加载,于是需要配置图床以便于可以把截图直接上传到云存储中,获取外链,从而加载出图片
1. 配置去不图床(https://7bu.top)
我使用的是去不图床,一年10元可以用1G,比较划算,且配置方便,注册登录后,新建一个token

2. 配置typora
点击文件-偏好设置-图像,插入图片时选择上传图片,下面的上传服务设定改为自定义命令
3. 配置脚本
设置脚本名称为:Ctoolstypora-7bu-upload.ps1
param(
[Parameter(ValueFromRemainingArguments = $true)]
[string[]]$Files
)
$ErrorActionPreference = "Stop"
$Token = $env:QIBU_TOKEN
if ([string]::IsNullOrWhiteSpace($Token)) {
throw "请先设置环境变量 QIBU_TOKEN"
}
$UploadUrl = "https://7bu.top/api/v1/upload"
$AlbumId = "1957"
Add-Type -AssemblyName System.Net.Http
foreach ($File in $Files) {
if (-not (Test-Path -LiteralPath $File)) {
throw "文件不存在:$File"
}
$Client = [System.Net.Http.HttpClient]::new()
$Client.DefaultRequestHeaders.Authorization =
[System.Net.Http.Headers.AuthenticationHeaderValue]::new("Bearer", $Token)
$Form = [System.Net.Http.MultipartFormDataContent]::new()
$Stream = [System.IO.File]::OpenRead($File)
$Content = [System.Net.Http.StreamContent]::new($Stream)
$Content.Headers.ContentType =
[System.Net.Http.Headers.MediaTypeHeaderValue]::Parse("application/octet-stream")
$FileName = [System.IO.Path]::GetFileName($File)
$Form.Add($Content, "file", $FileName)
$Form.Add([System.Net.Http.StringContent]::new($AlbumId), "album_id")
try {
$Response = $Client.PostAsync($UploadUrl, $Form).Result
$Body = $Response.Content.ReadAsStringAsync().Result
if (-not $Response.IsSuccessStatusCode) {
throw "上传失败 HTTP $($Response.StatusCode):$Body"
}
$Json = $Body | ConvertFrom-Json
$ImageUrl = $Json.data.links.url
if ([string]::IsNullOrWhiteSpace($ImageUrl)) {
throw "没有从返回值里解析到图片 URL:$Body"
}
Write-Output $ImageUrl
}
finally {
if ($Stream) { $Stream.Dispose() }
if ($Client) { $Client.Dispose() }
}
}
4. 运行脚本
在typora中设置命令powershell.exe -NoProfile -ExecutionPolicy Bypass -File ""脚本地址""

验证图片上传选项即可
5. 优化措施
5.1 更改存储相册
如果想把截图放进特定相册,需要在脚本中增加两行代码
$UploadUrl = "https://7bu.top/api/v1/upload"
#在这一行中添加如下代码
#albumid可以在图床url中获取,例如:https://7bu.top/user/images?album_id=1957
$AlbumId = "xxx"
######
$Form.Add($Content, "file", $FileName)
#在这一行中添加如下代码
$Form.Add([System.Net.Http.StringContent]::new($AlbumId), "album_id")
5.2 增加上传速度
主要从 4 个方向优化:
- 压缩截图再上传
截图 PNG 往往很大。Typora 粘贴的截图如果是 PNG,上传会慢。
可以在脚本里先把 PNG 转成 JPG 或 WebP 再上传,体积通常能小很多,速度提升最明显。 - 脚本复用逻辑,减少开销
现在的 PowerShell 脚本每张图都会新建 HttpClient,可以改成脚本启动后只创建一次 HttpClient,多张图循环上传。
如果一次粘贴多张图,会快一些。 - 改用 PowerShell 7
Windows 自带的 Windows PowerShell 5.1 启动和网络处理都偏慢。
安装 PowerShell 7 后,把 Typora 命令改成:pwsh.exe -NoProfile -ExecutionPolicy Bypass -File "D:\Develop\Code\typora-cmd\typora-7bu-upload.ps1" - 换成 Node.js/Python 脚本
对 Typora Custom Command 来说,Node.js 脚本通常比 PowerShell 启动和 multipart 上传更利落。
如果追求“粘贴后尽快返回外链”,更推荐 Node.js 版本。
下述为node.js代码,同时需要把Typora 里的Custom Command 改成:node "D:xxx\xxx\typora-7bu-upload.js"
#!/usr/bin/env node
const fs = require("node:fs");
const path = require("node:path");
const token = process.env.QIBU_TOKEN;
const uploadUrl = "https://7bu.top/api/v1/upload";
// Optional: 在const fixedAlbumId = ""中设置你的相册id.
// 如果没设置则默认直接传入图床.
const fixedAlbumId = "";
const albumId = fixedAlbumId || process.env.QIBU_ALBUM_ID || "";
const files = process.argv.slice(2);
async function upload(file) {
if (!fs.existsSync(file)) {
throw new Error(`File does not exist: ${file}`);
}
const form = new FormData();
const blob = new Blob([await fs.promises.readFile(file)]);
form.append("file", blob, path.basename(file));
if (albumId) {
form.append("album_id", albumId);
}
const response = await fetch(uploadUrl, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
},
body: form,
});
const body = await response.text();
if (!response.ok) {
throw new Error(`Upload failed HTTP ${response.status}: ${body}`);
}
let json;
try {
json = JSON.parse(body);
} catch {
throw new Error(`Invalid JSON response: ${body}`);
}
const imageUrl = json?.data?.links?.url || json?.data?.url || json?.url;
if (!imageUrl) {
throw new Error(`Image URL not found in response: ${body}`);
}
return imageUrl;
}
async function main() {
if (!token) {
throw new Error("Please set the QIBU_TOKEN environment variable first.");
}
if (files.length === 0) {
throw new Error("No image files were passed by Typora.");
}
for (const file of files) {
console.log(await upload(file));
}
}
main().catch((error) => {
console.error(error.message);
process.exit(1);
});
