
Piclist Upload
- 82 installs
- 543 repo stars
- Updated August 5, 2026
- cat-xierluo/legal-skills
Upload local images referenced in Markdown to an image host via the PicList HTTP server and replace paths with cloud links.
About
Uploads local images referenced in Markdown files to a configured image host via the PicList HTTP server and replaces local paths with cloud links. A developer uses it to batch-process Markdown files or directories so images are accessible across devices.
- Uploads local Markdown images via PicList server
- Batch processes files or directories
Piclist Upload by the numbers
- 82 all-time installs (skills.sh)
- Ranked #886 of 2,715 Automation & Workflows skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/cat-xierluo/legal-skills --skill piclist-uploadAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 82 |
|---|---|
| repo stars | ★ 543 |
| Last updated | August 5, 2026 |
| Repository | cat-xierluo/legal-skills ↗ |
What it does
Upload local images referenced in Markdown to an image host via the PicList HTTP server and replace paths with cloud links.
Files
PicList 图片上传
将 Markdown 文件中的本地图片上传到配置的图床,并将本地路径替换为云端链接。
前置条件
- 已安装 PicList 并启用 HTTP Server
- 已在 PicList 中配置图床
- 已安装
jq - 已安装
curl
首次配置: 请参阅 references/setup.md 安装和配置指南。
⚠️ 强制规则:必须通过脚本执行
禁止手动 curl 上传。所有操作必须通过 `scripts/process.sh` 脚本执行。 脚本已内置上传、URL 替换和本地文件删除的完整逻辑,手动操作容易遗漏步骤。
默认行为是上传成功后删除本地图片。 只有当用户明确要求保留时,才可添加 --keep-local。不得擅自保留本地图片。
使用方法
处理文件或目录(默认删除本地图片)
bash scripts/process.sh --in-place <file.md|directory...>保留本地图片(仅在用户明确要求时使用)
bash scripts/process.sh --in-place --keep-local <file.md|directory...>预览模式(不修改文件)
bash scripts/process.sh --dry-run <file.md|directory...>命令选项
| 选项 | 说明 |
|---|---|
--in-place | 直接修改原文件(必须指定,否则仅输出到终端) |
--keep-local | 保留本地图片文件。仅在用户明确要求时使用 |
--dry-run | 预览模式,不上传、不修改文件 |
响应格式
解析上传返回的 JSON:
{"success":true,"result":["https://example.com/image.png"]}统计报告
处理完成后显示:
📊 Summary:
Total uploaded: 5
Total skipped: 3
Total failed: 0- Uploaded: 成功上传并替换(已删除本地图片)
- Skipped: 已是云端 URL(无需操作)
- Failed: 上传错误(保留原始路径)
错误处理
- PicList Server 未运行: 提示启动 PicList 应用
- 文件不存在: 跳过并显示 ⚠️ 警告,继续处理
- 上传失败: 保留原始路径,标记 ❌,继续
- 无效 JSON: 视为上传失败
配置
PicList Server 默认地址为 http://127.0.0.1:36677/upload。可通过环境变量覆盖:
export PICLIST_SERVER=http://127.0.0.1:PORT支持格式
png, jpg, jpeg, gif, webp, svg, bmp
变更日志
[1.2.0] - 2026-04-28
移除
- 移除 429 速率限制重试逻辑:经核实,429 错误来源于调用方(大模型)而非 PicList 上传 API,现已移除相关检测和重试机制
技术优化
- 简化
upload_image()函数,移除不必要的重试循环 - 删除
MAX_RETRY、RETRY_BACKOFF参数(原本专用于 429 重试)
---
[1.1.2] - 2026-04-10
修复
- `--dry-run` 语义修正:预览模式现在只显示待上传图片,不再实际上传或修改文件
文档完善
- 在
SKILL.md的前置条件中补充jq与curl - 将
--dry-run说明统一为“不上传、不修改文件”的预览模式
---
[1.1.1] - 2026-02-05
修复
- 修复 macOS 兼容性问题: macOS 默认
/bin/bash版本为 3.2,不支持关联数组declare -A - 将 shebang 改为
#!/usr/bin/env bash,优先使用较新的 bash(如 homebrew 安装的版本) - 修复图片匹配正则表达式问题: bash 内置正则表达式无法正确匹配复杂图片路径
- 改用
grep -o提取所有图片引用,更可靠且兼容性更好 - 使用字符串处理提取 alt 文本和路径,避免正则表达式转义问题
技术优化
- 使用
grep+ 字符串操作代替 bash 正则匹配,提高脚本兼容性 - 优化已处理路径的去重逻辑,避免重复上传同一图片
---
[1.1.0] - 2026-01-21
新增
- 自动删除本地图片: 上传成功后默认删除本地图片文件,节省存储空间
- `--keep-local` 选项: 支持保留本地图片文件,用户可选择是否删除
修复
- 修复脚本意外退出问题: 原脚本使用
set -e导致计数器为 0 时返回退出码 1,脚本被终止 - 改用
set -o pipefail+let命令进行计数,避免计数器导致脚本退出 - 修复一行多图片处理问题: 原脚本逐行处理,一行有多个
![]()时只处理第一个 - 改用正则循环 + 字符串替换,正确处理一行中的所有图片引用
技术优化
- 新增
delete_local_image()函数处理本地图片删除逻辑 - 使用关联数组
declare -A uploaded_files追踪已上传文件 - 优化图片匹配正则表达式,支持复杂 Markdown 场景
- 改进路径规范化处理,兼容更多路径格式
文档更新
- 更新 SKILL.md,补充
--keep-local选项说明 - 新增命令选项表格,清晰展示各选项功能
- 更新工作流程说明,明确默认删除本地图片的行为
---
[1.0.0] - 2026-01-09
新增
- 初始版本发布:PicList 图片上传技能,支持通过 PicList HTTP Server 接口上传 Markdown 中的本地图片到图床
- 支持单个/多个 Markdown 文件批量处理
- 支持目录扫描:自动发现并处理指定目录下的所有
.md文件 - 支持两种输出模式:
--dry-run:预览模式,显示上传结果但不修改原文件--in-place:替换模式,直接修改原文件,将本地路径替换为云端 URL- 智能过滤:自动跳过已经是云端链接(
http://、https://)的图片,避免重复上传 - 相对路径解析:自动将 Markdown 中的相对路径解析为绝对路径
- 统计报告:显示上传成功、跳过和失败的图片数量
- 错误处理:文件不存在或上传失败时保留原路径,继续处理其他图片
技术优化
- 使用 Bash 脚本实现图片上传和 Markdown 文件处理
- 通过 curl 调用 PicList HTTP Server API(默认端口 36677)
- 支持环境变量
PICLIST_SERVER自定义服务器地址 - 逐行解析 Markdown 文件,精确替换图片链接
- 保留原始 alt 文本描述
支持格式
- png, jpg, jpeg, gif, webp, svg, bmp
文档完善
- 添加 PicList 安装与配置指南(references/setup.md),包含:
- 各平台下载安装方法
- HTTP Server 启用步骤
- 主流图床配置示例(GitHub、阿里云 OSS、腾讯云 COS、SM.MS)
- 配置验证方法
- 更新 SKILL.md 前置条件,明确指向配置文档
改进
- 添加启动预检查:在处理前自动检测 PicList HTTP Server 连接状态
- 提供友好的错误提示:当服务不可用时,显示配置指南和下载地址
- 连接超时设置为 3 秒,快速反馈服务状态
MIT License
Copyright (c) 2025 杨卫薪律师(微信ywxlaw)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
PicList 安装与配置指南
什么是 PicList
PicList 是 PicGo 的持续维护版本,提供了更好的用户体验和更多功能。本项目使用 PicList 的内置 HTTP Server 功能来实现图片上传。
下载安装
macOS
# 通过 Homebrew 安装
brew install --cask piclist或直接下载:https://github.com/Kuingsmile/PicList/releases
Windows
下载安装包:https://github.com/Kuingsmile/PicList/releases
Linux
下载 AppImage 或解压包:https://github.com/Kuingsmile/PicList/releases
启用 HTTP Server
方法一:通过界面启用
1. 打开 PicList 应用 2. 点击左侧「设置」图标 3. 找到「插件设置」→「内置服务器」 4. 勾选「启动内置服务器」 5. 确认端口为 36677(默认)
方法二:通过配置文件
配置文件位置:
- macOS:
~/Library/Application Support/piclist/data.json - Windows:
%APPDATA%/piclist/data.json - Linux:
~/.config/piclist/data.json
添加以下配置:
{
"picBed": {
"current": "your-uploader",
"uploader": "your-uploader",
"transformer": "path"
},
"server": {
"port": 36677,
"host": "0.0.0.0",
"enable": true
}
}配置图床
PicList 支持多种图床服务:
GitHub
1. 进入「图床设置」→「GitHub图床」 2. 配置参数:
- 仓库名:
username/repo - 分支:
main或master - 路径:
images/(可选) - 自定义域名:
https://cdn.jsdelivr.net/gh/username/repo@main(可选,用于加速)
3. 设置 Token:
- 访问 https://github.com/settings/tokens
- 生成新 Token,勾选
repo权限 - 粘贴到 Token 字段
阿里云 OSS
1. 进入「图床设置」→「阿里云OSS图床」 2. 配置参数:
- KeyId: 阿里云 AccessKey ID
- KeySecret: 阿里云 AccessKey Secret
- 存储桶名: Bucket 名称
- 存储区域: 如
oss-cn-shanghai - 存储路径: 如
images/ - 自定义域名: 绑定的域名(可选)
腾讯云 COS
1. 进入「图床设置」→「腾讯云COS图床」 2. 配置参数:
- 版本: v5 或 v4
- SecretId: 腾讯云 SecretId
- SecretKey: 腾讯云 SecretKey
- 存储桶名: Bucket 名称
- 存储区域: 如
ap-shanghai - 存储路径: 如
images/ - 自定义域名: 绑定的域名(可选)
SM.MS
1. 进入「图床设置」→「SM.MS图床」 2. 配置参数:
- API密钥: 在 https://sm.ms/home/apitoken 获取
验证配置
测试 HTTP Server
curl http://127.0.0.1:36677/upload应返回 HTML 说明页面。
测试上传
curl -X POST "http://127.0.0.1:36677/upload" \
-F "file=@/path/to/test.png"应返回 JSON 格式的上传结果。
官方文档
- 项目地址:https://github.com/Kuingsmile/PicList
- 配置文档:https://piclist.cn/configure
- 常见问题:https://piclist.cn/faq
#!/usr/bin/env bash
# PicList Markdown image processor
# Usage: ./process.sh [--dry-run] [--keep-local] [--in-place] <file.md|directory...>
set -o pipefail
PICLIST_SERVER="${PICLIST_SERVER:-http://127.0.0.1:36677}"
DRY_RUN=false
IN_PLACE=false
KEEP_LOCAL=false
# Throttling parameters
UPLOAD_INTERVAL="${UPLOAD_INTERVAL:-0.5}"
BATCH_SIZE="${BATCH_SIZE:-20}"
BATCH_REST="${BATCH_REST:-3}"
# Global counters
TOTAL_UPLOADED=0
TOTAL_SKIPPED=0
TOTAL_FAILED=0
TOTAL_DIRS_REMOVED=0
TOTAL_SESSION_UPLOADED=0
# Track directories where files were deleted
declare -A deleted_dirs
# Parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
--dry-run)
DRY_RUN=true
shift
;;
--keep-local)
KEEP_LOCAL=true
shift
;;
--in-place)
IN_PLACE=true
shift
;;
-*)
echo "Unknown option: $1" >&2
exit 1
;;
*)
break
;;
esac
done
if [ $# -eq 0 ]; then
echo "Usage: $0 [--dry-run] [--keep-local] [--in-place] <file.md|directory...>" >&2
exit 1
fi
# Function to upload a single image
upload_image() {
local image_path="$1"
if [ ! -f "$image_path" ]; then
echo "⚠️ File not found: $image_path" >&2
return 1
fi
local response
response=$(curl -s -w "\n%{http_code}" -X POST "$PICLIST_SERVER/upload" -F "file=@$image_path" 2>/dev/null)
# Split response body and HTTP status code
local http_code
http_code=$(echo "$response" | tail -1)
local body
body=$(echo "$response" | sed '$d')
# Check for success
if echo "$body" | jq -e '.success == true' >/dev/null 2>&1; then
local url
url=$(echo "$body" | jq -r '.result[0]')
echo "$url"
return 0
else
echo "❌ Upload failed: $image_path" >&2
return 1
fi
}
# Function to delete local image file
delete_local_image() {
local image_path="$1"
if [ "$KEEP_LOCAL" = true ]; then
return 0
fi
if [ -f "$image_path" ]; then
local dir_path
dir_path="$(dirname "$image_path")"
rm -f "$image_path"
echo " 🗑️ Deleted: $image_path"
deleted_dirs["$dir_path"]=1
fi
}
# Function to process a single markdown file
process_markdown_file() {
local md_file="$1"
local temp_file="${md_file}.tmp"
local upload_count=0
local skip_count=0
local fail_count=0
declare -A uploaded_files # Track uploaded files to delete later
echo "Processing: $md_file"
# Read entire file content
local content
content=$(cat "$md_file")
# Process all image references using grep
local md_dir
md_dir="$(dirname "$md_file")"
# Extract all image references using grep
local images
images=$(grep -o '!\[[^]]*\]([^)]*)' "$md_file" 2>/dev/null || true)
# Process each unique image
local processed_paths=""
while IFS= read -r match; do
[ -z "$match" ] && continue
# Extract alt text and path
local alt_text="${match#*\[}"
alt_text="${alt_text%\]*}"
local image_path="${match#*\]}"
image_path="${image_path#[\(]}"
image_path="${image_path%\)}"
# Skip if already processed this path
if [[ "$processed_paths" =~ "|$image_path|" ]]; then
: $((skip_count++))
continue
fi
processed_paths="$processed_paths|$image_path|"
# Skip if already a URL
if [[ "$image_path" =~ ^https?:// ]]; then
: $((skip_count++))
continue
fi
# Resolve relative path
local full_path="$md_dir/$image_path"
# Normalize path
full_path=$(cd "$(dirname "$full_path")" 2>/dev/null && pwd)/$(basename "$full_path") 2>/dev/null || true
# Check if file exists
if [ ! -f "$full_path" ]; then
echo " ⚠️ File not found: $image_path" >&2
: $((fail_count++))
continue
fi
if [ "$DRY_RUN" = true ]; then
echo " 🔍 Would upload: $image_path"
: $((upload_count++))
continue
fi
# Upload image
echo " Uploading: $image_path..."
local new_url
new_url=$(upload_image "$full_path")
if [ -n "$new_url" ]; then
# Replace all occurrences in content
content="${content//"$match"/}"
: $((upload_count++))
# Track for deletion (use full_path as key)
uploaded_files["$full_path"]=1
# Delete local file immediately after successful upload
delete_local_image "$full_path"
# Throttle: basic interval after each upload
TOTAL_SESSION_UPLOADED=$((TOTAL_SESSION_UPLOADED + 1))
if [ "$(echo "$UPLOAD_INTERVAL > 0" | bc 2>/dev/null || echo 0)" = "1" ]; then
sleep "$UPLOAD_INTERVAL"
fi
# Throttle: batch rest after every BATCH_SIZE uploads
if [ $((TOTAL_SESSION_UPLOADED % BATCH_SIZE)) -eq 0 ]; then
echo " ⏸️ Batch rest ($BATCH_SIZE uploaded, pausing ${BATCH_REST}s)..."
sleep "$BATCH_REST"
fi
else
: $((fail_count++))
fi
done <<< "$images"
# Report results
if [ "$DRY_RUN" = true ]; then
echo " 🔍 Preview candidates: $upload_count, ⏭️ Skipped: $skip_count, ❌ Failed: $fail_count"
else
echo " ✅ Uploaded: $upload_count, ⏭️ Skipped: $skip_count, ❌ Failed: $fail_count"
fi
# Update global counters (use let to avoid set -e issues)
let "TOTAL_UPLOADED += upload_count" || true
let "TOTAL_SKIPPED += skip_count" || true
let "TOTAL_FAILED += fail_count" || true
# Write output
if [ "$DRY_RUN" = true ]; then
echo " 👀 Dry run complete: $md_file (未上传、未修改)"
elif [ "$IN_PLACE" = true ]; then
echo "$content" > "$temp_file"
mv "$temp_file" "$md_file"
echo " ✏️ File updated: $md_file"
else
echo "$content"
fi
}
# Function to check PicList Server availability
check_piclist_server() {
echo "🔍 检查 PicList HTTP Server..."
# Test if server is accessible
if ! curl -s --connect-timeout 3 "$PICLIST_SERVER/upload" >/dev/null 2>&1; then
echo "❌ 无法连接到 PicList HTTP Server"
echo
echo "请确保:"
echo " 1. PicList 应用正在运行"
echo " 2. HTTP Server 已启用(默认端口 36677)"
echo
echo "配置指南: references/setup.md"
echo "下载地址: https://github.com/Kuingsmile/PicList/releases"
echo
exit 1
fi
echo "✅ PicList Server 连接成功 ($PICLIST_SERVER)"
echo
}
# Function to collect markdown files from directories
collect_markdown_files() {
for target in "$@"; do
if [ -f "$target" ]; then
# Single file
if [[ "$target" =~ \.md$ ]]; then
echo "$target"
fi
elif [ -d "$target" ]; then
# Directory - find all .md files
find "$target" -type f -name "*.md" -print
fi
done
}
# Main execution
check_piclist_server
echo "🔍 Scanning for Markdown files..."
md_files=()
while IFS= read -r file; do
md_files+=("$file")
done < <(collect_markdown_files "$@")
if [ ${#md_files[@]} -eq 0 ]; then
echo "❌ No Markdown files found" >&2
exit 1
fi
echo "📝 Found ${#md_files[@]} Markdown file(s)"
echo
for md_file in "${md_files[@]}"; do
echo
process_markdown_file "$md_file"
done
# Clean up empty directories left after deleting images
if [ "$KEEP_LOCAL" = false ] && [ ${#deleted_dirs[@]} -gt 0 ]; then
for dir in "${!deleted_dirs[@]}"; do
if [ -d "$dir" ] && [ -z "$(ls -A "$dir" 2>/dev/null)" ]; then
rmdir "$dir"
echo " 🗑️ Removed empty dir: $dir"
: $((TOTAL_DIRS_REMOVED++))
fi
done
fi
echo
echo "📊 Summary:"
if [ "$DRY_RUN" = true ]; then
echo " Total preview candidates: $TOTAL_UPLOADED"
else
echo " Total uploaded: $TOTAL_UPLOADED"
fi
echo " Total skipped: $TOTAL_SKIPPED"
echo " Total failed: $TOTAL_FAILED"
if [ "$KEEP_LOCAL" = false ]; then
echo " Empty dirs removed: $TOTAL_DIRS_REMOVED"
fi