吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 134|回复: 8
收起左侧

[资源求助] 把文件内容转换成二维码的生成器

[复制链接]
哄妻公 发表于 2026-5-14 15:54
100吾爱币
本帖最后由 哄妻公 于 2026-5-14 16:04 编辑

(生成二维码后扫码就能显示文件内容)

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

xhkj123 发表于 2026-5-15 06:43
微信小程序草料二维码或腾讯微码,都会使用微信,如果想脱离使用微信,可以自己建个网站,把文件放进去,把网制作成二维码也可以(注:这里推荐使用联图二维码.)推荐都在必应里搜索.
enoch316 发表于 2026-5-15 08:44
不知道什应用场景?多大文件?先推荐一个在线的https://tools.miku.ac/t/qrcode  二维码生成器  
yangaaaaa 发表于 2026-5-15 08:49
https://tools.liumingye.cn/#/explore/playlist 日常应该够了
cmbrun 发表于 2026-5-15 08:55
不知你要显示什么格式及文件内容多大,建议要展示的文档先传到腾讯文档,然后把文档链接用草料二维码之类的东西转成二维码。
jyjjf 发表于 2026-5-15 12:26
这玩意有字数限制的(大概2000个字),字越多,二维码看起来会很复杂(远看一团黑),你试试有知道了。
kuhaiqiudu120 发表于 2026-5-15 16:59
为啥我发个帖子,还需要审核呢。
kuhaiqiudu120 发表于 2026-5-15 17:01
我之前也有这个诉求,让ai写了一个软件,但软件不好用,我就改成了html页面,你直接保存成qr.html试试,如果你需要离线使用,你需要把里面引用的js文件也保存再来,修改下本地的。  我加了一个使用gz压缩功能,挺好用,能大幅减少生成二维码的数量。 最后提醒下切勿非法使用。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
    <title>文件 &#8644; 二维码 · 生成/解码 (支持Gzip压缩)</title>
    <style>
        * {
            box-sizing: border-box;
            font-family: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
        }
        body {
            background: #f0f4f8;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            margin: 20px;
        }
        .card {
            max-width: 1000px;
            width: 100%;
            background: rgba(255,255,255,0.7);
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
            border-radius: 36px;
            box-shadow: 0 20px 40px rgba(0,0,0,0.12), 0 8px 24px rgba(0,20,40,0.08);
            padding: 32px;
            border: 1px solid rgba(255,255,255,0.5);
        }
        h1 {
            font-size: 2rem;
            font-weight: 600;
            margin: 0 0 8px 0;
            background: linear-gradient(145deg, #0b2b44, #1b4a6b);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            letter-spacing: -0.01em;
        }
        .sub {
            color: #2c5777;
            margin-bottom: 20px;
            font-size: 0.95rem;
            border-left: 4px solid #3a7ca5;
            padding-left: 16px;
            background: #e7f0f9;
            border-radius: 0 40px 40px 0;
            line-height: 1.5;
            width: fit-content;
            padding: 8px 20px 8px 16px;
        }

        .tab-bar {
            display: flex;
            gap: 12px;
            margin-bottom: 28px;
            border-bottom: 2px solid #c5d9eb;
            padding-bottom: 10px;
        }
        .tab-btn {
            background: transparent;
            border: none;
            padding: 10px 32px;
            font-size: 1.2rem;
            font-weight: 600;
            color: #36698b;
            border-radius: 60px 60px 0 0;
            cursor: pointer;
            transition: 0.15s;
            border-bottom: 4px solid transparent;
            margin-bottom: -2px;
        }
        .tab-btn.active {
            color: #0b3b5c;
            background: #ffffffd0;
            border-bottom: 4px solid #1b6b9e;
            box-shadow: 0 -4px 10px rgba(0,30,50,0.05);
        }
        .tab-panel {
            display: none;
        }
        .tab-panel.active-panel {
            display: block;
        }

        .control-panel {
            background: white;
            border-radius: 28px;
            padding: 24px;
            box-shadow: inset 0 1px 3px rgba(0,0,0,0.02), 0 10px 20px -10px rgba(0,40,70,0.2);
            margin-bottom: 32px;
            border: 1px solid rgba(255,255,255,0.8);
        }
        .file-area {
            display: flex;
            flex-wrap: wrap;
            align-items: center;
            gap: 20px;
        }
        .file-label {
            background: #1b4b6e;
            color: white;
            padding: 12px 28px;
            border-radius: 40px;
            font-weight: 500;
            cursor: pointer;
            transition: 0.2s;
            box-shadow: 0 4px 10px rgba(23,65,104,0.3);
            display: inline-flex;
            align-items: center;
            gap: 8px;
            border: 1px solid #ffffff40;
        }
        .file-label:hover {
            background: #0f3b55;
            transform: scale(1.02);
        }
        #file-input {
            position: absolute;
            opacity: 0;
            width: 0.1px;
            height: 0.1px;
        }
        .file-info {
            background: #eef3f9;
            border-radius: 60px;
            padding: 8px 22px;
            color: #113750;
            font-weight: 450;
            word-break: break-word;
            flex: 1;
            min-width: 200px;
            border: 1px dashed #aac3d9;
        }
        .options {
            display: flex;
            flex-wrap: wrap;
            align-items: center;
            gap: 20px;
            margin-top: 20px;
        }
        .checkbox-wrapper {
            display: flex;
            align-items: center;
            gap: 10px;
            background: #deecf5;
            padding: 8px 20px;
            border-radius: 40px;
        }
        .checkbox-wrapper input[type="checkbox"] {
            width: 20px;
            height: 20px;
            accent-color: #1f6392;
            border-radius: 6px;
            cursor: pointer;
        }
        .btn-primary {
            background: #2274a5;
            border: none;
            color: white;
            padding: 12px 36px;
            border-radius: 60px;
            font-weight: 600;
            font-size: 1.1rem;
            cursor: pointer;
            transition: 0.15s;
            box-shadow: 0 8px 18px #1f4b6e80;
            border: 1px solid #7bb3da;
            letter-spacing: 0.3px;
        }
        .btn-primary:hover {
            background: #0e5b88;
            box-shadow: 0 6px 14px #0d3955b0;
        }
        .btn-primary:active {
            transform: scale(0.97);
        }
        .meta-note {
            font-size: 0.85rem;
            color: #3c5a73;
            margin-top: 12px;
            display: flex;
            flex-wrap: wrap;
            gap: 12px;
            align-items: center;
        }
        .badge {
            background: #c4d9e9;
            border-radius: 40px;
            padding: 4px 14px;
            font-weight: 500;
        }
        .result-header {
            display: flex;
            justify-content: space-between;
            align-items: baseline;
            flex-wrap: wrap;
            margin: 20px 0 12px 0;
        }
        .result-header h2 {
            font-size: 1.5rem;
            font-weight: 500;
            color: #10344c;
            margin: 0;
        }
        #segment-count {
            background: #0e3044;
            color: white;
            border-radius: 60px;
            padding: 4px 20px;
            font-size: 0.9rem;
        }
        .compress-stats {
            background: #dfeef7;
            border-radius: 32px;
            padding: 4px 16px;
            font-size: 0.8rem;
            font-weight: 500;
            color: #0d4465;
        }
        .qr-viewer {
            background: white;
            border-radius: 36px;
            padding: 30px 20px 25px 20px;
            box-shadow: 0 12px 28px -12px rgba(0,42,78,0.3);
            border: 1px solid #d6e6f5;
            margin-top: 15px;
        }
        .canvas-wrapper {
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 280px;
            background: #f9fcff;
            border-radius: 32px;
            padding: 20px;
        }
        .canvas-wrapper canvas {
            border-radius: 24px;
            background: white;
            box-shadow: 0 6px 18px rgba(0,20,40,0.15);
            max-width: 100%;
            height: auto;
        }
        .nav-controls {
            display: flex;
            justify-content: center;
            align-items: center;
            gap: 25px;
            margin-top: 25px;
        }
        .nav-btn {
            background: #e3effa;
            border: none;
            padding: 10px 28px;
            border-radius: 60px;
            font-weight: 600;
            color: #114e77;
            cursor: pointer;
            font-size: 1rem;
            transition: 0.15s;
            border: 1px solid #b3cee8;
            box-shadow: 0 4px 6px #c0d8ec;
        }
        .nav-btn:hover:not(:disabled) {
            background: #cde2f2;
            box-shadow: 0 6px 10px #aac3db;
        }
        .nav-btn:disabled {
            opacity: 0.3;
            cursor: not-allowed;
            box-shadow: none;
        }
        #page-indicator {
            font-weight: 600;
            background: #114e77;
            color: white;
            padding: 6px 24px;
            border-radius: 60px;
            font-size: 1rem;
            letter-spacing: 0.5px;
            border: 1px solid #4892c1;
        }
        .placeholder-tip {
            color: #6b8caa;
            padding: 40px;
            text-align: center;
            width: 100%;
            font-size: 1.1rem;
        }
        .clear-btn {
            background: none;
            border: 1.5px solid #517e9f;
            color: #1e4e73;
            padding: 6px 18px;
            border-radius: 40px;
            font-weight: 500;
            cursor: pointer;
            transition: 0.2s;
        }
        .clear-btn:hover {
            background: #cde1f0;
        }

        .decode-panel {
            background: white;
            border-radius: 28px;
            padding: 28px;
            box-shadow: 0 10px 20px -10px rgba(0,40,70,0.2);
            border: 1px solid rgba(255,255,255,0.8);
        }
        .decode-panel textarea {
            width: 100%;
            padding: 20px;
            border-radius: 24px;
            border: 2px solid #d5e4f0;
            font-family: 'Monaco', 'Menlo', monospace;
            font-size: 14px;
            line-height: 1.5;
            resize: vertical;
            background: #f9fdff;
            margin-bottom: 20px;
        }
        .decode-panel textarea:focus {
            outline: none;
            border-color: #3f8ab8;
        }
        .decode-options {
            display: flex;
            flex-wrap: wrap;
            align-items: center;
            gap: 20px;
            margin-bottom: 25px;
            background: #ecf3fa;
            padding: 12px 20px;
            border-radius: 48px;
        }
        .file-name-row {
            display: flex;
            align-items: center;
            gap: 15px;
            flex-wrap: wrap;
            background: #ecf3fa;
            padding: 8px 20px;
            border-radius: 50px;
        }
        .file-name-row label {
            font-weight: 500;
            color: #15435e;
        }
        #decode-filename {
            border: none;
            background: white;
            padding: 8px 20px;
            border-radius: 40px;
            border: 1px solid #accae3;
            width: 200px;
            font-family: inherit;
        }
        .decode-status {
            margin-top: 25px;
            padding: 16px 22px;
            border-radius: 40px;
            background: #e4f0fa;
            color: #144a6b;
            font-weight: 500;
        }
        .decode-status.error {
            background: #ffded5;
            color: #a13b1e;
        }
        .btn-secondary {
            background: #619fcb;
            border: none;
            color: white;
            padding: 10px 30px;
            border-radius: 60px;
            font-weight: 600;
            cursor: pointer;
            transition: 0.15s;
            border: 1px solid #9fc9e7;
        }
        .btn-secondary:hover {
            background: #3683b7;
        }
        .footer-note {
            margin-top: 35px;
            text-align: center;
            font-size: 0.8rem;
            color: #5e7c97;
            border-top: 1px solid #bed6ea;
            padding-top: 22px;
        }
        .info-tip {
            font-size: 0.7rem;
            color: #4d7ca1;
            margin-left: 8px;
        }
    </style>
    <script src="https://cdn.jsdelivr.net/npm/qrcode-generator@1.4.4/qrcode.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/pako@2.1.0/dist/pako.min.js"></script>
