// ==UserScript== // @name Storage 复制工具 // @namespace http://tampermonkey.net/ // @version 1.0.1 // @description 在任何网页上复制和粘贴 localStorage 和 sessionStorage // @author You // @match *://*/* // @grant GM_setClipboard // @grant GM_getClipboard // @grant GM_registerMenuCommand // @grant unsafeWindow // @run-at document-idle // License: MIT // @downloadURL none // ==/UserScript== /** * ==================================== * Storage 复制工具 - 使用说明 * ==================================== * * 📖 功能介绍: * 这个脚本可以帮你在不同的网页之间复制和粘贴浏览器的存储数据(localStorage 和 sessionStorage) * 适用于开发调试、迁移登录状态、快速配置测试环境等场景 * * 🎯 使用方法: * * 方法一:通过 Tampermonkey 菜单(推荐新手使用) * ├─ 点击浏览器右上角的 Tampermonkey 图标 * ├─ 在弹出菜单中选择对应功能: * │ ├─ 📋 复制 Storage - 将当前页面的 Storage 复制到剪贴板 * │ ├─ 📥 粘贴 Storage - 将之前复制的 Storage 粘贴到当前页面 * │ ├─ 👀 查看 Storage - 在控制台查看当前页面的 Storage 内容 * │ └─ 🗑️ 清空 Storage - 清空当前页面的 Storage(会二次确认) * * 方法二:通过浏览器控制台(推荐开发者使用) * ├─ 按 F12 打开开发者工具,切换到 Console 标签 * ├─ 直接输入以下命令: * │ ├─ copyStorage() - 复制当前页面 Storage * │ ├─ pasteStorage() - 粘贴 Storage * │ ├─ viewStorage() - 查看当前 Storage * │ └─ clearStorage() - 清空当前 Storage * * 💡 典型使用场景: * * 场景1:将生产环境的登录状态复制到测试环境 * ├─ 1. 在生产环境页面 (https://prod.example.com) * │ 执行 copyStorage() 或点击菜单"复制 Storage" * ├─ 2. 打开测试环境页面 (https://test.example.com) * │ 执行 pasteStorage() 或点击菜单"粘贴 Storage" * └─ 3. 刷新页面,即可使用相同的登录状态 * * 场景2:备份当前页面的配置数据 * ├─ 执行 copyStorage() 复制数据 * ├─ 将剪贴板内容保存到文本文件 * └─ 需要恢复时,复制文本内容并执行 pasteStorage() * * 场景3:查看网站存储了哪些数据 * └─ 执行 viewStorage() 在控制台以表格形式查看 * * ⚙️ 高级用法(通过 storageTools 对象): * * // 只复制 localStorage,不复制 sessionStorage * storageTools.copy({ * includeLocalStorage: true, * includeSessionStorage: false * }); * * // 合并模式粘贴(不清空现有数据,只添加/覆盖) * storageTools.paste(data, { * merge: true, // true: 合并模式, false: 清空后粘贴 * overwrite: true // true: 覆盖相同key, false: 保留已存在的key * }); * * // 只清空 localStorage * storageTools.clear({ * clearLocalStorage: true, * clearSessionStorage: false * }); * * ⚠️ 注意事项: * ├─ 1. 复制的数据仅在当前浏览器会话有效(关闭浏览器后剪贴板会被清空) * ├─ 2. sessionStorage 只在当前标签页有效,关闭标签页后会丢失 * ├─ 3. 默认粘贴模式会清空目标页面的现有 Storage,请谨慎使用 * ├─ 4. 某些网站可能对 Storage 有特殊验证,粘贴后可能需要刷新页面 * └─ 5. 跨域限制:Storage 数据是按域名隔离的,但此工具可以跨域复制粘贴 * * 🔧 技术细节: * ├─ 使用 GM_setClipboard API 进行剪贴板操作(更可靠) * ├─ 提供多层备用方案确保兼容性 * ├─ 数据以 JSON 格式存储和传输 * └─ 支持所有网站 * * 📝 版本信息: * Version: 1.0.0 * Author: Full-Stack Developer * License: MIT * * ==================================== */ (function() { 'use strict'; // 等待页面加载完成后再挂载全局函数 function initStorageTools() { /** * 复制当前页面的 Storage 到剪贴板 * * @param {Object} options - 配置选项 * @param {boolean} options.includeLocalStorage - 是否包含 localStorage,默认 true * @param {boolean} options.includeSessionStorage - 是否包含 sessionStorage,默认 true * * @example * // 复制所有 Storage(默认行为) * copyStorage(); * * @example * // 只复制 localStorage * copyStorage({ includeLocalStorage: true, includeSessionStorage: false }); * * @example * // 只复制 sessionStorage * copyStorage({ includeLocalStorage: false, includeSessionStorage: true }); */ function copyStorage(options = {}) { const { includeLocalStorage = true, includeSessionStorage = true } = options; const data = {}; try { // 复制 localStorage if (includeLocalStorage) { data.localStorage = {}; for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); data.localStorage[key] = localStorage.getItem(key); } } // 复制 sessionStorage if (includeSessionStorage) { data.sessionStorage = {}; for (let i = 0; i < sessionStorage.length; i++) { const key = sessionStorage.key(i); data.sessionStorage[key] = sessionStorage.getItem(key); } } // 生成可执行的代码 const code = `pasteStorage(${JSON.stringify(data, null, 2)})`; // 使用 Tampermonkey 的 GM_setClipboard API if (typeof GM_setClipboard !== 'undefined') { GM_setClipboard(code); console.log('✅ Storage 已复制到剪贴板(Tampermonkey)!'); console.log(`📦 localStorage 项目: ${Object.keys(data.localStorage || {}).length}`); console.log(`📦 sessionStorage 项目: ${Object.keys(data.sessionStorage || {}).length}`); console.log('💡 在目标页面的 console 中执行 pasteStorage() 或直接粘贴即可'); alert('✅ Storage 已复制到剪贴板!\n\n可以在其他页面使用了。'); return; } // 备用方案 useFallbackCopy(code, data); } catch (error) { console.error('❌ 读取 Storage 失败:', error); alert('❌ 读取 Storage 失败: ' + error.message); } } /** * 备用复制方案 - 使用传统的 document.execCommand * 当 GM_setClipboard 不可用时自动调用 * * @param {string} code - 要复制的代码字符串 * @param {Object} data - Storage 数据对象 * @private */ function useFallbackCopy(code, data) { const textarea = document.createElement('textarea'); textarea.value = code; textarea.style.position = 'fixed'; textarea.style.top = '0'; textarea.style.left = '0'; textarea.style.opacity = '0'; document.body.appendChild(textarea); textarea.select(); try { const successful = document.execCommand('copy'); if (successful) { console.log('✅ Storage 已复制到剪贴板(兼容模式)!'); console.log(`📦 localStorage 项目: ${Object.keys(data.localStorage || {}).length}`); console.log(`📦 sessionStorage 项目: ${Object.keys(data.sessionStorage || {}).length}`); alert('✅ Storage 已复制到剪贴板!'); } else { showManualCopy(code, data); } } catch (err) { showManualCopy(code, data); } finally { document.body.removeChild(textarea); } } /** * 显示手动复制提示 * 当所有自动复制方案都失败时调用 * * @param {string} code - 要复制的代码字符串 * @param {Object} data - Storage 数据对象 * @private */ function showManualCopy(code, data) { console.log('%c⚠️ 自动复制失败,请手动复制以下代码:', 'color: orange; font-weight: bold'); console.log(`📦 localStorage 项目: ${Object.keys(data.localStorage || {}).length}`); console.log(`📦 sessionStorage 项目: ${Object.keys(data.sessionStorage || {}).length}`); console.log('%c📋 点击下方代码,Ctrl+A 全选,Ctrl+C 复制:', 'color: #4CAF50; font-weight: bold'); console.log(code); window.__storageCode = code; console.log('%c💡 提示: 代码已保存到 window.__storageCode,可以直接复制', 'color: #2196F3'); alert('⚠️ 自动复制失败\n\n请打开控制台查看代码并手动复制'); } /** * 将 Storage 数据粘贴到当前页面 * * @param {Object} data - Storage 数据对象 * @param {Object} data.localStorage - localStorage 数据 * @param {Object} data.sessionStorage - sessionStorage 数据 * @param {Object} options - 配置选项 * @param {boolean} options.merge - 是否合并模式(不清空现有数据),默认 false * @param {boolean} options.overwrite - 是否覆盖已存在的 key,默认 true * * @example * // 通过菜单粘贴(会提示输入) * pasteStorage(); * * @example * // 直接传入数据对象粘贴(清空现有数据) * const data = { * localStorage: { token: 'xxx', userId: '123' }, * sessionStorage: { tempData: 'yyy' } * }; * pasteStorage(data); * * @example * // 合并模式粘贴(保留现有数据,只添加/更新) * pasteStorage(data, { merge: true }); * * @example * // 合并模式且不覆盖已存在的 key * pasteStorage(data, { merge: true, overwrite: false }); */ function pasteStorage(data, options = {}) { const { merge = false, overwrite = true } = options; try { let localCount = 0; let sessionCount = 0; // 恢复 localStorage if (data.localStorage) { if (!merge) { localStorage.clear(); } for (const [key, value] of Object.entries(data.localStorage)) { if (overwrite || !localStorage.getItem(key)) { localStorage.setItem(key, value); localCount++; } } } // 恢复 sessionStorage if (data.sessionStorage) { if (!merge) { sessionStorage.clear(); } for (const [key, value] of Object.entries(data.sessionStorage)) { if (overwrite || !sessionStorage.getItem(key)) { sessionStorage.setItem(key, value); sessionCount++; } } } console.log('✅ Storage 已恢复!'); console.log(`📦 localStorage 写入: ${localCount} 项`); console.log(`📦 sessionStorage 写入: ${sessionCount} 项`); alert(`✅ Storage 已恢复!\n\nlocalStorage: ${localCount} 项\nsessionStorage: ${sessionCount} 项`); } catch (error) { console.error('❌ 恢复 Storage 失败:', error); alert('❌ 恢复 Storage 失败: ' + error.message); } } /** * 清空当前页面的 Storage * * @param {Object} options - 配置选项 * @param {boolean} options.clearLocalStorage - 是否清空 localStorage,默认 true * @param {boolean} options.clearSessionStorage - 是否清空 sessionStorage,默认 true * * @example * // 清空所有 Storage(默认行为) * clearStorage(); * * @example * // 只清空 localStorage * clearStorage({ clearLocalStorage: true, clearSessionStorage: false }); * * @example * // 只清空 sessionStorage * clearStorage({ clearLocalStorage: false, clearSessionStorage: true }); */ function clearStorage(options = {}) { const { clearLocalStorage = true, clearSessionStorage = true } = options; if (clearLocalStorage) { localStorage.clear(); console.log('🗑️ localStorage 已清空'); } if (clearSessionStorage) { sessionStorage.clear(); console.log('🗑️ sessionStorage 已清空'); } alert('🗑️ Storage 已清空'); } /** * 在控制台以表格形式查看当前页面的 Storage 内容 * * @example * // 查看所有 Storage * viewStorage(); * * 输出示例: * ┌─────────┬──────────────────┐ * │ (index) │ Values │ * ├─────────┼──────────────────┤ * │ token │ 'eyJhbGc...' │ * │ userId │ '12345' │ * └─────────┴──────────────────┘ */ function viewStorage() { console.log('📦 localStorage:'); console.table(Object.fromEntries( Array.from({ length: localStorage.length }, (_, i) => { const key = localStorage.key(i); return [key, localStorage.getItem(key)]; }) )); console.log('📦 sessionStorage:'); console.table(Object.fromEntries( Array.from({ length: sessionStorage.length }, (_, i) => { const key = sessionStorage.key(i); return [key, sessionStorage.getItem(key)]; }) )); alert('📦 Storage 内容已输出到控制台'); } // ========== 注册菜单命令 ========== if (typeof GM_registerMenuCommand !== 'undefined') { GM_registerMenuCommand('📋 复制 Storage', () => { copyStorage(); }); GM_registerMenuCommand('📥 粘贴 Storage', () => { const input = prompt('请粘贴之前复制的 Storage 数据:\n(格式: pasteStorage({...}) 或直接粘贴 JSON 对象)'); if (input) { try { // 尝试提取 JSON 数据 let data; // 匹配 pasteStorage({...}) 格式 const match = input.match(/pasteStorage\s*\(\s*(\{[\s\S]*\})\s*\)/); if (match) { data = JSON.parse(match[1]); } else { // 直接解析为 JSON data = JSON.parse(input); } // 执行粘贴 pasteStorage(data); } catch (error) { console.error('解析失败:', error); alert('❌ 数据格式错误\n\n请确保粘贴的是有效的 JSON 数据或 pasteStorage() 调用代码'); } } }); GM_registerMenuCommand('👀 查看 Storage', () => { viewStorage(); }); GM_registerMenuCommand('🗑️ 清空 Storage', () => { if (confirm('确定要清空当前页面的 Storage 吗?')) { clearStorage(); } }); } // ========== 挂载到全局对象 ========== window.storageTools = { copy: copyStorage, paste: pasteStorage, clear: clearStorage, view: viewStorage }; // 单独挂载常用函数 window.copyStorage = copyStorage; window.pasteStorage = pasteStorage; window.viewStorage = viewStorage; window.clearStorage = clearStorage; // 确保函数可以在控制台访问 unsafeWindow.storageTools = window.storageTools; unsafeWindow.copyStorage = copyStorage; unsafeWindow.pasteStorage = pasteStorage; unsafeWindow.viewStorage = viewStorage; unsafeWindow.clearStorage = clearStorage; // ========== 初始化提示 ========== console.log(` %c📦 Storage 复制工具已加载! 🎯 使用方法: 1️⃣ 控制台命令: copyStorage() - 复制当前页面 Storage pasteStorage() - 粘贴 Storage(需要先复制) viewStorage() - 查看当前 Storage clearStorage() - 清空当前 Storage 2️⃣ 右键菜单: 点击 Tampermonkey 图标 → 选择对应功能 3️⃣ 高级用法: storageTools.copy({ includeLocalStorage: true, includeSessionStorage: false }) storageTools.paste(data, { merge: true, overwrite: false }) `, 'color: #4CAF50; font-weight: bold; font-size: 14px;'); } // 立即初始化 initStorageTools(); // 如果页面还未加载完成,在 DOMContentLoaded 时再次确保挂载 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initStorageTools); } })();