侧边栏壁纸
博主头像
虚拟多多

行动起来,活在当下

  • 累计撰写 53 篇文章
  • 累计创建 7 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

cloudflare workers部署一个基于360和搜狗的远程图床

部署前准备

你需要准备好:

  1. Cloudflare 账号

  2. 一个 KV 命名空间:用于存储你的无限量上传历史链接。

  3. workers.js代码放在文章最后


详细部署步骤

第一步:创建 KV 命名空间(存储中心)

  1. 登录 Cloudflare 控制台

  2. 在左侧菜单点击 “存储与数据库 (Storage & Databases)” -> “KV”

  3. 点击 “创建命名空间 (Create Namespace)”

  4. 输入名字:IMG_LINKS(注意大小写,代码中引用了这个名字)。

  5. 点击 “添加 (Add)”

第二步:创建 Worker 服务

  1. 在左侧菜单点击 “计算 (Compute)” -> “Workers 和 Pages”

  2. 点击 “创建应用程序 (Create application)” -> “创建 Worker”

  3. 给你的 Worker 起个名字(例如 cloudshot-pro),点击 “部署 (Deploy)”

第三步:绑定 KV 到 Worker

这是最关键的一步,否则无法记录历史:

  1. 进入你刚刚创建的 Worker 详情页。

  2. 点击上方选项卡中的 “设置 (Settings)”

  3. 在左侧子菜单选择 “变量 (Variables)”

  4. 滚动到 “KV 命名空间绑定 (KV Namespace Bindings)” 区域,点击 “添加绑定 (Add binding)”

  5. 变量名称 (Variable name):填写 IMG_LINKS

  6. KV 命名空间 (KV namespace):在下拉框选择你刚才创建的那个 IMG_LINKS

  7. 点击 “保存并部署 (Save and deploy)”

第四步:写入代码

  1. 回到 Worker 的 “预览 (Preview)” 或点击右上角的 “编辑代码 (Edit Code)”

  2. 清空编辑器中原有的所有代码。

  3. 将我上一条回复中提供的那份 “最终优化版”完整代码 粘贴进去。

  4. 点击右上角的 “部署 (Deploy)”