</head>
<body>
<div class="card">
    <h1>&#128230; 文件 &#8596; 二维码 · 智能压缩</h1>
    <div class="sub">生成切片 / 从文本还原文件 · 支持Gzip压缩大幅减少二维码数量</div>

    <div class="tab-bar">
        <button class="tab-btn active" id="tab-generate">&#10024; 生成二维码</button>
        <button class="tab-btn" id="tab-decode">&#128275; 解码还原文件</button>
    </div>

    <!-- 生成面板 -->
    <div id="panel-generate" class="tab-panel active-panel">
        <div class="control-panel">
            <div class="file-area">
                <label for="file-input" class="file-label"><i>&#128206;</i> 选择文件</label>
                <input type="file" id="file-input" accept="*/*">
                <div class="file-info" id="file-info-display">未选择任何文件</div>
            </div>
            <div class="options">
                <div class="checkbox-wrapper">
                    <input type="checkbox" id="base64-checkbox" checked="checked">
                    <label for="base64-checkbox">&#128272; Base64编码</label>
                </div>
                <div class="checkbox-wrapper">
                    <input type="checkbox" id="gzip-checkbox" checked="checked">
                    <label for="gzip-checkbox">&#128476;&#65039; Gzip压缩 (大幅减少切片)</label>
                </div>
                <button class="btn-primary" id="generate-btn">&#9889; 生成二维码</button>
                <button class="clear-btn" id="clear-btn">&#128465;&#65039; 清空全部</button>
            </div>
            <div class="meta-note">
                <span class="badge">&#128208; 每个二维码 ≤2000字符 (字节模式)</span>
                <span class="badge" id="file-size-hint">文件大小: —</span>
                <span id="compress-info" class="compress-stats" style="display: none;"></span>
            </div>
        </div>

        <div class="result-header">
            <h2>&#128306; 二维码浏览</h2>
            <div style="display: flex; gap: 12px; align-items: center;">
                <span id="segment-count">0 个切片</span>
                <span id="qr-savings-tip" class="compress-stats" style="background:#cbe4fe;"></span>
            </div>
        </div>
        <div id="qr-viewer" class="qr-viewer">
            <div id="canvas-wrapper" class="canvas-wrapper">
                <div class="placeholder-tip">&#128070; 选择文件并点击生成 (可勾选Gzip压缩)</div>
            </div>
            <div class="nav-controls">
                <button id="prev-btn" class="nav-btn" disabled>&#9664; 上一张</button>
                <span id="page-indicator">0 / 0</span>
                <button id="next-btn" class="nav-btn" disabled>下一张 &#9654;</button>
            </div>
        </div>
    </div>

    <!-- 解码面板 -->
    <div id="panel-decode" class="tab-panel">
        <div class="decode-panel">
            <textarea id="decode-input" rows="10" placeholder="将二维码扫描得到的文本粘贴在这里&#10;&#9888;&#65039; 若生成时使用了 Gzip 压缩 / Base64,解码时请勾选下方对应选项&#10;【多个切片请按顺序连续粘贴,程序将自动拼接】"></textarea>
            
            <div class="decode-options">
                <div class="checkbox-wrapper">
                    <input type="checkbox" id="decode-base64-checkbox" checked="checked">
                    <label for="decode-base64-checkbox">&#128272; 内容已进行 Base64 编码</label>
                </div>
                <div class="checkbox-wrapper">
                    <input type="checkbox" id="decode-gzip-checkbox" checked="checked">
                    <label for="decode-gzip-checkbox">&#128476;&#65039; 需要 Gzip 解压 (生成时若勾选了压缩则必须勾选)</label>
                </div>
                <div class="file-name-row">
                    <label for="decode-filename">保存为:</label>
                    <input type="text" id="decode-filename" value="decoded.bin">
                </div>
            </div>

            <div style="display: flex; gap: 20px; align-items: center; flex-wrap: wrap;">
                <button class="btn-primary" id="decode-btn">&#128229; 解码并下载文件</button>
                <button class="clear-btn" id="decode-clear-btn">&#129529; 清空输入</button>
            </div>

            <div id="decode-status" class="decode-status">等待操作……</div>
        </div>
    </div>

    <div class="footer-note">
        &#9889; Gzip压缩 + Base64 推荐组合:显著减少二维码数量且文本便于复制。解码时记得勾选相同选项。
    </div>
