主要是用别人的图床老是提示一分钟上传限制
想想自己弄一个代码参照https://skyimg.de/
全部用chatgpt编写的
我有一台**Web Hosting
之前推荐的2o一年
教程编写中。。。
📜 粘贴 & 拖拽图片自动上传 🚀🔥 支持 Markdown / BBCode / HTML / URL 图片上传,搭配 PHP 服务器后端
,高效 & 可控!
✅ 粘贴 (Ctrl + V
) 或拖拽上传
✅ 自动转换 JPEG / PNG
为 WebP
(可选)
✅ 支持 Markdown / BBCode / URL / HTML
格式
✅ 上传成功自动复制图片链接
✅ Tampermonkey
菜单选择格式
✅ 支持 PHP 服务器端
,图片存储 安全 & 可控
Tampermonkey
用户脚本📥 复制以下 J**aScript
代码,并粘贴到 Tampermonkey
编辑器// ==UserScript==// @name 粘贴 & 拖拽图片自动上传// @namespace https://ganba.de/// @version 1.0.0// @license MIT// @description 修复格式选择问题,支持 Markdown / BBCode / URL / HTML / 富文本// @match *://*/*// @grant GM_notification// @grant GM_xmlhttpRequest// @grant GM_getValue// @grant GM_setValue// @grant GM_registerMenuCommand// ==/UserScript==(function() { 'use strict'; let storedFormat = GM_getValue('uploadFormat', 'markdown'); let autoDetect = GM_getValue('autoDetect', false); const config = { `apiUrl: 'https://yourdomain.com/upload.php',` defaultFormat: storedFormat, autoDetectFormat: autoDetect, }; let lastActiveElement = null; document.addEventListener('focusin', function(e) { lastActiveElement = e.target; }); document.addEventListener('paste', function(e) { const clipboardData = e.clipboardData || window.clipboardData; if (!clipboardData) return; const items = clipboardData.items; let imageFile = null; for (let item of items) { if (item.type.indexOf('image') !== -1) { imageFile = item.getAsFile(); break; } } if (!imageFile) return; e.preventDefault(); uploadImage(imageFile); }); document.addEventListener('dragover', (e) => e.preventDefault()); document.addEventListener('drop', function(e) { e.preventDefault(); const files = e.dataTransfer.files; if (files.length > 0 && files[0].type.startsWith('image/')) { uploadImage(files[0]); } }); function uploadImage(file) { showToast('正在上传图片...', 'info'); const formData = new FormData(); formData.append('file', file); GM_xmlhttpRequest({ method: 'POST', url: config.apiUrl, data: formData, responseType: 'json', onload: function(response) { try { const data = JSON.parse(response.responseText.trim()); if (data.url) { const format = config.autoDetectFormat ? detectEditorFormat() : config.defaultFormat; const formattedLink = formatLink(data.url, format); insertText(formattedLink); copyToClipboard(formattedLink); showToast(`图片上传成功!格式:${format}`, 'success'); } else { showToast('上传失败:' + (data.error || '未知错误'), 'error'); } } catch (error) { showToast('服务器返回数据错误', 'error'); } }, onerror: function() { showToast('图片上传失败,请检查服务器', 'error'); } }); } function detectEditorFormat() { if (!lastActiveElement) return config.defaultFormat; if (lastActiveElement.tagName === 'TEXTAREA' || lastActiveElement.tagName === 'INPUT') return 'url'; if (lastActiveElement.closest('[contenteditable]')) return 'html'; return config.defaultFormat; } function formatLink(url, format) { switch (format) { case 'bbcode': return `[img]${url}[/img]`; case 'html': return `<img src="${url}" alt="image">`; case 'wysiwyg': return `<figure><img src="${url}"></figure>`; case 'url': return url; case 'markdown': default: return ``; } } function insertText(text) { const el = lastActiveElement; if (!el) return; if (el.tagName === 'TEXTAREA' || el.tagName === 'INPUT') { insertAtCursor(el, text); } else if (el.isContentEditable) { try { document.execCommand('insertText', false, text); } catch (e) { el.innerText += text; } } else { showToast('无法自动插入图片链接,请手动粘贴', 'warning'); copyToClipboard(text); } } function insertAtCursor(el, text) { const startPos = el.selectionStart || 0; const endPos = el.selectionEnd || startPos; el.value = el.value.substring(0, startPos) + text + el.value.substring(endPos); el.selectionStart = el.selectionEnd = startPos + text.length; el.focus(); } function copyToClipboard(text) { n**igator.clipboard.writeText(text).then(() => { showToast(`图片链接已复制到剪贴板!格式: ${config.defaultFormat}`, 'success'); }).catch(() => { const textarea = document.createElement('textarea'); textarea.value = text; document.body.appendChild(textarea); textarea.select(); document.execCommand('copy'); document.body.removeChild(textarea); showToast(`图片链接已复制到剪贴板!格式: ${config.defaultFormat}`, 'success'); }); } function showToast(message, type = 'info') { GM_notification({ text: message, title: '图片上传', timeout: 5000 }); } function updateFormat(format) { GM_setValue('uploadFormat', format); config.defaultFormat = format; showToast(`已切换格式:${format}`, 'success'); } GM_registerMenuCommand("📜 选择: Markdown", () => updateFormat('markdown')); GM_registerMenuCommand("📜 选择: BBCode", () => updateFormat('bbcode')); GM_registerMenuCommand("📜 选择: HTML", () => updateFormat('html')); GM_registerMenuCommand("📜 选择: WYSIWYG", () => updateFormat('wysiwyg')); GM_registerMenuCommand("📜 选择: 纯 URL", () => updateFormat('url')); GM_registerMenuCommand("⚙️ 自动匹配 (开关)", () => { const newState = !GM_getValue('autoDetect', false); GM_setValue('autoDetect', newState); config.autoDetectFormat = newState; showToast(`自动匹配: ${newState ? '开启' : '关闭'}`, 'success'); });})();
📌 2️⃣ 服务器端 (PHP)📥 复制以下 upload.php
,上传到你的服务器<?php// **彻底清理所有可能的无效输出**if (ob_get_length()) ob_end_clean();header('Content-Type: application/json; charset=utf-8');header('Access-Control-Allow-Origin: *');header('Access-Control-Allow-Methods: POST, OPTIONS');header('Access-Control-Allow-Headers: Content-Type');// **优化 PHP 运行**ini_set('display_errors', 0);ini_set('log_errors', 1);ini_set('output_buffering', 'off');ini_set('zlib.output_compression', 0);error_reporting(0);// **配置**$config = [ 'upload_dir' => 'uploads/', 'allowed_types' => ['image/jpeg', 'image/png', 'image/webp', 'image/gif'], 'max_size' => 50 * 1024 * 1024, // 50MB 'enable_webp' => true, 'base_url' => 'https://yourdomain.come/uploads/', 'hash_algorithm' => 'md5' // 可选:'md5' 或 'sha1'];// **处理 OPTIONS 请求**if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') exit;// **检查 POST 请求**if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_FILES['file'])) { output_json(['error' => 'No file uploaded'], 400);}// **获取文件信息**$file = $_FILES['file'];$fileType = mime_content_type($file['tmp_name']);$fileSize = $file['size'];$fileTmp = $file['tmp_name'];if (!in_array($fileType, $config['allowed_types'])) { output_json(['error' => 'Unsupported file type'], 400);}if ($fileSize > $config['max_size']) { output_json(['error' => 'File too large'], 400);}// **计算文件哈希(唯一文件名)**$fileHash = ($config['hash_algorithm'] === 'sha1') ? sha1_file($fileTmp) : md5_file($fileTmp);$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));$newFilename = "{$fileHash}.{$ext}";$uploadPath = $config['upload_dir'] . $newFilename;$fileUrl = trim($config['base_url'] . $newFilename);// **如果文件已存在,直接返回 URL**if (file_exists($uploadPath)) { output_json(['url' => $fileUrl]);}// **确保目录存在**if (!is_dir($config['upload_dir']) && !mkdir($config['upload_dir'], 0755, true)) { output_json(['error' => 'Failed to create upload directory', 'debug' => error_get_last()], 500);}// **移动文件**if (!move_uploaded_file($fileTmp, $uploadPath)) { output_json(['error' => 'Failed to s**e file', 'debug' => error_get_last()], 500);}// **WebP 转换逻辑**if ($config['enable_webp'] && in_array($fileType, ['image/jpeg', 'image/png'])) { $webpFilename = "{$fileHash}.webp"; $webpPath = $config['upload_dir'] . $webpFilename; if (convertToWebp($uploadPath, $webpPath)) { unlink($uploadPath); // **删除原始文件** output_json(['url' => trim($config['base_url'] . $webpFilename)]); }}// **最终返回上传 URL**output_json(['url' => $fileUrl]);/** * **快速 JSON 响应** */function output_json($data, $http_status = 200) { http_response_code($http_status); header('Content-Type: application/json; charset=utf-8'); echo json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); exit;}/** * **将图片转换为 WebP 格式** */function convertToWebp($sourcePath, $destinationPath) { $info = getimagesize($sourcePath); $mime = $info['mime']; if ($mime === 'image/jpeg') { $image = imagecreatefromjpeg($sourcePath); } elseif ($mime === 'image/png') { $image = imagecreatefrompng($sourcePath); } else { return false; // **不转换 gif 或 webp** } if (!$image) return false; $result = imagewebp($image, $destinationPath, 80); imagedestroy($image); return $result;}
前排学习
挂alist会不会封号啊
学习学习
1
看着真不错
转webp的目的是?