README file from
GithubR2 Media Sync
R2 Media Sync is an Obsidian desktop plugin that automatically uploads local media assets referenced in Markdown notes to Cloudflare R2, rewrites the Markdown links to public R2 URLs, and optionally deletes the local files.
It is designed for workflows where other tools create local image files inside your vault, such as PDF-to-Markdown converters, document importers, AI assistants, or batch import tools.
How It Differs From Other Image Upload Plugins
R2 Media Sync is not primarily a paste-image uploader.
Many Obsidian image upload plugins focus on the moment you paste, drag, or manually select an image. That workflow is already handled well by plugins such as EzImage.
R2 Media Sync focuses on the next problem: other tools may create image files directly inside your vault and insert local image links into Markdown. Common examples include:
- PDF-to-Markdown converters that extract page images into the note folder.
- Document importers that generate local assets.
- AI assistants or automation tools that write Markdown and image files directly.
- Batch migration workflows that leave notes full of local image references.
In those cases, paste/drag upload hooks never run. R2 Media Sync watches the resulting Markdown files, uploads the referenced local images to Cloudflare R2, rewrites the links in-place, and can remove the local image files after a successful upload.
In short:
- Use EzImage or a similar plugin for pasted/dragged images.
- Use R2 Media Sync for generated or imported local images that already exist in your vault.
- Use both together if you want pasted images and generated images to follow the same R2 storage strategy.
What It Does
- Watches newly created or modified Markdown files.
- Detects local image links:
![[image.png]]
- Uploads referenced image files to Cloudflare R2.
- Rewrites the note to use a public R2 URL.
- Optionally deletes the local image file after successful upload.
- Can read Cloudflare R2 settings from the EzImage plugin or use its own manual R2 settings.
Why
Plugins such as EzImage handle pasted or dragged images very well, but some Obsidian workflows create files directly in the vault and write local links into Markdown. For example, a PDF conversion plugin may create files like _page_3_Picture_2.jpeg and insert:

