Skip to content
| Marketplace
Sign in
Visual Studio Code>Other>EzImageNew to Visual Studio Code? Get it now.
EzImage

EzImage

kiang

|
3 installs
| (0) | Free
The ultimate effortless multi-platform image uploader for VS Code. Support R2, S3 and more.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info
EzImage Logo

EzImage

A Minimalist, Efficient Multi-Platform Image Uploader for VS Code & AI-first IDEs

Version Platform IDEs License

English | 简体中文

Features • Supporting IDEs • Installation • Configuration • Hotkeys • Troubleshooting • Languages • Roadmap • Feedback


EzImage is a image uploader tool deeply customized for VS Code. It is designed to solve the pain points of "hard to save, annoying to paste" in Markdown creation, helping you upload images and insert Markdown links in the most natural way (clipboard paste, drag and drop, context menu).

⚠️ Compression not working or upload looks broken? Check the 🔧 Troubleshooting section — most reported issues (sharp failing to load, public URL 404, missing R2 fields) have a known fix there.

✨ Features

  • 🚀 Multi-Platform Support: Pluggable architecture, starting with Cloudflare R2, with AWS S3, Aliyun OSS, and Tencent COS coming soon.
  • 📸 Ultimate Pasting Experience:
    • Clipboard Upload: Press Cmd+Alt+V (Mac) or Ctrl+Alt+V (Win/Linux) to instantly upload and insert.
    • Smooth Drag & Drop: Drag images directly from your folder into the editor for automatic uploading.
  • 🔄 Local Images → Cloud URLs: Right-click → EzImage: Convert Local Image Paths to URLs to scan every ![alt](https://github.com/keepwonder/ezimage/raw/HEAD/local.png) in the current Markdown document, upload them to your bucket, and replace the path in place — all in one atomic undo step.
  • 📉 Intelligent Image Engine: Powered by the industrial-grade sharp engine.
    • Automatically convert images to WebP for maximum compression while maintaining quality.
    • Supports automatic resizing (Max Width) and quality control.
    • Zero-config setup: on first use, EzImage detects whether sharp is available and offers a one-click install of the native binary that matches your OS and Node.js ABI. If you prefer to skip compression, just toggle ezimage.compress off.
  • 📂 Flexible Naming: Support rich template variables like {yyyy}/{MM}/{timestamp}-{random}.{ext} to avoid file name conflicts.
  • 📋 Deep Integration: Provides editor context menus for a natural workflow.

🚀 Multi-IDE Support

EzImage not only supports standard VS Code, but also perfectly adapts to current mainstream AI code editors:

  • Antigravity: Deeply adapted, the preferred environment for AI-assisted development.
  • Cursor: Native compatibility with excellent performance.
  • Windsurf: Full feature support.
  • Trae: Perfectly compatible, powered by ByteDance.
  • VSCodium: Also works for the open-source version of VS Code.

📦 Installation

Method A: Install from VSIX (Recommended)

  1. Download the latest .vsix file from GitHub Releases. Look for the file named ezimage-X.Y.Z.vsix under the latest release.
  2. Press Cmd+Shift+P (Mac) / Ctrl+Shift+P (Win) in your editor and search for Install from VSIX.
  3. Or use the command line, replacing ezimage-X.Y.Z.vsix with the actual filename you downloaded:
IDE / Environment Command
VS Code code --install-extension ezimage-X.Y.Z.vsix
Antigravity antigravity --install-extension ezimage-X.Y.Z.vsix
Cursor cursor --install-extension ezimage-X.Y.Z.vsix
Windsurf windsurf --install-extension ezimage-X.Y.Z.vsix
Trae trae --install-extension ezimage-X.Y.Z.vsix

Method B: From Marketplace

  1. Search for EzImage in the extension marketplace and install it with one click.
  2. Or visit the VS Code Marketplace to install.

⚙️ Configuration

After installation, follow these steps to configure:

  1. Press Cmd+Shift+P (Mac) / Ctrl+Shift+P (Win) to open the command palette.
  2. Search and run EzImage: Open Settings.
  3. Configure your storage service (e.g., Cloudflare R2):
    • Provider: r2
    • Account ID: Your Cloudflare Account ID
    • Bucket Name: Your storage bucket name
    • Access Key ID / Secret Access Key: R2 API credentials
    • Public URL: Your bucket's public distribution URL

All settings

Settings are listed in the order they appear in VS Code's Settings panel, which follows an explicit order property in package.json. Within each group, the read order matches the most common user workflow.

Storage — where uploaded images go | Key | Default | Description | | :--- | :--- | :--- | | ezimage.provider | r2 | Storage provider. Currently only r2 is wired in; S3/OSS/COS are roadmap items. | | ezimage.r2.accountId | "" | Cloudflare Account ID. | | ezimage.r2.accessKeyId | "" | R2 access key ID. | | ezimage.r2.secretAccessKey | "" | R2 secret access key. | | ezimage.r2.bucketName | "" | R2 bucket name. | | ezimage.r2.publicUrl | "" | Public distribution URL, e.g. https://pub-xxxx.r2.dev. | | ezimage.pathTemplate | {yyyy}/{MM}/{timestamp}-{random}.{ext} | Path template for uploaded objects. Available variables: {yyyy} {MM} {dd} {hh} {mm} {ss} {timestamp} {random} {name} {ext}. |

Compression — what happens to images before upload | Key | Default | Description | | :--- | :--- | :--- | | ezimage.compress | true | Enable WebP compression before upload. | | ezimage.maxWidth | 1920 | Maximum width in pixels; images wider than this are downscaled before encoding. Set to 0 to keep original dimensions. | | ezimage.quality | 85 | WebP quality (1–100). Higher = larger files, better fidelity. | | ezimage.autoInstallSharp | true | When sharp is missing, prompt to install the native binary on first use. Requires Node.js ≥ 18.17 and npm on PATH. | | ezimage.disableCompressionNotice | false | Suppress the warning shown when the sharp compression engine is unavailable. |

Insert format — how the snippet appears in your Markdown | Key | Default | Description | | :--- | :--- | :--- | | ezimage.insertFormat | markdown | How to render the inserted snippet. One of markdown / html-wrap / html-figure / custom. See Insert format templates. | | ezimage.insertWidth | 100% | Width attribute used in HTML templates ({width}). e.g. 65%, 600px, auto. Empty string omits the attribute. | | ezimage.insertAlign | none | Alignment for HTML templates. none skips the wrapper <div>. | | ezimage.insertCustomTemplate | "" | Custom template used when insertFormat = custom. Variables: {url} {filename} {name} {ext} {width} {align} {alt}. | | ezimage.insertIncludeName | true | Use the source filename as alt text / figcaption. |

Language — display language of prompts and info messages | Key | Default | Description | | :--- | :--- | :--- | | ezimage.language | auto | Display language for EzImage prompts, errors, and info messages. auto follows VS Code's display language. Settings panel labels themselves always follow VS Code (not this setting). |

Insert format templates

Three built-in presets cover the most common shapes:

Format With insertAlign = center, insertWidth = 100% insertAlign = none
markdown ![photo](https://pub.example.com/2026/09/photo-abc123.webp) (same)
html-wrap <div align="center"><img src="…" alt="photo" width="100%"></div> <img src="…" alt="photo" width="100%"> (no wrapper)
html-figure <figure><img src="…" alt="photo" width="100%"><figcaption>photo</figcaption></figure> (same — figure ignores insertAlign)

For full control, set ezimage.insertFormat = custom and define ezimage.insertCustomTemplate with any combination of variables:

Variable Resolves to
{url} Public R2 URL after upload
{filename} Full filename including extension, e.g. photo.png
{name} Filename without extension, e.g. photo
{ext} Extension without dot, e.g. png
{width} ezimage.insertWidth value (empty → no width attribute)
{align} ezimage.insertAlign value
{alt} Alt text (filename, or insertCustomAlt when set)

Unknown placeholders are left in place so a typo in your template stays visible instead of being silently dropped.

One-shot override: the command EzImage: Upload Clipboard Image As… lets you pick a different format for a single upload without changing the default. Bind it to a shortcut via File → Preferences → Keyboard Shortcuts → search "ezimage.uploadClipboardAs" if you want a dedicated key (e.g. Ctrl+Alt+Shift+V) for HTML-centered inserts.

R2 setup walkthrough

For a step-by-step guide with screenshots covering Cloudflare R2 token creation, bucket configuration, and connecting it to EzImage, see 📘 Cloudflare R2 Setup Guide.

For path template variables in detail, see 📝 Path Variables Manual.

🔄 Local Images → Cloud URLs

When you've been drafting locally and want to publish, you don't have to upload each image by hand. EzImage can scan your Markdown file, upload every local image to the bucket, and rewrite the references in place — all in one go.

Quick start

  1. Open Settings (Cmd+, / Ctrl+,) → search for EzImage.
  2. Enable ezimage.localImageUpload.enabled. This adds two entries to the editor right-click menu under Modification:
    • EzImage: Convert Local Image Paths to URLs — scans the entire file.
    • EzImage: Convert Local Image Paths in Selection — scans only the current selection (appears only when something is selected).
  3. Right-click anywhere in a Markdown file → EzImage: Convert Local Image Paths to URLs.
  4. EzImage finds every ![alt](https://github.com/keepwonder/ezimage/raw/HEAD/path.png), shows a confirmation like "Found 5 local image(s) (2 skipped). Upload and replace in-place?", then uploads serially with a progress notification.
  5. The whole conversion lands as one atomic edit — press Cmd+Z once to roll it all back.

What gets scanned

  • ✅ Markdown inline images: ![alt](https://github.com/keepwonder/ezimage/raw/HEAD/relative.png), ![alt](https://github.com/keepwonder/ezimage/raw/HEAD/absolute/path.jpg), ![alt](https://github.com/keepwonder/ezimage/raw/HEAD/<./with space.webp>), ![alt](https://github.com/keepwonder/ezimage/raw/HEAD/x.png "title").
  • ✅ Relative paths (resolved from the Markdown file's directory) and absolute paths.
  • ✅ The same image referenced multiple times — each reference is processed independently (same source → potentially multiple cloud URLs, since cloud keys are randomized by ezimage.pathTemplate).

What gets skipped (with reasons)

Reason Example Behaviour
Remote URL ![alt](https://cdn.example.com/x.png) Skipped silently.
Data URI ![alt](https://github.com/keepwonder/ezimage/raw/HEAD/data:image/png;base64,…) Skipped silently.
Non-image file ![alt](https://github.com/keepwonder/ezimage/raw/HEAD/notes.txt) Skipped silently.
File not on disk ![alt](https://github.com/keepwonder/ezimage/raw/HEAD/deleted.png) Skipped silently by default. Disable ezimage.localImageUpload.skipNonExistent to surface as an error.

The confirmation dialog tells you how many were skipped so nothing is silently dropped without context.

Limitations (v1)

  • Only Markdown inline images are matched. Reference-style (![alt][ref]) and HTML <img> tags are intentionally out of scope for v1.
  • Only local workspaces (file scheme). Remote workspaces (WSL, SSH, Dev Containers) are not yet supported.
  • Untitled (unsaved) documents are rejected — save the file first so relative paths can be resolved.
  • The conversion is manual only: it never runs on save, on paste, or otherwise behind your back. Enable the context-menu entry, trigger it, watch it work, undo if you regret it.

Settings

Setting Default What it does
ezimage.localImageUpload.enabled false Show "EzImage: Convert Local Image Paths to URLs" in the editor context menu. Off by default — opt in once and forget about it.
ezimage.localImageUpload.skipNonExistent true Skip images whose local file is missing (deleted). Set false to surface them as hard errors instead.

🖱 Drag-and-Drop Upload

Opt-in: takes over drag-and-drop for image files in Markdown editors. When enabled, dropping one or more images into a .md file uploads them to the cloud and inserts the URLs in place of the local paths.

⚠️ You must hold Shift while dragging. VS Code's built-in editor-drop behaviour requires Shift — without it the file opens in a new tab. This is a VS Code design choice, not something EzImage controls. The status bar shows a Hold Shift to drop into editor hint while you drag.

Quick start

  1. Settings (Cmd+, / Ctrl+,) → search for EzImage.
  2. Enable ezimage.dropUpload.enabled.
  3. In a Markdown file, hold Shift, drag one or more images from Finder / Explorer into the editor.
  4. Confirm with Upload.
  5. Each image first appears as ![uploading…](https://github.com/keepwonder/ezimage/raw/HEAD/local-path), then morphs into ![filename](https://your-bucket/...) once the upload completes.
  6. A toast summarises: EzImage: drop complete — 3 uploaded, 0 failed. · saved 1.4 MB.

What gets uploaded

  • ✅ Pure image drops: one or more *.png / *.jpg / *.gif / *.webp / *.svg.
  • ❌ Mixed drops (e.g. foo.png + notes.txt): EzImage backs off entirely and lets VS Code handle the drop natively. We never silently swallow a non-image file the user dropped alongside images.
  • ❌ Non-Markdown files: EzImage's drop handler doesn't activate, so other languages keep their native drop semantics.

Settings

Setting Default What it does
ezimage.dropUpload.enabled false Take over drag-and-drop for image drops in Markdown editors. Off by default so your existing drop behaviour is unchanged until you opt in.
ezimage.dropUpload.requireConfirm true Pop a confirmation dialog before uploading. Disable to skip the prompt and upload immediately (faster, but a misclick triggers a network call).
ezimage.dropUpload.placeholder uploading… Alt text shown while the upload is in flight. If the upload fails, the placeholder is left as a still-valid (local-path) Markdown link rather than erased.

FAQ: why does it still open the file in a new tab?

This is VS Code's safety mechanism, not an EzImage bug. VS Code only treats a drag as "drop into the editor" when you hold Shift; without Shift it interprets the gesture as "open this file in a new editor tab" and calls its built-in file opener — completely bypassing every DocumentDropEditProvider, ours included. There is no extension-level setting that disables this behaviour; the only control surface is upstream in VS Code itself. The fix is just muscle memory: drag + Shift.

⌨️ Hotkeys

Action Mac Hotkey Windows/Linux Hotkey
EzImage: Upload Clipboard Image Cmd + Alt + V Ctrl + Alt + V
EzImage: Pick & Upload Image Files Search command EzImage: Pick & Upload Image Files

🌐 Languages

EzImage ships with English and Simplified Chinese translations. The display language is controlled by ezimage.language:

Value Behaviour
auto (default) Follows VS Code's display language. Chinese-locale users see 简体中文; everyone else sees English.
en Force English regardless of VS Code locale.
zh-CN Force Simplified Chinese.

Language changes take effect immediately — no reload needed.

What gets translated

Only the runtime UI that EzImage generates itself — prompts, error dialogs, the Output channel, the format-picker menu. Concretely:

  • ✅ All vscode.window.showErrorMessage / showWarningMessage / showInformationMessage text
  • ✅ All vscode.window.showQuickPick labels and descriptions (e.g. the "Insert this upload as…" picker)
  • ✅ All text written to the Output → EzImage channel
  • ✅ vscode.window.withProgress titles and progress messages
  • ❌ The labels and descriptions in the Settings panel itself

Why the Settings panel stays in English (or VS Code's language)

VS Code reads those strings from package.json and picks a translation only from the VS Code Marketplace's own package.nls.<locale>.json files, keyed by the VS Code display language, not the per-extension setting. There is no public API for an extension to override those strings at runtime.

In practice this is fine: the Settings panel labels describe what a setting does ("Maximum width for compressed images"), not when something failed. The user-facing runtime messages — which the user actually sees while uploading — are what ezimage.language controls.

If you want the entire VS Code UI in Chinese (including EzImage's settings panel), run Configure Display Language from the command palette and pick 中文 (简体). That's a VS Code-level switch, not EzImage's.

Adding a new translation

  1. Copy l10n/bundle.json to l10n/<locale>.bundle.json and translate the values (not the keys).
  2. In src/i18n.ts, add the new locale code to SupportedLocale and BUNDLES.
  3. Update src/types.ts / package.json enum so the language setting surfaces in the UI.
  4. CI validates key parity automatically — npm test catches drift between bundles.

Translations are JSON, so no special tooling is needed. PRs welcome.

🔧 Troubleshooting

Images upload as the original file (WebP compression not working)

This is the most common issue and almost always means the sharp native module failed to load inside VS Code's bundled Node.js. Open the Output → EzImage panel (View → Output → EzImage) and look for one of these lines:

  • Compression engine unavailable: <error> — sharp was found on disk but couldn't be loaded. The most likely cause is an Electron/Node ABI mismatch: VS Code ships with its own Node.js version, and the prebuilt binary you have may not match it. Click "Reload Window" after npm install sharp succeeds, or set ezimage.autoInstallSharp: false and reinstall manually with npm rebuild sharp.
  • Could not find npm on PATH — sharp could not be auto-installed because npm isn't available. Install Node.js ≥ 18.17 from nodejs.org (which includes npm), then click "Install sharp" again.
  • Auto-install exited with code N — npm install itself failed. The Output panel will include a hint tailored to the failure (network/permissions/proxy). Common fixes:
    • Corporate proxy: set NPM_CONFIG_REGISTRY in your environment to your internal mirror.
    • Permission denied: the extension folder may be read-only. Reinstall the VSIX or move VS Code's extensions directory to a writable location.
    • Network timeout: run npm install sharp manually in a terminal from inside the extension folder (~/.vscode/extensions/kiang.ezimage-X.Y.Z/) and reload VS Code when it finishes.

If you previously chose "不再提示" (don't ask again), reset the flag with ezimage.autoInstallSharp: true in settings, then trigger any upload command.

If you just want to keep working without compression, set ezimage.compress: false — uploads will continue to work, just without WebP conversion.

Upload succeeds but image URL returns 404

The object was uploaded but ezimage.r2.publicUrl doesn't match the bucket's actual public distribution URL. Check the Custom Domains tab of your R2 bucket and copy the Public Bucket URL (https://pub-xxxx.r2.dev) into the setting. Make sure the bucket itself has public access enabled.

Missing R2 Access Key ID (or another "Missing R2 …" error)

Run EzImage: Open Settings and fill in the field mentioned in the error. All five R2 fields (accountId, accessKeyId, secretAccessKey, bucketName, publicUrl) are required.

Cmd/Ctrl+Alt+V doesn't paste anything

The command only fires inside Markdown files. Open a .md document, place the cursor where you want the image link, then press the shortcut. If you're still on macOS but pasted a Finder file (not a screenshot), the helper should detect the file reference and offer it; if it doesn't, check the Output → EzImage channel for the line Using image path from clipboard text or Unable to read clipboard file reference.

🗺️ Roadmap

  • [x] Cloudflare R2 basic support
  • [x] Multi-format auto-conversion to WebP compression (with one-click sharp install since 1.0.3)
  • [ ] Universal AWS S3 protocol support
  • [ ] Aliyun OSS & Tencent COS support
  • [ ] Gitee/GitHub image hosting mode
  • [ ] Upload history and statistics preview

🛠️ Development

Building from source

npm install            # install dependencies
npm run compile        # compile TypeScript to ./out/
npm run verify         # package a dev VSIX + run smoke tests
npm run package        # same as verify but keeps the .vsix on disk
npm run vsce:package   # package using the official @vscode/vsce

Commit message conventions

This project follows Conventional Commits so that standard-version can auto-generate the changelog and pick the next version number:

Type Triggers version bump Section in CHANGELOG
feat: minor ✨ Features
fix: patch 🐛 Bug Fixes
perf: patch ⚡ Performance
refactor: — ♻️ Refactors
docs: — 📚 Documentation
build: — 📦 Build System
ci: — 🔧 Continuous Integration
chore: / style: — hidden

Breaking changes: append ! after the type, e.g. feat!: rewrite upload pipeline. This will bump the major version.

Release process

# 1. Make sure conventional commits are in place since the last release
git log v1.0.3..HEAD --oneline

# 2. Run the release script — this bumps package.json, regenerates CHANGELOG.md,
#    and creates a single chore(release): X.Y.Z commit. Tag is NOT created yet
#    so you can review the diff first.
./scripts/release.sh patch      # or minor / major

# 3. Inspect the changes, push the commit, then tag and push the tag
git push
git tag v1.0.4
git push origin v1.0.4

# 4. The GitHub Actions release workflow automatically:
#    - packages the VSIX with the official @vscode/vsce
#    - smoke-tests it (AWS SDK must require correctly)
#    - extracts release notes from CHANGELOG.md
#    - attaches the .vsix to a GitHub Release
#    - if VSCE_PAT is configured, also publishes to the VS Code Marketplace

A dry run is available without modifying anything:

./scripts/release.sh --dry

Publishing to the VS Code Marketplace

The release workflow calls vsce publish automatically — but only if a VSCE_PAT secret is configured in the repo (Settings → Secrets and variables → Actions). Without it the VSIX is still attached to the GitHub Release, just not pushed to the Marketplace.

To enable automatic Marketplace publishing:

  1. Sign in to dev.azure.com using the same Microsoft account that owns the kiang publisher on the Marketplace.
  2. User settings → Personal Access Tokens → New Token.
  3. Scopes: Marketplace (Manage) — this is the only scope publish needs.
  4. Copy the token, then in this repo go to Settings → Secrets and variables → Actions → New repository secret:
    • Name: VSCE_PAT
    • Value: the token
  5. The next tagged release will publish automatically.

If the secret is missing the release workflow logs a warning and the GitHub Release still goes out — so you can also publish manually afterward with:

npx @vscode/vsce publish --packagePath ezimage-1.0.4.vsix

🤝 Contribution & Feedback

If you encounter any issues or have feature suggestions, please:

  • Submit an issue on GitHub Issues.
  • Join our discussion group for deep conversations.

📞 Contact & Support

💬 Feedback

Scan the QR code below to add the author on WeChat, please mention "EzImage":

WeChat Contact

Scan to get technical support

☕ Support the Author

If EzImage has improved your efficiency, feel free to buy me a coffee! Your support is my greatest motivation for optimization and maintenance.

WeChat Pay
WeChat Pay
Alipay Pay
Alipay Pay

Developed with ❤️ by Kiang

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft