Upload local images in Markdown files to the configured image hosting service, and replace local paths with cloud links.
Prerequisites
- PicList is installed and HTTP Server is enabled
- An image hosting service has been configured in PicList
- Installed
jq - Installed
curl
System Compatibility: The script is compatible with bash 3.2+ (built into macOS /bin/bash is directly usable, no need to install a newer bash), Linux distributions, and Git Bash (Windows).lsof When missing, automatically fall back to /dev/tcp port detection, no need for bc。
Initial Configuration: Please refer to references/setup.md installation and configuration guide.
⚠️ Mandatory Rule: Must Be Executed via Script
Manual curl uploads are prohibited. All operations must be executed via the scripts/process.sh script. The script has built-in complete logic for uploading, URL replacement, and local file deletion; manual operations are prone to missing steps.
The default behavior is to delete local images after a successful upload. Only when the user explicitly requests to keep them may --keep-localbe added. Local images must not be kept without authorization.
Usage
Process a file or directory (delete local images by default)
bash scripts/process.sh --in-place <file.md|directory...>
Keep local images (use only when the user explicitly requests it)
bash scripts/process.sh --in-place --keep-local <file.md|directory...>
Preview mode (do not modify files)
bash scripts/process.sh --dry-run <file.md|directory...>
Command Options
| Option | Description |
|---|---|
--in-place |
Directly modify the original file (must be specified, otherwise only output to the terminal) |
--keep-local |
Keep local image files.Use only when the user explicitly requests it |
--dry-run |
Preview mode, no upload, no file modification |
Response Format
Parse the JSON returned by the upload:
{"success":true,"result":["https://example.com/image.png"]}
Statistics Report
Displayed after processing is complete:
📊 Summary:
Total uploaded: 5
Total skipped: 3
Total failed: 0
- Uploaded: Successfully uploaded and replaced (local image deleted)
- Skipped: Already a cloud URL (no action needed)
- Failed: Upload error (original path retained)
Resumable Processing
--in-place In this mode, after each image is successfully uploaded and before the local file is deleted, the script immediately writes the already-replaced md content back to disk atomically. Therefore, if interrupted midway (timeout, crash, Ctrl-C), the already-uploaded URLs will not be lost: the completed parts are already cloud links in the md, and the incomplete parts remain local references,simply rerun the same command to resume(idempotent).
Error Handling
- PicList process is not running: The script will first perform port-level detection; if missing, it will automatically try
open -a PicList.app(macOS only) and wait up to 15 seconds (PICLIST_START_WAIT); it exits only if there is still no response. - PicList is listening but the business endpoint returns 503/000: This is usually because the PicList application is stuck or the image hosting backend is not configured. The script will report an error and prompt the user to check the default image hosting service/token in the PicList application, and restart the application if necessary.
- File does not exist: Skip and display a ⚠️ warning, continue processing
- Single upload failed: automatically retry once (
MAX_RETRIES=1, intervalRETRY_DELAY=2s); if it still fails, keep the original path, mark ❌, and continue - Invalid JSON: treated as upload failure
Configuration
The default address of PicList Server is http://127.0.0.1:36677/upload. It can be overridden via environment variables:
export PICLIST_SERVER=http://127.0.0.1:PORT
# 以下为可选调优(一般无需改动)
export PICLIST_START_WAIT=15 # 启动 PicList 后等待端口就绪的秒数
export MAX_RETRIES=1 # 单图上传失败时的额外重试次数
export RETRY_DELAY=2 # 重试间隔秒数
Supported formats
png, jpg, jpeg, gif, webp, svg, bmp
Supported reference syntaxes
| Syntax | Example | Processing |
|---|---|---|
| Plain path | 、 |
Parsed as-is (since v1.0.0) |
| Angle-bracket path | .png>) |
Strip angle brackets before parsing |
| Obsidian file:// |  |
Strip angle brackets and file://,file://<host>/ form removes the hostname; percent-encoding inside the URL (%20 , etc.) is decoded to the real path (since v1.5.0) |
Literal %in a plain path (such as 50%.png) is not decoded and remains as-is.