</div>

<script>
    (function() {
        // ---------- 辅助函数 ----------
        function binaryStringToUint8Array(str) {
            const len = str.length;
            const bytes = new Uint8Array(len);
            for (let i = 0; i < len; i++) {
                bytes[i] = str.charCodeAt(i) & 0xFF;
            }
            return bytes;
        }

        function uint8ArrayToBinaryString(uint8arr) {
            let binary = '';
            const chunkSize = 8192;
            for (let i = 0; i < uint8arr.length; i += chunkSize) {
                const chunk = uint8arr.subarray(i, i + chunkSize);
                binary += String.fromCharCode.apply(null, chunk);
            }
            return binary;
        }

        function arrayBufferToBase64(buffer) {
            const bytes = new Uint8Array(buffer);
            let binary = '';
            for (let i = 0; i < bytes.length; i++) {
                binary += String.fromCharCode(bytes[i]);
            }
            return btoa(binary);
        }

        // 生成模块
        (function setupGenerate() {
            const MAX_CHUNK_LENGTH = 2000;
            const fileInput = document.getElementById('file-input');
            const fileInfoDiv = document.getElementById('file-info-display');
            const base64Check = document.getElementById('base64-checkbox');
            const gzipCheck = document.getElementById('gzip-checkbox');
            const generateBtn = document.getElementById('generate-btn');
            const clearBtn = document.getElementById('clear-btn');
            const segmentCountSpan = document.getElementById('segment-count');
            const fileSizeHint = document.getElementById('file-size-hint');
            const canvasWrapper = document.getElementById('canvas-wrapper');
            const prevBtn = document.getElementById('prev-btn');
            const nextBtn = document.getElementById('next-btn');
            const pageIndicator = document.getElementById('page-indicator');
            const compressInfoSpan = document.getElementById('compress-info');
            const qrSavingsTip = document.getElementById('qr-savings-tip');

            let qrCanvasArray = [];
            let currentIndex = 0;
            let totalChunks = 0;

            function drawQrToCanvas(qr, cellSize = 5) {
                const size = qr.getModuleCount();
                const canvas = document.createElement('canvas');
                canvas.width = size * cellSize;
                canvas.height = size * cellSize;
                const ctx = canvas.getContext('2d');
                ctx.fillStyle = '#ffffff';
                ctx.fillRect(0, 0, canvas.width, canvas.height);
                ctx.fillStyle = '#0b2b3f';
                for (let row = 0; row < size; row++) {
                    for (let col = 0; col < size; col++) {
                        if (qr.isDark(row, col)) {
                            ctx.fillRect(col * cellSize, row * cellSize, cellSize, cellSize);
                        }
                    }
                }
                return canvas;
            }

            function generateQrCanvasFromString(chunkString) {
                try {
                    const qr = qrcode(0, 'L');
                    qr.addData(chunkString, 'Byte');
                    qr.make();
                    return drawQrToCanvas(qr, 5);
                } catch (e) {
                    console.warn('二维码生成失败', e);
                    const errCanvas = document.createElement('canvas');
                    errCanvas.width = 250;
                    errCanvas.height = 250;
                    const ctx = errCanvas.getContext('2d');
                    ctx.fillStyle = '#ffe0d0';
                    ctx.fillRect(0, 0, 250, 250);
                    ctx.font = 'bold 16px sans-serif';
                    ctx.fillStyle = '#a13e0b';
                    ctx.textAlign = 'center';
                    ctx.fillText('&#10060; 生成失败', 125, 130);
                    return errCanvas;
                }
            }

            function displayCurrentQr() {
                canvasWrapper.innerHTML = '';
                if (qrCanvasArray.length === 0 || totalChunks === 0) {
                    canvasWrapper.innerHTML = '<div class="placeholder-tip">&#128237; 没有二维码可显示</div>';
                    pageIndicator.textContent = `0 / 0`;
                    prevBtn.disabled = true;
                    nextBtn.disabled = true;
                    return;
                }
                canvasWrapper.appendChild(qrCanvasArray[currentIndex]);
                pageIndicator.textContent = `${currentIndex + 1} / ${totalChunks}`;
                prevBtn.disabled = (currentIndex === 0);
                nextBtn.disabled = (currentIndex === totalChunks - 1);
            }

            function resetViewer(keepStats = false) {
                qrCanvasArray = [];
                currentIndex = 0;
                totalChunks = 0;
                segmentCountSpan.textContent = '0 个切片';
                if (!keepStats) {
                    compressInfoSpan.style.display = 'none';
                    qrSavingsTip.textContent = '';
                }
                displayCurrentQr();
            }

            function updateFileInfo() {
                if (fileInput.files.length > 0) {
                    const file = fileInput.files[0];
                    const size = file.size;
                    let sizeText = '';
                    if (size < 1024) sizeText = size + ' B';
                    else if (size < 1024 * 1024) sizeText = (size / 1024).toFixed(1) + ' KB';
                    else sizeText = (size / (1024 * 1024)).toFixed(2) + ' MB';
                    fileInfoDiv.textContent = `&#128196; ${file.name} (${sizeText})`;
                    fileSizeHint.textContent = `文件大小: ${sizeText}`;
                } else {
                    fileInfoDiv.textContent = '未选择任何文件';
                    fileSizeHint.textContent = '文件大小: —';
                }
            }

            async function handleGenerate() {
                if (fileInput.files.length === 0) {
                    alert('请先选择一个文件');
                    return;
                }
                const file = fileInput.files[0];
                const useBase64 = base64Check.checked;
                const useGzip = gzipCheck.checked;
                const originalSize = file.size;

                const reader = new FileReader();
                reader.onload = function(e) {
                    try {
                        const arrayBuffer = e.target.result;
                        let processedData;      // Uint8Array (最终二进制)
                        let finalString;         // 待切片的字符串

                        // 1. 是否压缩
                        if (useGzip) {
                            if (!window.pako) throw new Error('pako库未加载,无法进行Gzip压缩');
                            const rawBytes = new Uint8Array(arrayBuffer);
                            const compressed = pako.deflate(rawBytes);
                            processedData = compressed;
                            const compressRatio = ((1 - compressed.length / originalSize) * 100).toFixed(1);
                            compressInfoSpan.style.display = 'inline-block';
                            compressInfoSpan.innerHTML = `&#128476;&#65039; 压缩后: ${(compressed.length/1024).toFixed(1)} KB (${compressRatio}% 减小)`;
                        } else {
                            processedData = new Uint8Array(arrayBuffer);
                            compressInfoSpan.style.display = 'none';
                        }

                        // 2. 转为最终字符串 (Base64 或 二进制字符串)
                        if (useBase64) {
                            let binaryForB64 = uint8ArrayToBinaryString(processedData);
                            finalString = btoa(binaryForB64);
                        } else {
                            finalString = uint8ArrayToBinaryString(processedData);
                        }

                        const totalLength = finalString.length;
                        if (totalLength === 0) throw new Error('内容为空');

                        const chunks = Math.ceil(totalLength / MAX_CHUNK_LENGTH);
                        segmentCountSpan.textContent = `${chunks} 个切片`;

                        // 计算节省提示(对比未压缩且未base64的理论原始切片数)
                        if (useGzip) {
                            const rawWithoutCompress = originalSize; // 原始字节
                            const rawChunksWithout = Math.ceil(rawWithoutCompress / MAX_CHUNK_LENGTH);
                            if (rawChunksWithout > chunks) {
                                qrSavingsTip.textContent = `&#10024; 相比未压缩节省 ${rawChunksWithout - chunks} 个二维码 (减少${Math.round((1 - chunks/rawChunksWithout)*100)}%)`;
                            } else {
                                qrSavingsTip.textContent = `&#128476;&#65039; 压缩后切片数 ${chunks}`;
                            }
                        } else {
                            qrSavingsTip.textContent = ``;
                        }

                        if (chunks > 80) {
                            if (!confirm(`将生成 ${chunks} 个二维码,较多,继续吗?`)) {
                                resetViewer();
                                segmentCountSpan.textContent = '已取消';
                                return;
                            }
                        }

                        const newCanvasArray = [];
                        for (let i = 0; i < chunks; i++) {
                            const start = i * MAX_CHUNK_LENGTH;
                            const end = Math.min(start + MAX_CHUNK_LENGTH, totalLength);
                            const chunk = finalString.substring(start, end);
                            const canvas = generateQrCanvasFromString(chunk);
                            newCanvasArray.push(canvas);
                        }
                        qrCanvasArray = newCanvasArray;
                        totalChunks = chunks;
                        currentIndex = 0;
                        displayCurrentQr();
                    } catch (err) {
                        console.error(err);
                        alert(`生成失败: ${err.message || '未知错误'}`);
                        resetViewer();
                    }
                };
                reader.onerror = () => { alert('文件读取失败'); resetViewer(); };
                reader.readAsArrayBuffer(file);
            }

            fileInput.addEventListener('change', function() {
                updateFileInfo();
                resetViewer();
                if (fileInput.files.length > 0 && fileInput.files[0].size > 30 * 1024 * 1024) {
                    if (!confirm('文件超过30MB,压缩和编码可能较慢,继续?')) {
                        fileInput.value = '';
                        updateFileInfo();
                    }
                }
            });

            generateBtn.addEventListener('click', handleGenerate);
            clearBtn.addEventListener('click', function() {
                fileInput.value = '';
                updateFileInfo();
                resetViewer();
                compressInfoSpan.style.display = 'none';
                qrSavingsTip.textContent = '';
            });
            prevBtn.addEventListener('click', () => { if (currentIndex > 0) { currentIndex--; displayCurrentQr(); } });
            nextBtn.addEventListener('click', () => { if (currentIndex < totalChunks - 1) { currentIndex++; displayCurrentQr(); } });

            updateFileInfo();
            resetViewer();
        })();

        // 解码模块 (支持 Gzip 解压)
        (function setupDecode() {
            const decodeInput = document.getElementById('decode-input');
            const decodeBase64Check = document.getElementById('decode-base64-checkbox');
            const decodeGzipCheck = document.getElementById('decode-gzip-checkbox');
            const decodeFilename = document.getElementById('decode-filename');
            const decodeBtn = document.getElementById('decode-btn');
            const decodeClearBtn = document.getElementById('decode-clear-btn');
            const decodeStatus = document.getElementById('decode-status');

            decodeClearBtn.addEventListener('click', () => {
                decodeInput.value = '';
                decodeStatus.textContent = '输入已清空';
                decodeStatus.className = 'decode-status';
            });

            decodeBtn.addEventListener('click', async () => {
                const rawText = decodeInput.value;
                if (!rawText.trim()) {
                    decodeStatus.textContent = '&#10060; 请输入要解码的文本(二维码扫描结果)';
                    decodeStatus.className = 'decode-status error';
                    return;
                }

                try {
                    let intermediateBytes;   // Uint8Array (解码base64或直接转换后的原始字节流,可能是压缩数据也可能是最终数据)
                    const useBase64 = decodeBase64Check.checked;
                    const useGzip = decodeGzipCheck.checked;

                    // 1. 从文本还原为二进制数据 (Uint8Array)
                    if (useBase64) {
                        // 拼接所有行 (忽略空行)
                        const lines = rawText.split(/\r?\n/).filter(l => l.trim() !== '');
                        if (lines.length === 0) throw new Error('没有有效的Base64数据行');
                        const fullBase64 = lines.join('');
                        const binaryStr = atob(fullBase64);
                        intermediateBytes = binaryStringToUint8Array(binaryStr);
                    } else {
                        // 直接将整个文本视为二进制字符串 (latin1)
                        intermediateBytes = binaryStringToUint8Array(rawText);
                    }

                    // 2. 如果需要 Gzip 解压
                    let finalBytes = intermediateBytes;
                    if (useGzip) {
                        if (!window.pako) throw new Error('pako库未加载,无法解压Gzip数据');
                        try {
                            finalBytes = pako.inflate(intermediateBytes);
                        } catch (inflateErr) {
                            throw new Error(`Gzip解压失败: ${inflateErr.message},请确认生成时是否启用了压缩并正确勾选解压选项`);
                        }
                    }

                    // 3. 下载文件
                    const blob = new Blob([finalBytes], { type: 'application/octet-stream' });
                    const url = URL.createObjectURL(blob);
                    const a = document.createElement('a');
                    a.href = url;
                    a.download = decodeFilename.value.trim() || 'decoded.bin';
                    document.body.appendChild(a);
                    a.click();
                    document.body.removeChild(a);
                    URL.revokeObjectURL(url);

                    let sizeInfo = `${finalBytes.length} 字节`;
                    if (useGzip) sizeInfo += ` (已解压)`;
                    decodeStatus.textContent = `&#9989; 解码成功!文件大小: ${sizeInfo},已触发下载。`;
                    decodeStatus.className = 'decode-status';
                } catch (e) {
                    console.error(e);
                    let errMsg = e.message || '解码失败,请检查Base64格式 / Gzip选项是否正确';
                    if (errMsg.includes('Invalid character') || errMsg.includes('atob')) errMsg = 'Base64解码失败,请确认文本是否为有效Base64,或取消Base64选项';
                    decodeStatus.textContent = `&#10060; 解码失败: ${errMsg}`;
                    decodeStatus.className = 'decode-status error';
                }
            });

            decodeStatus.textContent = '等待操作…… (勾选Gzip解压前请确认生成时启用了压缩)';
        })();

        // 标签页切换
        const tabGenerate = document.getElementById('tab-generate');
        const tabDecode = document.getElementById('tab-decode');
        const panelGenerate = document.getElementById('panel-generate');
        const panelDecode = document.getElementById('panel-decode');

        tabGenerate.addEventListener('click', () => {
            tabGenerate.classList.add('active');
            tabDecode.classList.remove('active');
            panelGenerate.classList.add('active-panel');
            panelDecode.classList.remove('active-panel');
        });
        tabDecode.addEventListener('click', () => {
            tabDecode.classList.add('active');
            tabGenerate.classList.remove('active');
            panelDecode.classList.add('active-panel');
            panelGenerate.classList.remove('active-panel');
        });
    })();
</script>
</body>
</html>
DNZJZ52PJ 发表于 2026-5-15 17:24
如果是纯文字的话随便找个二维码生成工具就好了,扫码就能看到文字,推荐草料二维码,如果文字非常多的话就推荐第二种方式,把文件放到第三方平台比如腾讯文档,或者百度云,然后分享出来一个链接,把链接生成二维码就可以了,如果要确保内容安全可以自己用AI搭建一个网站或者在局域网部署一个服务
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-5-16 08:19

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表