const HTML_CONTENT = `<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="referrer" content="no-referrer">
    <title>CloudShot Pro - 智能图床工作台</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
    <style>
        @import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;600;800&display=swap');
        body { font-family: 'Plus Jakarta Sans', sans-serif; background: #f8fafc; color: #1e293b; }
        .glass-blur { background: rgba(255, 255, 255, 0.85); backdrop-filter: blur(20px); border: 1px solid rgba(255,255,255,0.4); }
        .drop-zone { border: 2px dashed #cbd5e1; border-radius: 24px; transition: 0.3s cubic-bezier(0.4, 0, 0.2, 1); }
        .drop-zone.dragover { border-color: #3b82f6; background: #eff6ff; transform: scale(1.01); }
        
        /* 相册图片卡片优化 */
        .img-card { aspect-ratio: 1/1; border-radius: 16px; overflow: hidden; background: #f1f5f9; position: relative; cursor: zoom-in; box-shadow: 0 4px 12px rgba(0,0,0,0.03); }
        .img-card img { width: 100%; height: 100%; object-fit: cover; transition: 0.6s cubic-bezier(0.4, 0, 0.2, 1); }
        .img-card:hover img { transform: scale(1.08); }
        
        /* 复制按钮移至底部,解决遮挡问题 */
        .img-overlay { position: absolute; inset: 0; display: flex; flex-direction: column; justify-content: flex-end; opacity: 0; transition: 0.3s; padding: 8px; }
        .img-card:hover .img-overlay { opacity: 1; background: linear-gradient(to top, rgba(0,0,0,0.6) 0%, transparent 50%); }
        .action-bar { display: flex; gap: 4px; width: 100%; }
        .action-btn { flex: 1; background: rgba(255, 255, 255, 0.95); backdrop-filter: blur(4px); font-size: 10px; font-weight: 700; padding: 6px 0; border-radius: 8px; color: #1e293b; transition: 0.2s; }
        .action-btn:hover { background: #fff; transform: translateY(-1px); }

        /* 灯箱预览 */
        #lightbox { display: none; position: fixed; inset: 0; z-index: 9999; background: rgba(15, 23, 42, 0.95); backdrop-filter: blur(10px); align-items: center; justify-content: center; padding: 20px; cursor: zoom-out; }
        #lightbox img { max-width: 95%; max-height: 90vh; border-radius: 12px; box-shadow: 0 25px 50px -12px rgba(0,0,0,0.5); transform: scale(0.9); transition: 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); }
        #lightbox.active { display: flex; }
        #lightbox.active img { transform: scale(1); }
        
        /* 侧边栏按钮美化 */
        .copy-btn-sm { font-size: 11px; font-weight: 700; padding: 8px 0; border-radius: 8px; background: #f1f5f9; color: #475569; width: 100%; text-align: center; transition: all 0.2s; border: 1px solid transparent; }
        .copy-btn-sm:hover { background: #fff; border-color: #e2e8f0; color: #3b82f6; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.05); }
        
        ::-webkit-scrollbar { width: 6px; }
        ::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 10px; }
    </style>
</head>
<body class="h-screen overflow-hidden flex flex-col md:flex-row">

    <aside class="w-full md:w-80 h-[38vh] md:h-screen bg-white border-r border-slate-200 flex flex-col z-20 shadow-2xl overflow-hidden">
        <div class="p-6 border-b border-slate-100 flex items-center justify-between">
            <h1 class="text-xl font-800 tracking-tighter flex items-center gap-2">
                <span class="bg-blue-600 text-white w-8 h-8 flex items-center justify-center rounded-lg shadow-lg italic">C</span>
                CloudShot
            </h1>
            <button onclick="openGallery()" class="text-[11px] bg-blue-50 text-blue-600 px-3 py-1.5 rounded-full font-bold hover:bg-blue-100 transition-all">全量相册</button>
        </div>
        <div class="p-6 flex-1 bg-slate-50/30 overflow-hidden">
            <h3 class="text-[10px] font-bold text-slate-400 uppercase tracking-widest mb-4">最近同步 (6)</h3>
            <div id="recentList" class="space-y-5">
                </div>
        </div>
    </aside>

    <main class="flex-1 overflow-y-auto p-6 md:p-16">
        <div class="max-w-4xl mx-auto">
            <header class="mb-10">
                <h2 class="text-3xl font-800 text-slate-900 mb-2 tracking-tight">发布新的媒体资源</h2>
                <p class="text-slate-500 font-medium italic">无限制云端记录 · 毫秒级外链分发</p>
            </header>

            <div class="glass-blur p-8 rounded-[40px] shadow-2xl shadow-slate-200/50">
                <div class="grid grid-cols-2 md:grid-cols-4 gap-3 mb-8">
                    <label class="cursor-pointer"><input type="radio" name="api" value="360tc" class="peer sr-only" checked><div class="py-3 text-center rounded-2xl border border-slate-100 text-sm font-bold text-slate-500 peer-checked:bg-slate-900 peer-checked:text-white transition-all">360图床</div></label>
                    <label class="cursor-pointer"><input type="radio" name="api" value="jdtc" class="peer sr-only"><div class="py-3 text-center rounded-2xl border border-slate-100 text-sm font-bold text-slate-500 peer-checked:bg-slate-900 peer-checked:text-white transition-all">京东</div></label>
                    <label class="cursor-pointer"><input type="radio" name="api" value="sogotc" class="peer sr-only"><div class="py-3 text-center rounded-2xl border border-slate-100 text-sm font-bold text-slate-500 peer-checked:bg-slate-900 peer-checked:text-white transition-all">搜狗</div></label>
                    <label class="cursor-pointer"><input type="radio" name="api" value="yanxuantc" class="peer sr-only"><div class="py-3 text-center rounded-2xl border border-slate-100 text-sm font-bold text-slate-500 peer-checked:bg-slate-900 peer-checked:text-white transition-all">网易严选</div></label>
                </div>

                <div id="dropZone" class="drop-zone bg-slate-50/50 p-16 text-center mb-8 cursor-pointer group hover:bg-white transition-all">
                    <input type="file" id="fileInput" class="hidden" accept="image/*" multiple>
                    <div class="text-6xl text-slate-200 group-hover:text-blue-500 transition-all mb-4"><i class="fa-solid fa-cloud-arrow-up"></i></div>
                    <p class="text-slate-700 font-bold">将文件拖拽至此或点击选择</p>
                    <div id="pendingList" class="hidden mt-8 flex flex-wrap justify-center gap-3 pt-6 border-t border-slate-100"></div>
                </div>

                <button id="uploadButton" class="w-full bg-blue-600 hover:bg-blue-700 text-white py-5 rounded-[24px] font-bold text-lg shadow-xl shadow-blue-200 transition-all active:scale-[0.98]">
                    <span id="btnText">开始同步任务</span>
                </button>
            </div>
        </div>
    </main>

    <div id="galleryModal" class="fixed inset-0 bg-slate-900 z-[5000] hidden flex flex-col">
        <div class="p-8 border-b border-white/10 flex justify-between items-center">
            <h2 class="text-white text-2xl font-800 flex items-center gap-3"><i class="fa-solid fa-layer-group"></i> 全量媒体库</h2>
            <div class="flex items-center gap-8">
                <button onclick="clearHistory()" class="text-red-400 text-sm font-bold hover:text-red-300">清空所有记录</button>
                <button onclick="closeGallery()" class="text-white/60 hover:text-white text-3xl transition-colors"><i class="fa-solid fa-times"></i></button>
            </div>
        </div>
        <div id="fullGallery" class="p-8 overflow-y-auto grid grid-cols-2 sm:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 gap-6">
            </div>
    </div>

    <div id="lightbox" onclick="closeLightbox()">
        <img id="lightboxImg" src="" onclick="event.stopPropagation()">
    </div>

    <script>
        const fileInput = document.getElementById('fileInput');
        const dropZone = document.getElementById('dropZone');
        const recentList = document.getElementById('recentList');
        const fullGallery = document.getElementById('fullGallery');
        const uploadButton = document.getElementById('uploadButton');
        const lightbox = document.getElementById('lightbox');
        const lightboxImg = document.getElementById('lightboxImg');
        let selectedFiles = [];

        window.onload = async () => {
            const resp = await fetch('/history');
            const data = await resp.json();
            const sorted = data.reverse();
            // 核心改进:只保留侧边栏 6 张
            sorted.slice(0, 6).forEach(item => renderRecentCard(item));
            // 相册仍然显示全部
            sorted.forEach(item => renderGalleryCard(item));
        };

        function showImage(url) {
            lightboxImg.src = url.replace(/^http:\\/\\//i, 'https://');
            lightbox.classList.add('active');
        }
        function closeLightbox() { lightbox.classList.remove('active'); }
        function openGallery() { document.getElementById('galleryModal').classList.remove('hidden'); }
        function closeGallery() { document.getElementById('galleryModal').classList.add('hidden'); }

        function renderRecentCard(item) {
            const safeUrl = item.url.replace(/^http:\\/\\//i, 'https://');
            const div = document.createElement('div');
            div.className = 'space-y-3';
            div.innerHTML = \`
                <div class="flex items-center gap-4 cursor-pointer group" onclick="showImage('\${safeUrl}')">
                    <div class="w-12 h-12 rounded-xl overflow-hidden bg-slate-200 flex-shrink-0 shadow-md ring-2 ring-white transition-all group-hover:ring-blue-100">
                        <img src="\${safeUrl}" class="w-full h-full object-cover">
                    </div>
                    <div class="flex-1 min-w-0">
                        <p class="text-[11px] font-bold text-slate-800 truncate mb-1">\${item.name}</p>
                        <p class="text-[9px] text-slate-400 uppercase tracking-tighter italic">Cloud Sync</p>
                    </div>
                </div>
                <div class="flex gap-2">
                    <button onclick="copy('\${safeUrl}', this)" class="copy-btn-sm">复制链接</button>
                    <button onclick="copyHTML('\${safeUrl}', '\${item.name}', this)" class="copy-btn-sm">复制HTML代码</button>
                </div>\`;
            recentList.appendChild(div);
        }

        function renderGalleryCard(item) {
            const safeUrl = item.url.replace(/^http:\\/\\//i, 'https://');
            const div = document.createElement('div');
            div.className = 'img-card';
            div.onclick = () => showImage(safeUrl);
            div.innerHTML = \`
                <img src="\${safeUrl}" loading="lazy">
                <div class="img-overlay">
                    <div class="action-bar" onclick="event.stopPropagation()">
                        <button onclick="copy('\${safeUrl}', this)" class="action-btn">复制链接</button>
                        <button onclick="copyHTML('\${safeUrl}', '\${item.name}', this)" class="action-btn">复制HTML</button>
                    </div>
                </div>\`;
            fullGallery.appendChild(div);
        }

        function handleFiles(files) {
            selectedFiles = Array.from(files);
            const pending = document.getElementById('pendingList');
            pending.innerHTML = ''; pending.classList.remove('hidden');
            selectedFiles.forEach(f => {
                const reader = new FileReader();
                reader.onload = (e) => {
                    const d = document.createElement('div');
                    d.className = 'w-12 h-12 rounded-xl border-2 border-white shadow-lg overflow-hidden';
                    d.innerHTML = \`<img src="\${e.target.result}" class="w-full h-full object-cover">\`;
                    pending.appendChild(d);
                };
                reader.readAsDataURL(f);
            });
        }

        dropZone.addEventListener('click', () => fileInput.click());
        fileInput.addEventListener('change', (e) => handleFiles(e.target.files));
        dropZone.addEventListener('dragover', (e) => { e.preventDefault(); dropZone.classList.add('dragover'); });
        dropZone.addEventListener('dragleave', () => dropZone.classList.remove('dragover'));
        dropZone.addEventListener('drop', (e) => { e.preventDefault(); dropZone.classList.remove('dragover'); handleFiles(e.dataTransfer.files); });

        uploadButton.addEventListener('click', async function() {
            if (!selectedFiles.length) return alert('请先选择图片');
            const api = document.querySelector('input[name="api"]:checked').value;
            this.disabled = true;
            document.getElementById('btnText').innerText = '正在同步数据...';
            for (const file of selectedFiles) {
                const formData = new FormData();
                formData.append('file', file);
                formData.append('apiType', api);
                try { await fetch('/upload', { method: 'POST', body: formData }); } catch(e) {}
            }
            location.reload(); 
        });

        async function clearHistory() {
            if(confirm('此操作将永久抹除云端所有记录,确定吗?')) { await fetch('/clear', { method: 'POST' }); location.reload(); }
        }

        async function copy(t, b) {
            await navigator.clipboard.writeText(t);
            const o = b.innerText; b.innerText = '已复制';
            setTimeout(() => b.innerText = o, 1000);
        }
        async function copyHTML(url, name, b) {
            await navigator.clipboard.writeText(\`<img src="\${url}" alt="\${name}">\`);
            const o = b.innerText; b.innerText = '已复制';
            setTimeout(() => b.innerText = o, 1000);
        }
    </script>
</body>
</html>`;