Those files can clutter the vault and consume sync storage. R2 Media Sync cleans up that class of generated assets automatically.
Features
- Configurable local cleanup after upload:
- move to Obsidian trash
- move to a review folder before manual cleanup
- Configurable scan scope:
- whole vault
- selected folders only
- Configurable excluded folders, such as
.obsidian,.git,Templates, or attachment folders. - Toggle support for Markdown image links and wiki image embeds separately.
- Use existing EzImage R2 settings or enter R2 credentials directly.
- Reuse previous uploads by file hash to avoid uploading identical image content again.
- Retry failed uploads before recording them as failed.
- Keep a local failed upload log for troubleshooting.
- View recent failed upload details in a modal.
- Show the latest sync state in the Obsidian status bar.
- Choose the plugin interface language: Auto, English, or Traditional Chinese.
- Manual command to scan the current note.
- Manual command to scan the configured scope.
- Manual command to import EzImage settings.
Safety Defaults
R2 Media Sync uses conservative defaults for first-time installs:
- It does not scan on startup by default.
- It does not delete local images by default.
- You can run a manual scan on the current note before enabling broader automation.
- If local cleanup is enabled, files can be moved through Obsidian's trash handling or moved to a review folder after upload and link rewrite.
- Failed uploads are recorded locally so you can inspect and retry later instead of losing track of partial failures.
After confirming your R2 settings, public URL, and scan scope, you can opt in to startup scans or local deletion from the plugin settings.
Privacy and Security
- No telemetry.
- No analytics.
- No remote service other than the Cloudflare R2 endpoint you configure.
- Credentials are stored locally in Obsidian plugin data when using manual mode.
- When using EzImage mode, this plugin reads the EzImage
data.jsonfile from your vault config folder locally and does not modify it. - Public Markdown links will contain your configured public R2 URL.
- Upload history and failed upload logs are stored locally in this plugin's data folder.
Vault Access
R2 Media Sync scans Markdown files so it can find local media references that were created by PDF converters, importers, AI tools, or other automation.
You can limit this behavior by:
- Choosing
Only included foldersinstead ofWhole vault. - Setting included folders such as
AI 工作區or a specific project folder. - Excluding folders such as
.obsidian,.git,.trash,Templates, or attachment folders.
The plugin only uploads image files that are referenced by Markdown notes and are inside the configured scan scope.
Requirements
- Obsidian desktop.
- A Cloudflare R2 bucket.
- A public R2 URL or custom public domain.
- R2 API credentials with permission to upload objects to the target bucket.
Installation
This plugin is not yet published to the Obsidian community plugin registry.
BRAT Installation
If you use the BRAT plugin:
- Install and enable BRAT.
- Run
BRAT: Add a beta plugin for testing. - Enter:
fab34/cloudflare-media-sync
- Enable
R2 Media Syncin Community plugins.
Manual Installation
Manual installation:
- Download or build the plugin files:
manifest.jsonmain.jsstyles.css
- Create this folder in your vault:
.obsidian/plugins/cloudflare-media-sync/
- Copy the three files into that folder.
- Restart Obsidian.
- Open
Settings -> Community plugins. - Enable
R2 Media Sync.
Configuration
Open Settings -> R2 Media Sync.
Language
Choose one:
Auto: follow your system/browser language when it is Traditional Chinese, otherwise English.EnglishTraditional Chinese
R2 Settings Source
Choose one:
Read from EzImage: reuse EzImage's R2 settings.Manual: enter Cloudflare R2 settings directly in this plugin.
Manual fields:
- Cloudflare account ID
- Access key ID
- Secret access key
- Bucket name
- Public URL
- Path template
Reliability
Reuse uploads by file hash: avoids uploading identical image content more than once by storing a local hash-to-URL history.Upload retry attempts: retries each R2 upload before the image is recorded in the failed upload log.
Local Cleanup
Local cleanup is disabled by default.
When enabled, choose one cleanup mode:
Move to Obsidian trash: uses Obsidian's file trash handling.Move to review folder: moves uploaded local files into a vault folder such as_synced_media_trash, preserving the original path under that folder so you can inspect before deleting.
Use Clear local review folder from the command palette when you are ready to move review-folder files to Obsidian trash.
Path Template
Default:
{yyyy}/{MM}/{timestamp}-{random}.{ext}
Supported tokens:
{yyyy}{MM}{dd}{hh}{mm}{ss}{timestamp}{random}{name}{ext}
Scan Scope
Use the whole vault, or limit processing to included folders.
Recommended exclusions:
.obsidian, .git, .trash, Templates
Recommended First Run
- Keep
Delete local image after uploaddisabled. - Keep
Scan on startupdisabled. - Open a note with one or two local test images.
- Run
R2 Media Sync: Upload local images in current note. - Confirm the note was rewritten to public R2 URLs.
- Enable local cleanup or startup scans only after the manual test behaves as expected.
Commands
Open the command palette and search for R2 Media Sync.
Upload local images in current noteScan configured scope nowImport settings from EzImageShow failed upload summaryClear failed upload logClear local review folder
Example
Before:

![[diagram.png]]
After:


Development
Install dependencies:
npm install
Build:
npm run build
Development watch:
npm run dev
Release Files
For a manual release, include:
manifest.jsonmain.jsstyles.css
Community Plugin Submission
This repository is structured for eventual submission to the Obsidian community plugin directory.
Before submitting:
- Publish a GitHub release whose tag exactly matches the version in
manifest.json, for example0.1.2. - Attach
manifest.json,main.js, andstyles.cssto that release. - Open a pull request to
obsidianmd/obsidian-releases.
See COMMUNITY_SUBMISSION.md for the suggested entry and checklist.
Notes
This plugin intentionally only processes image files that are referenced by Markdown notes. It does not upload unreferenced orphan images, because doing so could remove files that are still being staged or reviewed.
License
MIT — 歡迎自由使用、修改、散布。Issue 與 PR 都歡迎。