上传图片到 Cloudinary(右键菜单版)

点击图片后,通过浏览器右键菜单上传到 Cloudinary,并复制链接。

// ==UserScript==
// @name         上传图片到 Cloudinary(右键菜单版)
// @version      1.1
// @description  点击图片后,通过浏览器右键菜单上传到 Cloudinary,并复制链接。
// @author       Phinsin666
// @source       https://github.com/Phinsin666/Uploading-images-to-Cloudinary
// @match        *://*/*
// @namespace https://gf.qytechs.cn/users/385149
// ==/UserScript==

(function () {
    'use strict';

    const cloudName = 'Cloudinary Cloud Name';              // ← 替换为你的 Cloudinary Cloud Name
    const uploadPreset = 'tampermonkey_upload';      // ← 替换为你的 Upload Preset

    let selectedElement = null;

    // 捕获用户点击的元素
    document.addEventListener('click', (e) => {
        selectedElement = e.target;
        console.log('[Cloudinary] 选中元素:', selectedElement);
    });

    // 注册(不可用)右键菜单命令
    GM_registerMenuCommand('上传选中图片到 Cloudinary', async () => {
        if (!selectedElement) {
            alert('请先点击一张图片或含图的元素。');
            return;
        }

        try {
            const blob = await getImageBlob(selectedElement);
            if (!blob) throw new Error('无法识别或提取图片');

            await uploadBlobToCloudinary(blob);
        } catch (err) {
            alert('❌ 错误:' + err.message);
        }
    });

    // 判断并提取图片 Blob
    async function getImageBlob(el) {
        // 1. <img>
        if (el.tagName === 'IMG' && el.src) {
            return fetchToBlob(el.src);
        }

        // 2. picture > img
        if (el.tagName === 'PICTURE') {
            const img = el.querySelector('img');
            if (img?.src) return fetchToBlob(img.src);
        }

        // 3. background-image
        const bg = getComputedStyle(el).backgroundImage;
        const bgUrlMatch = bg?.match(/url\(["']?(.*?)["']?\)/);
        if (bgUrlMatch && bgUrlMatch[1]) {
            return fetchToBlob(bgUrlMatch[1]);
        }

        // 4. <canvas>
        if (el.tagName === 'CANVAS') {
            const dataUrl = el.toDataURL('image/png');
            return dataURLtoBlob(dataUrl);
        }

        // 5. <svg>
        if (el.tagName === 'SVG' || el instanceof SVGElement) {
            const svgData = new XMLSerializer().serializeToString(el);
            const svgBlob = new Blob([svgData], { type: 'image/svg+xml' });

            // 用 Canvas 转成 PNG
            const imgUrl = URL.createObjectURL(svgBlob);
            const pngBlob = await svgToPngBlob(imgUrl);
            URL.revokeObjectURL(imgUrl);
            return pngBlob;
        }

        return null;
    }

    // 上传图片 Blob 到 Cloudinary
    async function uploadBlobToCloudinary(blob) {
        const apiUrl = `https://api.cloudinary.com/v1_1/${cloudName}/image/upload`;
        const formData = new FormData();
        formData.append('file', blob);
        formData.append('upload_preset', uploadPreset);

        const res = await fetch(apiUrl, { method: 'POST', body: formData });
        const data = await res.json();

        if (data.secure_url) {
            GM_setClipboard(data.secure_url);
            alert(`✅ 上传成功:\n${data.secure_url}`);
            window.open(data.secure_url, '_blank');
        } else {
            throw new Error(data.error?.message || '上传失败');
        }
    }

    // URL 转 blob
    function fetchToBlob(url) {
        return fetch(url).then(res => res.blob());
    }

    // dataURL 转 blob
    function dataURLtoBlob(dataurl) {
        const arr = dataurl.split(',');
        const mime = arr[0].match(/:(.*?);/)[1];
        const bstr = atob(arr[1]);
        let n = bstr.length;
        const u8arr = new Uint8Array(n);
        while (n--) u8arr[n] = bstr.charCodeAt(n);
        return new Blob([u8arr], { type: mime });
    }

    // svg blob url → png blob
    function svgToPngBlob(svgUrl) {
        return new Promise((resolve, reject) => {
            const img = new Image();
            img.onload = () => {
                const canvas = document.createElement('canvas');
                canvas.width = img.width;
                canvas.height = img.height;
                const ctx = canvas.getContext('2d');
                ctx.drawImage(img, 0, 0);
                canvas.toBlob(resolve, 'image/png');
            };
            img.onerror = reject;
            img.src = svgUrl;
        });
    }
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址