export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    if (url.pathname === '/history') {
      const data = await env.IMG_LINKS.get('list');
      return new Response(data || '[]', { headers: { 'Content-Type': 'application/json' } });
    }
    if (url.pathname === '/clear') {
      await env.IMG_LINKS.put('list', '[]');
      return new Response('ok');
    }
    if (url.pathname === '/upload' && request.method === 'POST') {
      const formData = await request.formData();
      const file = formData.get('file');
      const apiType = formData.get('apiType');
      const endpoints = {
        '360tc': 'https://api.xinyew.cn/api/360tc',
        'jdtc': 'https://api.xinyew.cn/api/jdtc',
        'sogotc': 'https://api.xinyew.cn/api/sogotc',
        'yanxuantc': 'https://api.xinyew.cn/api/yanxuantc'
      };
      try {
        const resp = await fetch(endpoints[apiType], { method: 'POST', body: formData });
        const result = await resp.json();
        const finalUrl = result.url || result.data?.url;
        if (finalUrl) {
          let list = JSON.parse(await env.IMG_LINKS.get('list') || '[]');
          list.push({ url: finalUrl, name: file.name, time: Date.now() });
          await env.IMG_LINKS.put('list', JSON.stringify(list));
        }
        return new Response(JSON.stringify(result));
      } catch (e) {
        return new Response(JSON.stringify({ error: e.message }), { status: 500 });
      }
    }
    return new Response(HTML_CONTENT, { headers: { 'Content-Type': 'text/html; charset=utf-8' } });
  }
};

0

评论区