Greasy Fork

来自缓存

Greasy Fork is available in English.

Storage 复制工具

在任何网页上复制和粘贴 localStorage 和 sessionStorage

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Storage 复制工具
// @namespace    http://tampermonkey.net/
// @version      1.0.2
// @description  在任何网页上复制和粘贴 localStorage 和 sessionStorage
// @author       You
// @match        *://*/*
// @grant        GM_setClipboard
// @grant        GM_getClipboard
// @grant        GM_registerMenuCommand
// @grant        unsafeWindow
// @run-at       document-idle
// License: MIT
// ==/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() 或直接粘贴即可');
                console.log('✅ Storage 已复制到剪贴板!\n\n可以在其他页面使用了。');
                return;
            }

            // 备用方案
            useFallbackCopy(code, data);

        } catch (error) {
            console.error('❌ 读取 Storage 失败:', error);
            console.log('❌ 读取 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}`);
                console.log('✅ 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');
        console.log('⚠️ 自动复制失败\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} 项`);
            console.log(`✅ Storage 已恢复!\n\nlocalStorage: ${localCount} 项\nsessionStorage: ${sessionCount} 项`);

        } catch (error) {
            console.error('❌ 恢复 Storage 失败:', error);
            console.log('❌ 恢复 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 已清空');
        }

        console.log('🗑️ 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)];
            })
        ));

        console.log('📦 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);
                    console.log('❌ 数据格式错误\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);
    }

})();