LskyPro使用Picgo上传图片
- 安装 Releases · Molunerfinn/PicGo (github.com) 2.3.0 版本。
- 启动 PicGo 并安装 lankong 插件,可以直接搜索安装。

- 修改插件源码 index.js
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const https_1 = __importDefault(require("https"));
const UPLOADER = 'lankong';
module.exports = (ctx) => {
const register = () => {
ctx.helper.uploader.register(UPLOADER, {
handle,
name: UPLOADER,
config: config
});
// 注册同步删除 remove 事件
ctx.on('remove', onDelete);
};
async function onDelete(files, guiApi) {
let userConfig = ctx.getConfig('picBed.' + UPLOADER);
const syncDelete = userConfig.syncDelete;
// 如果同步删除按钮关闭则不执行
if (!syncDelete) {
return;
}
const VersionId = userConfig.lskyProVersion;
switch (VersionId) {
case 'version1':
{
ctx.emit('notification', {
title: `V1版本的兰空图床不支持同步删除, 请关闭同步删除选项以抑制此通知`
});
return;
}; break;
case 'version2':
{
}; break;
case 'version3':
{
}; break;
}
const deleteList = files.filter(each => each.type === UPLOADER);
if (deleteList.length === 0) {
return;
}
for (let i = 0; i < deleteList.length; i++) {
const item = deleteList[i];
const deleteParam = GenDeleteParam(item, userConfig);
let body = await ctx.Request.request(deleteParam);
body = JSON.parse(body);
if (body.status === true) {
ctx.emit('notification', {
title: `${item.imgUrl} 同步删除成功`,
body: body.message
});
}
else {
ctx.emit('notification', {
title: `${item.imgUrl} 同步删除失败`,
body: body.message
});
throw new Error(body.message);
}
}
}
const GenDeleteParam = (img, userConfig) => {
const token = userConfig.token;
const ignoreCertErr = userConfig.ignoreCertErr;
const serverUrl = userConfig.server;
const currentImageKey = img.id;
const v2Headers = {
'Accept': 'application/json',
'User-Agent': 'PicGo',
'Connection': 'keep-alive',
'Authorization': token || undefined
};
// 如果忽略证书错误开关打开则带上 http agent 访问, 否则不需要带(以提高性能)
if (ignoreCertErr) {
let requestAgent = new https_1.default.Agent({
// 此处需要取反 忽略证书错误 拒绝未授权证书选项
rejectUnauthorized: !ignoreCertErr
});
return {
method: 'DELETE',
url: `${serverUrl}/api/v1/images/${currentImageKey}`,
agent: requestAgent,
headers: v2Headers
};
}
else {
return {
method: 'DELETE',
url: `${serverUrl}/api/v1/images/${currentImageKey}`,
headers: v2Headers
};
}
};
// 图片上传
const postOptions = (userConfig, fileName, image) => {
const serverUrl = userConfig.server;
if (serverUrl.endsWith("/")) {
throw new Error("Server url cannot ends with /");
}
const VersionId = userConfig.lskyProVersion;
const token = userConfig.token;
const ignoreCertErr = userConfig.ignoreCertErr;
// LskyProV1需要的配置
const v1Headers = {
'Content-Type': 'multipart/form-data',
'User-Agent': 'PicGo',
'Connection': 'keep-alive',
'token': token || undefined
};
const v1FormData = {
image: {
value: image,
options: {
filename: fileName
}
},
ssl: 'true'
};
// LskyProV2需要的配置
const v2Headers = {
'Content-Type': 'multipart/form-data',
'User-Agent': 'PicGo',
'Connection': 'keep-alive',
'Accept': 'application/json',
'Authorization': token || undefined
};
const strategyId = userConfig.strategyId;
const albumId = userConfig.albumId;
let permission = userConfig.permission.value;
if (permission === undefined) {
permission = userConfig.permission;
}
const v2FormData = {
file: {
value: image,
options: {
filename: fileName
}
},
ssl: 'true',
strategy_id: strategyId,
album_id: albumId,
permission: permission
};
// V2版本情况下, 如果用户没有填写策略ID, 删除 v2FormData 中的 key: strategy_id
if (!strategyId) {
delete v2FormData.strategy_id;
}
if (!albumId) {
delete v2FormData.album_id;
}
if (!(permission === 0 || permission === 1)) {
delete v2FormData.permission;
}
let api_url = `${serverUrl}/api/upload`;
let Headers = v1Headers;
let FormData = v1FormData;
switch (VersionId) {
case 'version1':
{
api_url = `${serverUrl}/api/upload`;
Headers = v1Headers;
FormData = v1FormData
} break;
case 'version2':
{
api_url = `${serverUrl}/api/v1/upload`;
Headers = v2Headers;
FormData = v2FormData
} break;
case 'version3':
{
api_url = `${serverUrl}/api/v1/upload`;
Headers = v2Headers;
FormData = v2FormData
} break;
}
// 如果忽略证书错误开关打开则带上 http agent 访问, 否则不需要带(以提高性能)
if (ignoreCertErr) {
let requestAgent = new https_1.default.Agent({
// 此处需要取反 忽略证书错误 拒绝未授权证书选项
rejectUnauthorized: !ignoreCertErr
});
return {
method: 'POST',
url: api_url,
agent: requestAgent,
headers: Headers,
formData: FormData
};
}
else {
return {
method: 'POST',
url: api_url,
headers: Headers,
formData: FormData
};
}
};
const handle = async (ctx) => {
let userConfig = ctx.getConfig('picBed.' + UPLOADER);
if (!userConfig) {
throw new Error('Can\'t find uploader config');
}
const imgList = ctx.output;
for (let i in imgList) {
let image = imgList[i].buffer;
if (!image && imgList[i].base64Image) {
image = Buffer.from(imgList[i].base64Image, 'base64');
}
const postConfig = postOptions(userConfig, imgList[i].fileName, image);
let body = await ctx.Request.request(postConfig);
body = JSON.parse(body);
let VersionId = userConfig.lskyProVersion;
switch (VersionId) {
case 'version1':
{
let condition = (body.code === 200);
if (condition) {
delete imgList[i].base64Image;
delete imgList[i].buffer;
// 图片链接
imgList[i]['imgUrl'] = body.data.url;
}
else {
ctx.emit('notification', {
title: 'upload failed',
body: body.message
});
throw new Error(body.message);
}
} break;
case 'version2':
{
let condition = (body.status === true);
if (condition) {
delete imgList[i].base64Image;
delete imgList[i].buffer;
// 图片链接
imgList[i]['imgUrl'] = body.data.links.url;
// 图片唯一密钥
imgList[i]['id'] = body.data.key;
}
else {
ctx.emit('notification', {
title: 'upload failed',
body: body.message
});
throw new Error(body.message);
}
} break;
case 'version3':
{
let condition = (body.status === true);
if (condition) {
delete imgList[i].base64Image;
delete imgList[i].buffer;
// 图片链接
imgList[i]['imgUrl'] = body.data.links.url;
// 图片唯一密钥
imgList[i]['id'] = body.data.key;
}
else {
ctx.emit('notification', {
title: 'upload failed',
body: body.message
});
throw new Error(body.message);
}
} break;
}
}
return ctx;
};
const config = ctx => {
let userConfig = ctx.getConfig('picBed.' + UPLOADER);
if (!userConfig) {
userConfig = {};
}
return [
{
name: 'lskyProVersion',
type: 'list',
default: userConfig.lskyProVersion || 'version1',
message: 'Choose a version',
choices: [
{
name: 'LskyProV1',
value: 'version1'
},
{
name: 'LskyProV2',
value: 'version2'
},
{
name: 'LskyProPlusV1',
value: 'version3'
}
],
required: true,
alias: 'Lsky Pro Version'
},
{
name: 'server',
type: 'input',
default: userConfig.server,
required: true,
message: '示例: https://example.com',
alias: 'Server'
},
{
name: 'token',
type: 'input',
default: userConfig.token,
required: true,
message: '认证 token 信息',
alias: 'Auth token'
},
{
name: 'strategyId',
type: 'input',
default: userConfig.strategyId,
required: false,
message: '选填, V1以及V2使用默认存储策略时请留空',
alias: 'Strategy ID'
},
{
name: 'albumId',
type: 'input',
default: userConfig.albumId,
required: false,
message: '选填, V2生效',
alias: 'Album ID'
},
{
name: 'permission',
type: 'list',
default: userConfig.permission || 'private(default)',
message: 'set permission',
choices: [
{
name: 'private(default)',
value: 0
},
{
name: 'public',
value: 1
}
],
required: false,
alias: 'Permission'
},
{
name: 'ignoreCertErr',
type: 'confirm',
default: userConfig.ignoreCertErr || false,
message: '是否忽略证书错误, 如果上传失败提示证书过期请设为true',
required: true,
alias: 'Ignore certificate error'
},
{
name: 'syncDelete',
type: 'confirm',
default: userConfig.syncDelete || false,
message: '是否同步删除, 只支持V2',
required: true,
alias: 'Sync Delete'
}
];
};
return {
uploader: UPLOADER,
config: config,
register
// guiMenu
};
};
- 重启PicGo,然后正常配置即可。

- 然后配合 Obsidian 写文章就可以复制粘贴实现自动上传图片了。
Picgo添加图片压缩插件
一般而言直接上传的图片一般比较大,而Picgo的一款插件 picgo-plugin-compress 则可以实现图片自动压缩后上传。
- Picgo插件中搜索 compress 并安装。

- 把 compress 插件更新一下。
- 配置 compress。
- tinypng 无损压缩,需要上传到 tinypng。
- imagemin 压缩过程不需要经过网络,但是图片会有损耗。
- image2webp 本地有损压缩,支持 GIF 格式有损压缩 注意:有些图床(比如 sm.ms)不支持 webp 图片格式,会上传失败。
类型 | tinypng | imagemin | image2webp |
---|---|---|---|
原大小 | 1.5 MB | 1.5 MB | 1.5 MB |
压缩后 | 315 KB | 411 KB | 216 KB |
- 这里我们选择 tinypng 在线压缩方式。
- 去tinypng申请一个API账号,然后填写自己的KEY后重启即可。

- 去 tinypng 官网看下使用次数是否增加以确认压缩是否成功。

- 去自己的图床查看图片是否上传成功。