<?xml version="1.0" encoding="utf-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"><channel><title>YLong软件</title><link>http://ylongsoft.com/</link><description>网址打包APP, 全景KRPano可是化编辑器, IOS重签名加密工具</description><item><title>HTML打包APK：页面打印功能实现方法</title><link>http://ylongsoft.com/post/html-apk-print-zblog-ylongsoft.html</link><description>&lt;p&gt;在网页中，一行 &lt;code&gt;window.print()&lt;/code&gt; 就能调起浏览器的打印功能。在HTML一键打包APK工具生成的APP中，这行代码同样有效：工具会拦截 &lt;code&gt;window.print()&lt;/code&gt; 调用，并转交给安卓系统打印服务。用户可以直接选择打印机打印，也可以&amp;quot;另存为PDF&amp;quot;把页面保存成PDF文件。&lt;/p&gt;
&lt;p&gt;如果你不了解HTML打包APK工具, 想要进一步了解, 可以查看官网:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://html2apk.leapever.com/&quot;&gt;HTML一键打包APK工具官网&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;创建打印页面&lt;/h2&gt;
&lt;p&gt;在HTML项目入口目录中创建 &lt;code&gt;index.html&lt;/code&gt;，写入以下代码。页面展示一张简单的单据，点击&amp;quot;打印&amp;quot;按钮后调用 &lt;code&gt;window.print()&lt;/code&gt; 触发打印。通过 &lt;code&gt;@media print&lt;/code&gt; 样式，打印时会自动隐藏按钮等屏幕元素，只保留单据内容。&lt;/p&gt;
&lt;pre class=&quot;prism-highlight prism-language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;!doctype html&amp;gt;
&amp;lt;html lang=&amp;quot;zh-CN&amp;quot;&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
        &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1, viewport-fit=cover&amp;quot; /&amp;gt;
        &amp;lt;title&amp;gt;单据打印&amp;lt;/title&amp;gt;
        &amp;lt;style&amp;gt;
            * {
                box-sizing: border-box;
            }

            body {
                min-height: 100vh;
                margin: 0;
                padding: 20px;
                display: grid;
                place-items: center;
                font-family: 'Microsoft YaHei', sans-serif;
                background: #f4f7f5;
                color: #17221c;
            }

            main {
                width: min(100%, 440px);
            }

            .sheet {
                padding: 24px;
                border: 1px solid #cad4ce;
                border-radius: 8px;
                background: #fff;
            }

            h1 {
                margin: 0 0 4px;
                font-size: 24px;
                text-align: center;
            }

            .sheet-meta {
                margin: 0 0 16px;
                font-size: 13px;
                color: #526159;
                text-align: center;
            }

            table {
                width: 100%;
                border-collapse: collapse;
                font-size: 14px;
            }

            th,
            td {
                padding: 8px 6px;
                border-bottom: 1px solid #e2e8e4;
                text-align: left;
            }

            th {
                background: #eef2f0;
            }

            .total {
                margin-top: 12px;
                text-align: right;
                font-size: 16px;
                font-weight: 700;
            }

            button {
                width: 100%;
                min-height: 48px;
                margin-top: 14px;
                border: 0;
                border-radius: 6px;
                background: #197447;
                color: white;
                font: inherit;
                font-weight: 700;
            }

            /* 打印相关样式 */
            @media print {
                body {
                    padding: 0;
                    background: #fff;
                }

                /* 打印时隐藏按钮，只保留单据 */
                .no-print {
                    display: none;
                }

                .sheet {
                    width: 100%;
                    border: 0;
                    border-radius: 0;
                }
            }
        &amp;lt;/style&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;main&amp;gt;
            &amp;lt;div class=&amp;quot;sheet&amp;quot;&amp;gt;
                &amp;lt;h1&amp;gt;购物清单&amp;lt;/h1&amp;gt;
                &amp;lt;p class=&amp;quot;sheet-meta&amp;quot;&amp;gt;单号：20260824001　日期：2026-08-24&amp;lt;/p&amp;gt;
                &amp;lt;table&amp;gt;
                    &amp;lt;thead&amp;gt;
                        &amp;lt;tr&amp;gt;
                            &amp;lt;th&amp;gt;品名&amp;lt;/th&amp;gt;
                            &amp;lt;th&amp;gt;数量&amp;lt;/th&amp;gt;
                            &amp;lt;th&amp;gt;金额&amp;lt;/th&amp;gt;
                        &amp;lt;/tr&amp;gt;
                    &amp;lt;/thead&amp;gt;
                    &amp;lt;tbody&amp;gt;
                        &amp;lt;tr&amp;gt;
                            &amp;lt;td&amp;gt;无线鼠标&amp;lt;/td&amp;gt;
                            &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;
                            &amp;lt;td&amp;gt;79.00&amp;lt;/td&amp;gt;
                        &amp;lt;/tr&amp;gt;
                        &amp;lt;tr&amp;gt;
                            &amp;lt;td&amp;gt;机械键盘&amp;lt;/td&amp;gt;
                            &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;
                            &amp;lt;td&amp;gt;299.00&amp;lt;/td&amp;gt;
                        &amp;lt;/tr&amp;gt;
                        &amp;lt;tr&amp;gt;
                            &amp;lt;td&amp;gt;USB数据线&amp;lt;/td&amp;gt;
                            &amp;lt;td&amp;gt;2&amp;lt;/td&amp;gt;
                            &amp;lt;td&amp;gt;30.00&amp;lt;/td&amp;gt;
                        &amp;lt;/tr&amp;gt;
                    &amp;lt;/tbody&amp;gt;
                &amp;lt;/table&amp;gt;
                &amp;lt;p class=&amp;quot;total&amp;quot;&amp;gt;合计：408.00 元&amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;button class=&amp;quot;no-print&amp;quot; type=&amp;quot;button&amp;quot; onclick=&amp;quot;window.print()&amp;quot;&amp;gt;打印&amp;lt;/button&amp;gt;
        &amp;lt;/main&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;代码说明：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;点击&amp;quot;打印&amp;quot;按钮执行 &lt;code&gt;window.print()&lt;/code&gt;，这是整个打印功能的核心，不需要引入任何第三方库。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@media print&lt;/code&gt; 中定义了打印时的样式，&lt;code&gt;display: none&lt;/code&gt; 的 &lt;code&gt;.no-print&lt;/code&gt; 元素（按钮等）不会出现在打印内容中。&lt;/li&gt;
&lt;li&gt;单据表格、金额等内容在屏幕和打印中都会完整保留。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;打包APK&lt;/h2&gt;
&lt;p&gt;打开HTML一键打包APK工具，选择包含 &lt;code&gt;index.html&lt;/code&gt; 的项目目录，填写应用名称、包名等打包信息。打印功能依赖安卓系统打印服务，无需额外勾选权限，按默认配置打包即可。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/08/1787574900146_fbe902d2.jpg&quot; alt=&quot;打包APK生成应用&quot;&gt;&lt;/p&gt;
&lt;p&gt;确认配置后开始打包，等待工具生成APK文件。&lt;/p&gt;
&lt;h2&gt;安装并测试打印&lt;/h2&gt;
&lt;p&gt;将生成的APK安装到安卓手机，打开APP点击&amp;quot;打印&amp;quot;按钮，系统会弹出打印预览界面。在这里可以：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;选择已连接的打印机（云打印机、局域网打印机等）直接打印；&lt;/li&gt;
&lt;li&gt;在打印机列表中选择&amp;quot;另存为PDF&amp;quot;，把页面保存成PDF文件。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;保存的PDF文件可以在手机的&amp;quot;文件&amp;quot;应用或相册中找到，方便发送给客户或存档。&lt;/p&gt;
&lt;h2&gt;常见问题&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;点击打印没有反应？&lt;/strong&gt; 请确认手机系统为Android 5.0及以上，旧系统没有内置打印服务。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;列表中找不到打印机？&lt;/strong&gt; 安卓打印服务需要打印机与手机处于同一网络，或打印机已接入云打印服务。也可以直接使用&amp;quot;另存为PDF&amp;quot;，不依赖任何打印机。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;打印内容不完整或分页异常？&lt;/strong&gt; 在 &lt;code&gt;@media print&lt;/code&gt; 中适当调整宽度、字号和间距，避免内容超出纸张范围；需要强制分页时，可以在指定元素上添加 &lt;code&gt;page-break-after: always&lt;/code&gt; 样式。&lt;/p&gt;
</description><pubDate>Mon, 24 Aug 2026 20:34:58 +0800</pubDate></item><item><title>HTML打包EXE退出密码设置指南</title><link>http://ylongsoft.com/post/html2exe-exit-password-guide.html</link><description>&lt;p&gt;HTML打包EXE工具提供了&lt;strong&gt;退出密码&lt;/strong&gt;功能。启用后，打包生成的 EXE 在用户点击关闭按钮时，不会直接退出，而是先弹出密码输入窗口，只有输入正确的退出密码才能真正关闭程序。&lt;/p&gt;
&lt;p&gt;对于放置在公共场所的展示机、教学机房的课件程序、无人值守的查询设备等场景，这个功能可以有效防止程序被误操作或他人随意关闭，保证内容持续稳定地展示和运行。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/08/1787145032842_6520dd46.png&quot; alt=&quot;退出密码配置&quot;&gt;&lt;/p&gt;
&lt;h2&gt;下载最新版本&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://api.leapever.com/packerv2/download&quot;&gt;立即下载最新版本&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://leapever.com/intro/html2exe&quot;&gt;HTML打包EXE工具官网&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;适用场景&lt;/h2&gt;
&lt;p&gt;退出密码功能适合需要让程序保持长时间运行、不希望被随意关闭的使用方式：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;展厅展示机&lt;/strong&gt;：触摸屏一体机循环播放展示内容，防止参观者误触关闭按钮退出程序。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;教学机房&lt;/strong&gt;：学生机上的课件程序保持打开状态，避免学生上课途中关闭课件。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;无人值守设备&lt;/strong&gt;：自助查询机、叫号机等设备需要程序持续运行，只有管理人员输入密码才能退出维护。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;儿童使用场景&lt;/strong&gt;：孩子使用电脑学习时，防止其随手关闭学习软件去做其他操作。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;会议和活动现场&lt;/strong&gt;：现场大屏播放的程序避免被工作人员误关，影响活动流程。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;简单来说，只要你的程序需要&amp;quot;一直开着&amp;quot;，退出密码就能帮上忙。&lt;/p&gt;
&lt;h2&gt;配置步骤&lt;/h2&gt;
&lt;p&gt;退出密码位于打包工具的&lt;strong&gt;静态密码&lt;/strong&gt;标签页中，配置步骤如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;打开 HTML打包EXE 工具，切换到&lt;strong&gt;静态密码&lt;/strong&gt;标签页。&lt;/li&gt;
&lt;li&gt;勾选&lt;strong&gt;启用退出密码功能&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;勾选后，下方的&lt;strong&gt;退出密码&lt;/strong&gt;输入框会变为可编辑状态，输入 1-16 位字母或数字作为退出密码。&lt;/li&gt;
&lt;li&gt;正常执行打包操作，生成 EXE。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;需要注意的是，&lt;strong&gt;未勾选&amp;quot;启用退出密码功能&amp;quot;时，退出密码输入框为灰色不可编辑状态&lt;/strong&gt;。只有先勾选启用，才能填写和修改密码，避免误填了密码却没有实际启用功能的情况。&lt;/p&gt;
&lt;h2&gt;用户看到的效果&lt;/h2&gt;
&lt;p&gt;打包生成的 EXE 运行后，用户点击窗口关闭按钮时，会弹出&amp;quot;需要退出密码&amp;quot;的提示窗口，要求输入退出密码。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/08/1787145033254_d47365a6.png&quot; alt=&quot;退出密码弹窗效果&quot;&gt;&lt;/p&gt;
&lt;p&gt;用户可以选择：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;输入正确的退出密码并确认，程序正常退出。&lt;/li&gt;
&lt;li&gt;输入错误密码，程序不会退出，需要重新输入。&lt;/li&gt;
&lt;li&gt;点击取消，放弃退出操作，程序继续运行。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;也就是说，不知道密码的用户无法通过常规方式关闭程序，只有掌握密码的管理人员才能退出。&lt;/p&gt;
&lt;h2&gt;和静态密码的区别&lt;/h2&gt;
&lt;p&gt;静态密码和退出密码位于同一个标签页，但作用阶段不同：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;对比项&lt;/th&gt;
&lt;th&gt;静态密码&lt;/th&gt;
&lt;th&gt;退出密码&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;作用时机&lt;/td&gt;
&lt;td&gt;打开程序时&lt;/td&gt;
&lt;td&gt;关闭程序时&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;作用&lt;/td&gt;
&lt;td&gt;输入正确密码才能进入软件&lt;/td&gt;
&lt;td&gt;输入正确密码才能退出软件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;适合场景&lt;/td&gt;
&lt;td&gt;限制谁可以使用软件&lt;/td&gt;
&lt;td&gt;防止软件被随意关闭&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;自定义信息&lt;/td&gt;
&lt;td&gt;支持配置激活弹窗显示的信息&lt;/td&gt;
&lt;td&gt;使用固定的提示信息&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;两者可以单独使用，也可以同时启用。例如一个付费展示项目，可以设置静态密码限制只有付费用户才能打开，再设置退出密码防止展示过程中被关闭，实现&amp;quot;进得来就出不去&amp;quot;的双重保护。&lt;/p&gt;
&lt;h2&gt;使用建议&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;妥善保管密码&lt;/strong&gt;：退出密码设置后只有自己知道，请务必记录保存。如果忘记密码，程序将无法正常退出。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;使用容易输入的密码&lt;/strong&gt;：密码支持 1-16 位字母和数字，建议使用便于记忆和快速输入的组合，方便现场维护人员操作。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;配合全屏使用效果更好&lt;/strong&gt;：对于展示机场景，建议同时勾选&lt;strong&gt;全屏&lt;/strong&gt;选项，隐藏窗口的放大、缩小、关闭按钮，配合退出密码形成更完整的防关闭方案。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;注意内核支持&lt;/strong&gt;：退出密码功能仅 Chrome 内核支持，打包时请确认内核选择为 Chrome 内核。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;定期更换密码&lt;/strong&gt;：如果程序长期放置在公共场所，建议定期更换退出密码并重新打包，避免密码泄露。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;HTML打包EXE工具的退出密码功能，配置简单但非常实用：勾选启用、填写密码、重新打包，三步即可让生成的 EXE 具备防关闭能力。&lt;/p&gt;
&lt;p&gt;对于展厅展示、教学机房、无人值守设备等需要程序持续运行的场景，退出密码可以有效避免误操作和恶意关闭。如果再配合静态密码、全屏等选项一起使用，就能让打包出来的程序既安全又稳定。&lt;/p&gt;
&lt;h2&gt;相关阅读&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;./html2exe-encryption-comparison.md&quot;&gt;HTML打包EXE三种加密方式对比&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;./html2exe-encrypt-tutorial.md&quot;&gt;离线一机一码使用详细教程&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;./offline-vs-network-verification.md&quot;&gt;离线一机一码验证和网络验证的区别&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description><pubDate>Wed, 19 Aug 2026 21:10:33 +0800</pubDate></item><item><title>用HTML打包APK制作视频播放器</title><link>http://ylongsoft.com/post/html-apk-video-fullscreen-player.html</link><description>&lt;p&gt;HTML页面可以通过 &lt;code&gt;video&lt;/code&gt; 标签播放网络和本地视频，结合浏览器全屏接口实现最大化播放，并在全屏时锁定横屏获得更好的观看体验。完成页面代码后，再通过HTML一键打包APK工具配置网络权限，即可生成支持视频播放和全屏播放的安卓APP。&lt;/p&gt;&lt;p&gt;工具官网：&lt;a href=&quot;https://html2apk.leapever.com/&quot;&gt;HTML一键打包APK工具官网&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;创建视频播放页面&lt;/h2&gt;&lt;p&gt;在项目入口目录创建 &lt;code&gt;index.html&lt;/code&gt;，包含视频播放区、最大化按钮和地址输入。&lt;/p&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;视频播放：使用 &lt;code&gt;video&lt;/code&gt; 标签加载视频，添加 &lt;code&gt;playsinline&lt;/code&gt; 属性保证内联播放。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;最大化按钮：调用全屏接口，兼容WebView。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;横屏锁定：全屏时自动横屏，退出竖屏。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;地址加载：输入视频直链，切换测试视频。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;视频标签需要添加 &lt;code&gt;playsinline&lt;/code&gt;、&lt;code&gt;webkit-playsinline&lt;/code&gt; 和 &lt;code&gt;x5-playsinline&lt;/code&gt; 属性，避免跳转系统播放器：&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-html&quot;&gt;&amp;lt;video&amp;nbsp;playsinline&amp;nbsp;webkit-playsinline&amp;nbsp;x5-playsinline&amp;nbsp;src=&amp;quot;video.mp4&amp;quot;&amp;gt;&amp;lt;/video&amp;gt;&lt;/pre&gt;&lt;p&gt;最大化按钮通过全屏接口处理标准接口和 &lt;code&gt;webkit&lt;/code&gt; 前缀接口：&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-javascript&quot;&gt;if&amp;nbsp;(shell.requestFullscreen)&amp;nbsp;shell.requestFullscreen();
else&amp;nbsp;if&amp;nbsp;(shell.webkitRequestFullscreen)&amp;nbsp;shell.webkitRequestFullscreen();&lt;/pre&gt;&lt;h2&gt;打包成APK&lt;/h2&gt;&lt;p&gt;保存代码到video-test.html，用工具载入并点击打包：&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/08/1786361556729_009c39d2.png&quot; alt=&quot;把html打包成apk&quot;/&gt;&lt;/p&gt;&lt;h2&gt;测试功能&lt;/h2&gt;&lt;p&gt;安装APK后测试：&lt;/p&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;默认视频加载，播放暂停正常。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;点最大化按钮进入全屏。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;全屏时横屏锁定，退出恢复竖屏。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;输入其他mp4链接，切换加载。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;拖动进度条，从指定位置播放。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;推荐H.264+AAC编码的mp4，避免HEVC等兼容问题。&lt;/p&gt;&lt;p&gt;如果全屏失效，检查系统横屏限制；视频加载失败，确认网络权限和地址可用。自动播放需要用户交互。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/08/1786361557143_3e5d1044.png&quot; alt=&quot;视频播放和最大化页面&quot;/&gt;&lt;/p&gt;&lt;p&gt;完整示例：&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-html&quot;&gt;&amp;lt;!DOCTYPE&amp;nbsp;html&amp;gt;
&amp;lt;html&amp;nbsp;lang=&amp;quot;zh-CN&amp;quot;&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;meta&amp;nbsp;charset=&amp;quot;UTF-8&amp;quot;&amp;gt;
&amp;lt;meta&amp;nbsp;name=&amp;quot;viewport&amp;quot;&amp;nbsp;content=&amp;quot;width=device-width,&amp;nbsp;initial-scale=1.0,&amp;nbsp;maximum-scale=1.0,&amp;nbsp;user-scalable=no&amp;quot;&amp;gt;
&amp;lt;title&amp;gt;视频播放测试&amp;lt;/title&amp;gt;
&amp;lt;style&amp;gt;
&amp;nbsp;&amp;nbsp;*&amp;nbsp;{&amp;nbsp;margin:&amp;nbsp;0;&amp;nbsp;padding:&amp;nbsp;0;&amp;nbsp;box-sizing:&amp;nbsp;border-box;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;html,&amp;nbsp;body&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;width:&amp;nbsp;100%;&amp;nbsp;height:&amp;nbsp;100%;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background:&amp;nbsp;#000;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-family:&amp;nbsp;-apple-system,&amp;nbsp;&amp;quot;PingFang&amp;nbsp;SC&amp;quot;,&amp;nbsp;&amp;quot;Microsoft&amp;nbsp;YaHei&amp;quot;,&amp;nbsp;sans-serif;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;overflow:&amp;nbsp;hidden;
&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;#player-wrap&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;position:&amp;nbsp;relative;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;width:&amp;nbsp;100%;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;height:&amp;nbsp;100%;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;display:&amp;nbsp;flex;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flex-direction:&amp;nbsp;column;
&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;/*&amp;nbsp;视频区域&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;#video-box&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;position:&amp;nbsp;relative;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flex:&amp;nbsp;1;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background:&amp;nbsp;#000;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;display:&amp;nbsp;flex;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;align-items:&amp;nbsp;center;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;justify-content:&amp;nbsp;center;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;overflow:&amp;nbsp;hidden;
&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;video&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;width:&amp;nbsp;100%;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;height:&amp;nbsp;100%;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;object-fit:&amp;nbsp;contain;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background:&amp;nbsp;#000;
&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;/*&amp;nbsp;最大化按钮（悬浮在视频右上角）&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;#btn-fullscreen&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;position:&amp;nbsp;absolute;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;top:&amp;nbsp;12px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;right:&amp;nbsp;12px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;z-index:&amp;nbsp;20;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;width:&amp;nbsp;44px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;height:&amp;nbsp;44px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;none;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border-radius:&amp;nbsp;8px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background:&amp;nbsp;rgba(0,&amp;nbsp;0,&amp;nbsp;0,&amp;nbsp;0.55);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;#fff;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;display:&amp;nbsp;flex;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;align-items:&amp;nbsp;center;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;justify-content:&amp;nbsp;center;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor:&amp;nbsp;pointer;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-webkit-tap-highlight-color:&amp;nbsp;transparent;
&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;#btn-fullscreen:active&amp;nbsp;{&amp;nbsp;background:&amp;nbsp;rgba(255,&amp;nbsp;255,&amp;nbsp;255,&amp;nbsp;0.25);&amp;nbsp;}
&amp;nbsp;&amp;nbsp;#btn-fullscreen&amp;nbsp;svg&amp;nbsp;{&amp;nbsp;width:&amp;nbsp;24px;&amp;nbsp;height:&amp;nbsp;24px;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;/*&amp;nbsp;底部控制栏&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;#controls&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;position:&amp;nbsp;relative;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;z-index:&amp;nbsp;10;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background:&amp;nbsp;#111;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;padding:&amp;nbsp;10px&amp;nbsp;12px&amp;nbsp;calc(10px&amp;nbsp;+&amp;nbsp;env(safe-area-inset-bottom));
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;display:&amp;nbsp;flex;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flex-direction:&amp;nbsp;column;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;gap:&amp;nbsp;10px;
&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;#btn-play&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;width:&amp;nbsp;100%;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;padding:&amp;nbsp;14px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;17px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;#fff;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background:&amp;nbsp;#1f80ff;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;none;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border-radius:&amp;nbsp;10px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-webkit-tap-highlight-color:&amp;nbsp;transparent;
&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;#btn-play:active&amp;nbsp;{&amp;nbsp;opacity:&amp;nbsp;0.8;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;.url-row&amp;nbsp;{&amp;nbsp;display:&amp;nbsp;flex;&amp;nbsp;gap:&amp;nbsp;8px;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;#url-input&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flex:&amp;nbsp;1;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;min-width:&amp;nbsp;0;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;padding:&amp;nbsp;12px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;14px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;#fff;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background:&amp;nbsp;#222;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;1px&amp;nbsp;solid&amp;nbsp;#333;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border-radius:&amp;nbsp;8px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;outline:&amp;nbsp;none;
&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;#btn-load&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;padding:&amp;nbsp;0&amp;nbsp;16px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;14px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;#fff;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background:&amp;nbsp;#333;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;none;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border-radius:&amp;nbsp;8px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;white-space:&amp;nbsp;nowrap;
&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;#status&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;12px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;#888;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;word-break:&amp;nbsp;break-all;
&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;/*&amp;nbsp;全屏时隐藏控制栏&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;:fullscreen&amp;nbsp;#controls&amp;nbsp;{&amp;nbsp;display:&amp;nbsp;none;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;:-webkit-full-screen&amp;nbsp;#controls&amp;nbsp;{&amp;nbsp;display:&amp;nbsp;none;&amp;nbsp;}
&amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;

&amp;lt;div&amp;nbsp;id=&amp;quot;player-wrap&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;nbsp;id=&amp;quot;video-box&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;video&amp;nbsp;id=&amp;quot;video&amp;quot;&amp;nbsp;playsinline&amp;nbsp;webkit-playsinline&amp;nbsp;x5-playsinline&amp;nbsp;preload=&amp;quot;metadata&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;src=&amp;quot;./BigBuckBunny.mp4&amp;quot;&amp;gt;&amp;lt;/video&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;!--&amp;nbsp;最大化按钮&amp;nbsp;--&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;button&amp;nbsp;id=&amp;quot;btn-fullscreen&amp;quot;&amp;nbsp;aria-label=&amp;quot;最大化&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;svg&amp;nbsp;id=&amp;quot;icon-max&amp;quot;&amp;nbsp;viewBox=&amp;quot;0&amp;nbsp;0&amp;nbsp;24&amp;nbsp;24&amp;quot;&amp;nbsp;fill=&amp;quot;none&amp;quot;&amp;nbsp;stroke=&amp;quot;currentColor&amp;quot;&amp;nbsp;stroke-width=&amp;quot;2&amp;quot;&amp;nbsp;stroke-linecap=&amp;quot;round&amp;quot;&amp;nbsp;stroke-linejoin=&amp;quot;round&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;path&amp;nbsp;d=&amp;quot;M8&amp;nbsp;3H5a2&amp;nbsp;2&amp;nbsp;0&amp;nbsp;0&amp;nbsp;0-2&amp;nbsp;2v3&amp;quot;/&amp;gt;&amp;lt;path&amp;nbsp;d=&amp;quot;M16&amp;nbsp;3h3a2&amp;nbsp;2&amp;nbsp;0&amp;nbsp;0&amp;nbsp;1&amp;nbsp;2&amp;nbsp;2v3&amp;quot;/&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;path&amp;nbsp;d=&amp;quot;M8&amp;nbsp;21H5a2&amp;nbsp;2&amp;nbsp;0&amp;nbsp;0&amp;nbsp;1-2-2v-3&amp;quot;/&amp;gt;&amp;lt;path&amp;nbsp;d=&amp;quot;M16&amp;nbsp;21h3a2&amp;nbsp;2&amp;nbsp;0&amp;nbsp;0&amp;nbsp;0&amp;nbsp;2-2v-3&amp;quot;/&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/svg&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;svg&amp;nbsp;id=&amp;quot;icon-min&amp;quot;&amp;nbsp;viewBox=&amp;quot;0&amp;nbsp;0&amp;nbsp;24&amp;nbsp;24&amp;quot;&amp;nbsp;fill=&amp;quot;none&amp;quot;&amp;nbsp;stroke=&amp;quot;currentColor&amp;quot;&amp;nbsp;stroke-width=&amp;quot;2&amp;quot;&amp;nbsp;stroke-linecap=&amp;quot;round&amp;quot;&amp;nbsp;stroke-linejoin=&amp;quot;round&amp;quot;&amp;nbsp;style=&amp;quot;display:none&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;path&amp;nbsp;d=&amp;quot;M8&amp;nbsp;3v3a2&amp;nbsp;2&amp;nbsp;0&amp;nbsp;0&amp;nbsp;1-2&amp;nbsp;2H3&amp;quot;/&amp;gt;&amp;lt;path&amp;nbsp;d=&amp;quot;M21&amp;nbsp;8h-3a2&amp;nbsp;2&amp;nbsp;0&amp;nbsp;0&amp;nbsp;1-2-2V3&amp;quot;/&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;path&amp;nbsp;d=&amp;quot;M3&amp;nbsp;16h3a2&amp;nbsp;2&amp;nbsp;0&amp;nbsp;0&amp;nbsp;1&amp;nbsp;2&amp;nbsp;2v3&amp;quot;/&amp;gt;&amp;lt;path&amp;nbsp;d=&amp;quot;M16&amp;nbsp;21v-3a2&amp;nbsp;2&amp;nbsp;0&amp;nbsp;0&amp;nbsp;1&amp;nbsp;2-2h3&amp;quot;/&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/svg&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/button&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;nbsp;id=&amp;quot;controls&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;button&amp;nbsp;id=&amp;quot;btn-play&amp;quot;&amp;gt;播放&amp;nbsp;/&amp;nbsp;暂停&amp;lt;/button&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;nbsp;class=&amp;quot;url-row&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;input&amp;nbsp;id=&amp;quot;url-input&amp;quot;&amp;nbsp;type=&amp;quot;url&amp;quot;&amp;nbsp;placeholder=&amp;quot;输入视频地址&amp;nbsp;(mp4/m3u8直链)&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;button&amp;nbsp;id=&amp;quot;btn-load&amp;quot;&amp;gt;加载&amp;lt;/button&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;nbsp;id=&amp;quot;status&amp;quot;&amp;gt;就绪&amp;lt;/div&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;script&amp;gt;
(function&amp;nbsp;()&amp;nbsp;{
&amp;nbsp;&amp;nbsp;var&amp;nbsp;wrap&amp;nbsp;=&amp;nbsp;document.getElementById(&amp;#39;player-wrap&amp;#39;);
&amp;nbsp;&amp;nbsp;var&amp;nbsp;video&amp;nbsp;=&amp;nbsp;document.getElementById(&amp;#39;video&amp;#39;);
&amp;nbsp;&amp;nbsp;var&amp;nbsp;btnFs&amp;nbsp;=&amp;nbsp;document.getElementById(&amp;#39;btn-fullscreen&amp;#39;);
&amp;nbsp;&amp;nbsp;var&amp;nbsp;btnPlay&amp;nbsp;=&amp;nbsp;document.getElementById(&amp;#39;btn-play&amp;#39;);
&amp;nbsp;&amp;nbsp;var&amp;nbsp;btnLoad&amp;nbsp;=&amp;nbsp;document.getElementById(&amp;#39;btn-load&amp;#39;);
&amp;nbsp;&amp;nbsp;var&amp;nbsp;urlInput&amp;nbsp;=&amp;nbsp;document.getElementById(&amp;#39;url-input&amp;#39;);
&amp;nbsp;&amp;nbsp;var&amp;nbsp;status&amp;nbsp;=&amp;nbsp;document.getElementById(&amp;#39;status&amp;#39;);
&amp;nbsp;&amp;nbsp;var&amp;nbsp;iconMax&amp;nbsp;=&amp;nbsp;document.getElementById(&amp;#39;icon-max&amp;#39;);
&amp;nbsp;&amp;nbsp;var&amp;nbsp;iconMin&amp;nbsp;=&amp;nbsp;document.getElementById(&amp;#39;icon-min&amp;#39;);

&amp;nbsp;&amp;nbsp;function&amp;nbsp;setStatus(msg)&amp;nbsp;{&amp;nbsp;status.textContent&amp;nbsp;=&amp;nbsp;msg;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;function&amp;nbsp;isFullscreen()&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;!!(document.fullscreenElement&amp;nbsp;||&amp;nbsp;document.webkitFullscreenElement&amp;nbsp;||
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;document.webkitCurrentFullScreenElement);
&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;function&amp;nbsp;updateIcon()&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;fs&amp;nbsp;=&amp;nbsp;isFullscreen();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;iconMax.style.display&amp;nbsp;=&amp;nbsp;fs&amp;nbsp;?&amp;nbsp;&amp;#39;none&amp;#39;&amp;nbsp;:&amp;nbsp;&amp;#39;&amp;#39;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;iconMin.style.display&amp;nbsp;=&amp;nbsp;fs&amp;nbsp;?&amp;nbsp;&amp;#39;&amp;#39;&amp;nbsp;:&amp;nbsp;&amp;#39;none&amp;#39;;
&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;//&amp;nbsp;最大化&amp;nbsp;/&amp;nbsp;退出全屏（兼容&amp;nbsp;iOS&amp;nbsp;Safari&amp;nbsp;的&amp;nbsp;video&amp;nbsp;原生全屏）
&amp;nbsp;&amp;nbsp;btnFs.addEventListener(&amp;#39;click&amp;#39;,&amp;nbsp;function&amp;nbsp;()&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(isFullscreen())&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(document.exitFullscreen)&amp;nbsp;document.exitFullscreen();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else&amp;nbsp;if&amp;nbsp;(document.webkitExitFullscreen)&amp;nbsp;document.webkitExitFullscreen();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else&amp;nbsp;if&amp;nbsp;(video.webkitExitFullscreen)&amp;nbsp;video.webkitExitFullscreen();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(wrap.requestFullscreen)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;wrap.requestFullscreen();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;else&amp;nbsp;if&amp;nbsp;(wrap.webkitRequestFullscreen)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;wrap.webkitRequestFullscreen();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;else&amp;nbsp;if&amp;nbsp;(video.webkitEnterFullscreen)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;iOS&amp;nbsp;Safari:&amp;nbsp;只对&amp;nbsp;video&amp;nbsp;元素有效
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;video.webkitEnterFullscreen();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;else&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;setStatus(&amp;#39;当前环境不支持全屏&amp;nbsp;API&amp;#39;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;});

&amp;nbsp;&amp;nbsp;[&amp;#39;fullscreenchange&amp;#39;,&amp;nbsp;&amp;#39;webkitfullscreenchange&amp;#39;].forEach(function&amp;nbsp;(ev)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;document.addEventListener(ev,&amp;nbsp;updateIcon);
&amp;nbsp;&amp;nbsp;});
&amp;nbsp;&amp;nbsp;video.addEventListener(&amp;#39;webkitendfullscreen&amp;#39;,&amp;nbsp;updateIcon);

&amp;nbsp;&amp;nbsp;//&amp;nbsp;播放&amp;nbsp;/&amp;nbsp;暂停
&amp;nbsp;&amp;nbsp;btnPlay.addEventListener(&amp;#39;click&amp;#39;,&amp;nbsp;function&amp;nbsp;()&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(video.paused)&amp;nbsp;video.play().catch(function&amp;nbsp;(e)&amp;nbsp;{&amp;nbsp;setStatus(&amp;#39;播放失败:&amp;nbsp;&amp;#39;&amp;nbsp;+&amp;nbsp;e.message);&amp;nbsp;});
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else&amp;nbsp;video.pause();
&amp;nbsp;&amp;nbsp;});

&amp;nbsp;&amp;nbsp;//&amp;nbsp;加载自定义地址
&amp;nbsp;&amp;nbsp;btnLoad.addEventListener(&amp;#39;click&amp;#39;,&amp;nbsp;function&amp;nbsp;()&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;url&amp;nbsp;=&amp;nbsp;urlInput.value.trim();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(!url)&amp;nbsp;{&amp;nbsp;setStatus(&amp;#39;请输入视频地址&amp;#39;);&amp;nbsp;return;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;video.src&amp;nbsp;=&amp;nbsp;url;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;video.load();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;video.play().catch(function&amp;nbsp;()&amp;nbsp;{});
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;setStatus(&amp;#39;已加载:&amp;nbsp;&amp;#39;&amp;nbsp;+&amp;nbsp;url);
&amp;nbsp;&amp;nbsp;});

&amp;nbsp;&amp;nbsp;//&amp;nbsp;状态反馈
&amp;nbsp;&amp;nbsp;video.addEventListener(&amp;#39;playing&amp;#39;,&amp;nbsp;function&amp;nbsp;()&amp;nbsp;{&amp;nbsp;setStatus(&amp;#39;播放中&amp;nbsp;&amp;#39;&amp;nbsp;+&amp;nbsp;video.videoWidth&amp;nbsp;+&amp;nbsp;&amp;#39;x&amp;#39;&amp;nbsp;+&amp;nbsp;video.videoHeight);&amp;nbsp;});
&amp;nbsp;&amp;nbsp;video.addEventListener(&amp;#39;pause&amp;#39;,&amp;nbsp;function&amp;nbsp;()&amp;nbsp;{&amp;nbsp;setStatus(&amp;#39;已暂停&amp;#39;);&amp;nbsp;});
&amp;nbsp;&amp;nbsp;video.addEventListener(&amp;#39;error&amp;#39;,&amp;nbsp;function&amp;nbsp;()&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;err&amp;nbsp;=&amp;nbsp;video.error;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;setStatus(&amp;#39;加载出错:&amp;nbsp;&amp;#39;&amp;nbsp;+&amp;nbsp;(err&amp;nbsp;?&amp;nbsp;&amp;#39;code&amp;nbsp;&amp;#39;&amp;nbsp;+&amp;nbsp;err.code&amp;nbsp;:&amp;nbsp;&amp;#39;未知错误&amp;#39;));
&amp;nbsp;&amp;nbsp;});
&amp;nbsp;&amp;nbsp;video.addEventListener(&amp;#39;ended&amp;#39;,&amp;nbsp;function&amp;nbsp;()&amp;nbsp;{&amp;nbsp;setStatus(&amp;#39;播放结束&amp;#39;);&amp;nbsp;});
})();
&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/pre&gt;</description><pubDate>Mon, 10 Aug 2026 19:32:34 +0800</pubDate></item><item><title>HTML打包APK实现拍照、录像、录音和文件选择</title><link>http://ylongsoft.com/post/html-apk-camera-video-audio-file-picker.html</link><description>&lt;p&gt;HTML页面可以通过文件选择控件和浏览器媒体接口调用安卓设备的相机、摄像头、麦克风及文件选择器。完成页面代码后，再通过HTML一键打包APK工具配置相机和麦克风权限，即可生成支持拍照、录像、录音和文件选择的安卓APP。&lt;/p&gt;&lt;p&gt;如果你不了解HTML打包APK工具，想要进一步了解，可以查看官网：&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://html2apk.leapever.com/&quot;&gt;HTML一键打包APK工具官网&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;创建文件采集页面&lt;/h2&gt;&lt;p&gt;在HTML项目入口目录中创建 &lt;code&gt;index.html&lt;/code&gt;，加入拍摄照片、录制视频、录制音频和选择文件四个入口。&lt;/p&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;拍摄照片：调用设备相机并优先进入后置摄像头拍照页面。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;录制视频：调用设备摄像头并进入视频录制页面。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;录制音频：申请麦克风权限，在网页内录制音频并生成可下载的音频文件。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;选择文件：打开系统文件选择界面，并支持一次选择多个文件。也可以选择相机进行拍照或者录频&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;一个完整的html示例代码如下：&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-html&quot;&gt;&amp;lt;!doctype&amp;nbsp;html&amp;gt;
&amp;lt;html&amp;nbsp;lang=&amp;quot;zh-CN&amp;quot;&amp;gt;
&amp;lt;head&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;lt;meta&amp;nbsp;charset=&amp;quot;utf-8&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;lt;meta&amp;nbsp;name=&amp;quot;viewport&amp;quot;&amp;nbsp;content=&amp;quot;width=device-width,&amp;nbsp;initial-scale=1,&amp;nbsp;viewport-fit=cover&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;lt;meta&amp;nbsp;name=&amp;quot;theme-color&amp;quot;&amp;nbsp;content=&amp;quot;#f4f1ea&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;lt;title&amp;gt;移动端文件采集&amp;lt;/title&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;lt;style&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;:root&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color-scheme:&amp;nbsp;light;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;--ink:&amp;nbsp;#18201d;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;--muted:&amp;nbsp;#65706b;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;--paper:&amp;nbsp;#f4f1ea;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;--surface:&amp;nbsp;#fffdf8;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;--line:&amp;nbsp;#d8d5cd;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;--green:&amp;nbsp;#18634b;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;--green-dark:&amp;nbsp;#104b39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;--coral:&amp;nbsp;#e56f51;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;--shadow:&amp;nbsp;0&amp;nbsp;12px&amp;nbsp;32px&amp;nbsp;rgba(24,&amp;nbsp;32,&amp;nbsp;29,&amp;nbsp;0.09);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;box-sizing:&amp;nbsp;border-box;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;letter-spacing:&amp;nbsp;0;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;body&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;min-width:&amp;nbsp;320px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;min-height:&amp;nbsp;100vh;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;margin:&amp;nbsp;0;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;var(--ink);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;linear-gradient(rgba(24,&amp;nbsp;99,&amp;nbsp;75,&amp;nbsp;0.055)&amp;nbsp;1px,&amp;nbsp;transparent&amp;nbsp;1px),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;linear-gradient(90deg,&amp;nbsp;rgba(24,&amp;nbsp;99,&amp;nbsp;75,&amp;nbsp;0.055)&amp;nbsp;1px,&amp;nbsp;transparent&amp;nbsp;1px),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var(--paper);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-size:&amp;nbsp;24px&amp;nbsp;24px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-family:&amp;nbsp;&amp;quot;Noto&amp;nbsp;Sans&amp;nbsp;SC&amp;quot;,&amp;nbsp;&amp;quot;Microsoft&amp;nbsp;YaHei&amp;nbsp;UI&amp;quot;,&amp;nbsp;sans-serif;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;button,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;input&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font:&amp;nbsp;inherit;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[hidden]&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;display:&amp;nbsp;none&amp;nbsp;!important;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.shell&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;width:&amp;nbsp;min(100%,&amp;nbsp;560px);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;min-height:&amp;nbsp;100vh;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;margin:&amp;nbsp;0&amp;nbsp;auto;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;padding:&amp;nbsp;max(24px,&amp;nbsp;env(safe-area-inset-top))&amp;nbsp;18px&amp;nbsp;max(32px,&amp;nbsp;env(safe-area-inset-bottom));
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;header&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;position:&amp;nbsp;relative;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;padding:&amp;nbsp;12px&amp;nbsp;4px&amp;nbsp;25px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;header::after&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;position:&amp;nbsp;absolute;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;right:&amp;nbsp;4px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;bottom:&amp;nbsp;18px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;width:&amp;nbsp;52px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;height:&amp;nbsp;5px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background:&amp;nbsp;var(--coral);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;content:&amp;nbsp;&amp;quot;&amp;quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;transform:&amp;nbsp;rotate(-3deg);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.eyebrow&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;margin:&amp;nbsp;0&amp;nbsp;0&amp;nbsp;8px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;var(--green);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;12px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-weight:&amp;nbsp;800;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;text-transform:&amp;nbsp;uppercase;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;h1&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;margin:&amp;nbsp;0;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-family:&amp;nbsp;&amp;quot;STSong&amp;quot;,&amp;nbsp;&amp;quot;SimSun&amp;quot;,&amp;nbsp;serif;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;clamp(32px,&amp;nbsp;10vw,&amp;nbsp;48px);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-weight:&amp;nbsp;700;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;line-height:&amp;nbsp;1.08;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.subtitle&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;max-width:&amp;nbsp;360px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;margin:&amp;nbsp;12px&amp;nbsp;0&amp;nbsp;0;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;var(--muted);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;14px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;line-height:&amp;nbsp;1.7;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.actions&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;display:&amp;nbsp;grid;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;grid-template-columns:&amp;nbsp;repeat(2,&amp;nbsp;minmax(0,&amp;nbsp;1fr));
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;gap:&amp;nbsp;10px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.picker&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;position:&amp;nbsp;relative;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;min-width:&amp;nbsp;0;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;min-height:&amp;nbsp;132px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;overflow:&amp;nbsp;hidden;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;1px&amp;nbsp;solid&amp;nbsp;var(--line);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border-radius:&amp;nbsp;8px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background:&amp;nbsp;var(--surface);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;box-shadow:&amp;nbsp;0&amp;nbsp;4px&amp;nbsp;12px&amp;nbsp;rgba(24,&amp;nbsp;32,&amp;nbsp;29,&amp;nbsp;0.035);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.picker:focus-within&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;outline:&amp;nbsp;3px&amp;nbsp;solid&amp;nbsp;rgba(24,&amp;nbsp;99,&amp;nbsp;75,&amp;nbsp;0.2);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;outline-offset:&amp;nbsp;2px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.picker&amp;nbsp;input&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;position:&amp;nbsp;absolute;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;width:&amp;nbsp;1px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;height:&amp;nbsp;1px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;opacity:&amp;nbsp;0;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pointer-events:&amp;nbsp;none;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.picker&amp;nbsp;label&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;display:&amp;nbsp;flex;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;width:&amp;nbsp;100%;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;min-height:&amp;nbsp;132px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;padding:&amp;nbsp;17px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor:&amp;nbsp;pointer;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flex-direction:&amp;nbsp;column;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;justify-content:&amp;nbsp;space-between;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-webkit-tap-highlight-color:&amp;nbsp;transparent;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.picker-button&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;display:&amp;nbsp;flex;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;width:&amp;nbsp;100%;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;min-height:&amp;nbsp;132px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;padding:&amp;nbsp;17px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;0;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;inherit;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background:&amp;nbsp;transparent;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor:&amp;nbsp;pointer;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flex-direction:&amp;nbsp;column;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;justify-content:&amp;nbsp;space-between;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;text-align:&amp;nbsp;left;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-webkit-tap-highlight-color:&amp;nbsp;transparent;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.picker&amp;nbsp;label:active,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.picker-button:active&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background:&amp;nbsp;#f2f7f3;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.icon&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;display:&amp;nbsp;grid;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;width:&amp;nbsp;42px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;height:&amp;nbsp;42px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border-radius:&amp;nbsp;50%;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;#fff;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background:&amp;nbsp;var(--green);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;20px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;place-items:&amp;nbsp;center;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.picker:nth-child(2)&amp;nbsp;.icon,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.picker:nth-child(4)&amp;nbsp;.icon&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background:&amp;nbsp;var(--coral);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.label-title&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;display:&amp;nbsp;block;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;margin-bottom:&amp;nbsp;4px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;16px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-weight:&amp;nbsp;800;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.label-meta&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;display:&amp;nbsp;block;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;overflow:&amp;nbsp;hidden;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;var(--muted);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;11px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;text-overflow:&amp;nbsp;ellipsis;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;white-space:&amp;nbsp;nowrap;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.result&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;margin-top:&amp;nbsp;18px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;overflow:&amp;nbsp;hidden;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;1px&amp;nbsp;solid&amp;nbsp;var(--line);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border-radius:&amp;nbsp;8px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background:&amp;nbsp;var(--surface);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;box-shadow:&amp;nbsp;var(--shadow);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;animation:&amp;nbsp;rise&amp;nbsp;240ms&amp;nbsp;ease-out;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.result-header&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;display:&amp;nbsp;flex;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;min-height:&amp;nbsp;52px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;padding:&amp;nbsp;12px&amp;nbsp;14px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border-bottom:&amp;nbsp;1px&amp;nbsp;solid&amp;nbsp;var(--line);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;align-items:&amp;nbsp;center;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;justify-content:&amp;nbsp;space-between;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;gap:&amp;nbsp;12px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.result-header&amp;nbsp;h2&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;margin:&amp;nbsp;0;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;15px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.clear&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;width:&amp;nbsp;34px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;height:&amp;nbsp;34px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;padding:&amp;nbsp;0;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;1px&amp;nbsp;solid&amp;nbsp;var(--line);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border-radius:&amp;nbsp;50%;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;var(--ink);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background:&amp;nbsp;transparent;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor:&amp;nbsp;pointer;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;20px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;line-height:&amp;nbsp;1;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.file-list&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;margin:&amp;nbsp;0;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;padding:&amp;nbsp;0;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;list-style:&amp;nbsp;none;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.file-list&amp;nbsp;li&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;display:&amp;nbsp;flex;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;gap:&amp;nbsp;8px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;min-height:&amp;nbsp;68px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;padding:&amp;nbsp;10px&amp;nbsp;14px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border-top:&amp;nbsp;1px&amp;nbsp;solid&amp;nbsp;var(--line);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;align-items:&amp;nbsp;center;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;12px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.file-list&amp;nbsp;li:first-child&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border-top:&amp;nbsp;0;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.file-info&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;min-width:&amp;nbsp;0;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flex:&amp;nbsp;1;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.file-name&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;display:&amp;nbsp;block;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;overflow:&amp;nbsp;hidden;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-weight:&amp;nbsp;700;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;text-overflow:&amp;nbsp;ellipsis;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;white-space:&amp;nbsp;nowrap;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.file-size&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;display:&amp;nbsp;block;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;margin-top:&amp;nbsp;4px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;var(--muted);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.download-button&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;min-width:&amp;nbsp;76px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;min-height:&amp;nbsp;40px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;padding:&amp;nbsp;0&amp;nbsp;12px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;1px&amp;nbsp;solid&amp;nbsp;var(--green);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border-radius:&amp;nbsp;6px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;#fff;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background:&amp;nbsp;var(--green);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor:&amp;nbsp;pointer;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-weight:&amp;nbsp;800;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flex-shrink:&amp;nbsp;0;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.recorder&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;margin-top:&amp;nbsp;18px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;padding:&amp;nbsp;18px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;1px&amp;nbsp;solid&amp;nbsp;var(--line);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border-radius:&amp;nbsp;8px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background:&amp;nbsp;var(--surface);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;box-shadow:&amp;nbsp;var(--shadow);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.recorder&amp;nbsp;h2&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;margin:&amp;nbsp;0&amp;nbsp;0&amp;nbsp;6px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;18px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.recorder-status&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;min-height:&amp;nbsp;42px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;margin:&amp;nbsp;0&amp;nbsp;0&amp;nbsp;14px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;var(--muted);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;13px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;line-height:&amp;nbsp;1.6;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.recorder-controls&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;display:&amp;nbsp;grid;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;grid-template-columns:&amp;nbsp;1fr&amp;nbsp;1fr;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;gap:&amp;nbsp;10px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.recorder-controls&amp;nbsp;button,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.audio-fallback&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;min-height:&amp;nbsp;46px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;1px&amp;nbsp;solid&amp;nbsp;var(--green);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border-radius:&amp;nbsp;6px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;#fff;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background:&amp;nbsp;var(--green);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor:&amp;nbsp;pointer;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-weight:&amp;nbsp;800;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.recorder-controls&amp;nbsp;button:disabled&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor:&amp;nbsp;not-allowed;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;opacity:&amp;nbsp;0.45;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.audio-fallback&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;display:&amp;nbsp;grid;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;margin-top:&amp;nbsp;10px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border-color:&amp;nbsp;var(--line);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;var(--ink);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background:&amp;nbsp;transparent;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;13px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;place-items:&amp;nbsp;center;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@keyframes&amp;nbsp;rise&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;from&amp;nbsp;{&amp;nbsp;opacity:&amp;nbsp;0;&amp;nbsp;transform:&amp;nbsp;translateY(8px);&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;to&amp;nbsp;{&amp;nbsp;opacity:&amp;nbsp;1;&amp;nbsp;transform:&amp;nbsp;translateY(0);&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@media&amp;nbsp;(min-width:&amp;nbsp;480px)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.shell&amp;nbsp;{&amp;nbsp;padding-inline:&amp;nbsp;26px;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.picker&amp;nbsp;label&amp;nbsp;{&amp;nbsp;min-height:&amp;nbsp;144px;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@media&amp;nbsp;(prefers-reduced-motion:&amp;nbsp;reduce)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.result&amp;nbsp;{&amp;nbsp;animation:&amp;nbsp;none;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;lt;main&amp;nbsp;class=&amp;quot;shell&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;header&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;p&amp;nbsp;class=&amp;quot;eyebrow&amp;quot;&amp;gt;Mobile&amp;nbsp;capture&amp;nbsp;lab&amp;lt;/p&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;h1&amp;gt;从设备中采集文件&amp;lt;/h1&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;p&amp;nbsp;class=&amp;quot;subtitle&amp;quot;&amp;gt;点击一种方式，系统会打开对应的拍摄、录制或文件选择界面。&amp;lt;/p&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/header&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;section&amp;nbsp;class=&amp;quot;actions&amp;quot;&amp;nbsp;aria-label=&amp;quot;文件采集方式&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;nbsp;class=&amp;quot;picker&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;input&amp;nbsp;id=&amp;quot;camera&amp;quot;&amp;nbsp;type=&amp;quot;file&amp;quot;&amp;nbsp;accept=&amp;quot;image/*&amp;quot;&amp;nbsp;capture=&amp;quot;environment&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;label&amp;nbsp;for=&amp;quot;camera&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;span&amp;nbsp;class=&amp;quot;icon&amp;quot;&amp;nbsp;aria-hidden=&amp;quot;true&amp;quot;&amp;gt;●&amp;lt;/span&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;span&amp;gt;&amp;lt;span&amp;nbsp;class=&amp;quot;label-title&amp;quot;&amp;gt;拍摄照片&amp;lt;/span&amp;gt;&amp;lt;span&amp;nbsp;class=&amp;quot;label-meta&amp;quot;&amp;gt;设备后置相机&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/label&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;nbsp;class=&amp;quot;picker&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;input&amp;nbsp;id=&amp;quot;video&amp;quot;&amp;nbsp;type=&amp;quot;file&amp;quot;&amp;nbsp;accept=&amp;quot;video/*&amp;quot;&amp;nbsp;capture=&amp;quot;environment&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;label&amp;nbsp;for=&amp;quot;video&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;span&amp;nbsp;class=&amp;quot;icon&amp;quot;&amp;nbsp;aria-hidden=&amp;quot;true&amp;quot;&amp;gt;▶&amp;lt;/span&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;span&amp;gt;&amp;lt;span&amp;nbsp;class=&amp;quot;label-title&amp;quot;&amp;gt;录制视频&amp;lt;/span&amp;gt;&amp;lt;span&amp;nbsp;class=&amp;quot;label-meta&amp;quot;&amp;gt;设备摄像头&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/label&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;nbsp;class=&amp;quot;picker&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;button&amp;nbsp;id=&amp;quot;openRecorder&amp;quot;&amp;nbsp;class=&amp;quot;picker-button&amp;quot;&amp;nbsp;type=&amp;quot;button&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;span&amp;nbsp;class=&amp;quot;icon&amp;quot;&amp;nbsp;aria-hidden=&amp;quot;true&amp;quot;&amp;gt;♪&amp;lt;/span&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;span&amp;gt;&amp;lt;span&amp;nbsp;class=&amp;quot;label-title&amp;quot;&amp;gt;录制音频&amp;lt;/span&amp;gt;&amp;lt;span&amp;nbsp;class=&amp;quot;label-meta&amp;quot;&amp;gt;设备麦克风&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/button&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;nbsp;class=&amp;quot;picker&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;input&amp;nbsp;id=&amp;quot;files&amp;quot;&amp;nbsp;type=&amp;quot;file&amp;quot;&amp;nbsp;multiple&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;label&amp;nbsp;for=&amp;quot;files&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;span&amp;nbsp;class=&amp;quot;icon&amp;quot;&amp;nbsp;aria-hidden=&amp;quot;true&amp;quot;&amp;gt;＋&amp;lt;/span&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;span&amp;gt;&amp;lt;span&amp;nbsp;class=&amp;quot;label-title&amp;quot;&amp;gt;选择文件&amp;lt;/span&amp;gt;&amp;lt;span&amp;nbsp;class=&amp;quot;label-meta&amp;quot;&amp;gt;支持多选&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/label&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/section&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;section&amp;nbsp;id=&amp;quot;recorder&amp;quot;&amp;nbsp;class=&amp;quot;recorder&amp;quot;&amp;nbsp;aria-live=&amp;quot;polite&amp;quot;&amp;nbsp;hidden&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;h2&amp;gt;麦克风录音&amp;lt;/h2&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;p&amp;nbsp;id=&amp;quot;recorderStatus&amp;quot;&amp;nbsp;class=&amp;quot;recorder-status&amp;quot;&amp;gt;点击“开始录音”，并允许浏览器使用麦克风。&amp;lt;/p&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;nbsp;class=&amp;quot;recorder-controls&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;button&amp;nbsp;id=&amp;quot;startRecording&amp;quot;&amp;nbsp;type=&amp;quot;button&amp;quot;&amp;gt;开始录音&amp;lt;/button&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;button&amp;nbsp;id=&amp;quot;stopRecording&amp;quot;&amp;nbsp;type=&amp;quot;button&amp;quot;&amp;nbsp;disabled&amp;gt;停止并使用&amp;lt;/button&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;input&amp;nbsp;id=&amp;quot;audioFallback&amp;quot;&amp;nbsp;type=&amp;quot;file&amp;quot;&amp;nbsp;accept=&amp;quot;audio/*&amp;quot;&amp;nbsp;hidden&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;label&amp;nbsp;class=&amp;quot;audio-fallback&amp;quot;&amp;nbsp;for=&amp;quot;audioFallback&amp;quot;&amp;gt;无法录音？选择已有音频&amp;lt;/label&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/section&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;section&amp;nbsp;id=&amp;quot;result&amp;quot;&amp;nbsp;class=&amp;quot;result&amp;quot;&amp;nbsp;aria-live=&amp;quot;polite&amp;quot;&amp;nbsp;hidden&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;nbsp;class=&amp;quot;result-header&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;h2&amp;nbsp;id=&amp;quot;resultTitle&amp;quot;&amp;gt;处理完成&amp;lt;/h2&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;button&amp;nbsp;id=&amp;quot;clearButton&amp;quot;&amp;nbsp;class=&amp;quot;clear&amp;quot;&amp;nbsp;type=&amp;quot;button&amp;quot;&amp;nbsp;aria-label=&amp;quot;清除选择&amp;quot;&amp;nbsp;title=&amp;quot;清除选择&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;ul&amp;nbsp;id=&amp;quot;fileList&amp;quot;&amp;nbsp;class=&amp;quot;file-list&amp;quot;&amp;gt;&amp;lt;/ul&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/section&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;lt;/main&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;lt;script&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;inputs&amp;nbsp;=&amp;nbsp;document.querySelectorAll(&amp;#39;input[type=&amp;quot;file&amp;quot;]&amp;#39;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;result&amp;nbsp;=&amp;nbsp;document.getElementById(&amp;#39;result&amp;#39;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;resultTitle&amp;nbsp;=&amp;nbsp;document.getElementById(&amp;#39;resultTitle&amp;#39;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;fileList&amp;nbsp;=&amp;nbsp;document.getElementById(&amp;#39;fileList&amp;#39;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;clearButton&amp;nbsp;=&amp;nbsp;document.getElementById(&amp;#39;clearButton&amp;#39;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;openRecorder&amp;nbsp;=&amp;nbsp;document.getElementById(&amp;#39;openRecorder&amp;#39;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;recorderPanel&amp;nbsp;=&amp;nbsp;document.getElementById(&amp;#39;recorder&amp;#39;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;recorderStatus&amp;nbsp;=&amp;nbsp;document.getElementById(&amp;#39;recorderStatus&amp;#39;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;startRecording&amp;nbsp;=&amp;nbsp;document.getElementById(&amp;#39;startRecording&amp;#39;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;stopRecording&amp;nbsp;=&amp;nbsp;document.getElementById(&amp;#39;stopRecording&amp;#39;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let&amp;nbsp;activeInput&amp;nbsp;=&amp;nbsp;null;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let&amp;nbsp;microphoneStream&amp;nbsp;=&amp;nbsp;null;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let&amp;nbsp;recordingAudioContext&amp;nbsp;=&amp;nbsp;null;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let&amp;nbsp;recordingSource&amp;nbsp;=&amp;nbsp;null;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let&amp;nbsp;recordingProcessor&amp;nbsp;=&amp;nbsp;null;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let&amp;nbsp;recordingGain&amp;nbsp;=&amp;nbsp;null;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let&amp;nbsp;pcmChunks&amp;nbsp;=&amp;nbsp;[];
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let&amp;nbsp;pcmSampleCount&amp;nbsp;=&amp;nbsp;0;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let&amp;nbsp;isRecording&amp;nbsp;=&amp;nbsp;false;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let&amp;nbsp;recordingTimer&amp;nbsp;=&amp;nbsp;null;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let&amp;nbsp;recordingStartedAt&amp;nbsp;=&amp;nbsp;0;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;function&amp;nbsp;formatSize(bytes)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(bytes&amp;nbsp;===&amp;nbsp;0)&amp;nbsp;return&amp;nbsp;&amp;#39;0&amp;nbsp;B&amp;#39;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;units&amp;nbsp;=&amp;nbsp;[&amp;#39;B&amp;#39;,&amp;nbsp;&amp;#39;KB&amp;#39;,&amp;nbsp;&amp;#39;MB&amp;#39;,&amp;nbsp;&amp;#39;GB&amp;#39;];
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;unitIndex&amp;nbsp;=&amp;nbsp;Math.min(Math.floor(Math.log(bytes)&amp;nbsp;/&amp;nbsp;Math.log(1024)),&amp;nbsp;units.length&amp;nbsp;-&amp;nbsp;1);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;`${(bytes&amp;nbsp;/&amp;nbsp;(1024&amp;nbsp;**&amp;nbsp;unitIndex)).toFixed(unitIndex&amp;nbsp;?&amp;nbsp;1&amp;nbsp;:&amp;nbsp;0)}&amp;nbsp;${units[unitIndex]}`;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;function&amp;nbsp;clearSelection()&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(activeInput)&amp;nbsp;activeInput.value&amp;nbsp;=&amp;nbsp;&amp;#39;&amp;#39;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;activeInput&amp;nbsp;=&amp;nbsp;null;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;fileList.replaceChildren();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;result.hidden&amp;nbsp;=&amp;nbsp;true;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;function&amp;nbsp;downloadFile(file)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;reader&amp;nbsp;=&amp;nbsp;new&amp;nbsp;FileReader();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;reader.addEventListener(&amp;#39;load&amp;#39;,&amp;nbsp;()&amp;nbsp;=&amp;gt;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;link&amp;nbsp;=&amp;nbsp;document.createElement(&amp;#39;a&amp;#39;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;link.href&amp;nbsp;=&amp;nbsp;reader.result;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;link.download&amp;nbsp;=&amp;nbsp;file.name&amp;nbsp;||&amp;nbsp;`文件_${Date.now()}`;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;document.body.appendChild(link);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;link.click();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;document.body.removeChild(link);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;reader.addEventListener(&amp;#39;error&amp;#39;,&amp;nbsp;()&amp;nbsp;=&amp;gt;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;resultTitle.textContent&amp;nbsp;=&amp;nbsp;&amp;#39;下载失败，请重试&amp;#39;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;reader.readAsDataURL(file);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;function&amp;nbsp;displayFiles(files,&amp;nbsp;input&amp;nbsp;=&amp;nbsp;null)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(!files.length)&amp;nbsp;return;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(activeInput&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;activeInput&amp;nbsp;!==&amp;nbsp;input)&amp;nbsp;activeInput.value&amp;nbsp;=&amp;nbsp;&amp;#39;&amp;#39;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;activeInput&amp;nbsp;=&amp;nbsp;input;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;resultTitle.textContent&amp;nbsp;=&amp;nbsp;files.length&amp;nbsp;&amp;gt;&amp;nbsp;1&amp;nbsp;?&amp;nbsp;`${files.length}&amp;nbsp;个文件处理完成`&amp;nbsp;:&amp;nbsp;&amp;#39;处理完成&amp;#39;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;fileList.replaceChildren(...files.map((file)&amp;nbsp;=&amp;gt;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;item&amp;nbsp;=&amp;nbsp;document.createElement(&amp;#39;li&amp;#39;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;info&amp;nbsp;=&amp;nbsp;document.createElement(&amp;#39;span&amp;#39;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;name&amp;nbsp;=&amp;nbsp;document.createElement(&amp;#39;span&amp;#39;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;size&amp;nbsp;=&amp;nbsp;document.createElement(&amp;#39;span&amp;#39;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;downloadButton&amp;nbsp;=&amp;nbsp;document.createElement(&amp;#39;button&amp;#39;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;info.className&amp;nbsp;=&amp;nbsp;&amp;#39;file-info&amp;#39;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;name.className&amp;nbsp;=&amp;nbsp;&amp;#39;file-name&amp;#39;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;name.textContent&amp;nbsp;=&amp;nbsp;file.name;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;size.className&amp;nbsp;=&amp;nbsp;&amp;#39;file-size&amp;#39;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;size.textContent&amp;nbsp;=&amp;nbsp;formatSize(file.size);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;downloadButton.className&amp;nbsp;=&amp;nbsp;&amp;#39;download-button&amp;#39;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;downloadButton.type&amp;nbsp;=&amp;nbsp;&amp;#39;button&amp;#39;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;downloadButton.textContent&amp;nbsp;=&amp;nbsp;&amp;#39;↓&amp;nbsp;下载&amp;#39;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;downloadButton.addEventListener(&amp;#39;click&amp;#39;,&amp;nbsp;()&amp;nbsp;=&amp;gt;&amp;nbsp;downloadFile(file));
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;info.append(name,&amp;nbsp;size);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;item.append(info,&amp;nbsp;downloadButton);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;item;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}));
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;result.hidden&amp;nbsp;=&amp;nbsp;false;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;result.scrollIntoView({&amp;nbsp;behavior:&amp;nbsp;&amp;#39;smooth&amp;#39;,&amp;nbsp;block:&amp;nbsp;&amp;#39;nearest&amp;#39;&amp;nbsp;});
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;function&amp;nbsp;stopMicrophone()&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(recordingTimer)&amp;nbsp;clearInterval(recordingTimer);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;recordingTimer&amp;nbsp;=&amp;nbsp;null;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;isRecording&amp;nbsp;=&amp;nbsp;false;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(recordingProcessor)&amp;nbsp;recordingProcessor.disconnect();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(recordingSource)&amp;nbsp;recordingSource.disconnect();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(recordingGain)&amp;nbsp;recordingGain.disconnect();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;recordingProcessor&amp;nbsp;=&amp;nbsp;null;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;recordingSource&amp;nbsp;=&amp;nbsp;null;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;recordingGain&amp;nbsp;=&amp;nbsp;null;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(microphoneStream)&amp;nbsp;microphoneStream.getTracks().forEach((track)&amp;nbsp;=&amp;gt;&amp;nbsp;track.stop());
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;microphoneStream&amp;nbsp;=&amp;nbsp;null;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(recordingAudioContext&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;recordingAudioContext.state&amp;nbsp;!==&amp;nbsp;&amp;#39;closed&amp;#39;)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;recordingAudioContext.close().catch(()&amp;nbsp;=&amp;gt;&amp;nbsp;{});
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;recordingAudioContext&amp;nbsp;=&amp;nbsp;null;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;function&amp;nbsp;createWavFile(chunks,&amp;nbsp;sampleCount,&amp;nbsp;sampleRate)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;buffer&amp;nbsp;=&amp;nbsp;new&amp;nbsp;ArrayBuffer(44&amp;nbsp;+&amp;nbsp;sampleCount&amp;nbsp;*&amp;nbsp;2);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;view&amp;nbsp;=&amp;nbsp;new&amp;nbsp;DataView(buffer);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;function&amp;nbsp;writeText(offset,&amp;nbsp;text)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;(let&amp;nbsp;index&amp;nbsp;=&amp;nbsp;0;&amp;nbsp;index&amp;nbsp;&amp;lt;&amp;nbsp;text.length;&amp;nbsp;index&amp;nbsp;+=&amp;nbsp;1)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;view.setUint8(offset&amp;nbsp;+&amp;nbsp;index,&amp;nbsp;text.charCodeAt(index));
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;writeText(0,&amp;nbsp;&amp;#39;RIFF&amp;#39;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;view.setUint32(4,&amp;nbsp;36&amp;nbsp;+&amp;nbsp;sampleCount&amp;nbsp;*&amp;nbsp;2,&amp;nbsp;true);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;writeText(8,&amp;nbsp;&amp;#39;WAVE&amp;#39;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;writeText(12,&amp;nbsp;&amp;#39;fmt&amp;nbsp;&amp;#39;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;view.setUint32(16,&amp;nbsp;16,&amp;nbsp;true);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;view.setUint16(20,&amp;nbsp;1,&amp;nbsp;true);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;view.setUint16(22,&amp;nbsp;1,&amp;nbsp;true);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;view.setUint32(24,&amp;nbsp;sampleRate,&amp;nbsp;true);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;view.setUint32(28,&amp;nbsp;sampleRate&amp;nbsp;*&amp;nbsp;2,&amp;nbsp;true);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;view.setUint16(32,&amp;nbsp;2,&amp;nbsp;true);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;view.setUint16(34,&amp;nbsp;16,&amp;nbsp;true);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;writeText(36,&amp;nbsp;&amp;#39;data&amp;#39;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;view.setUint32(40,&amp;nbsp;sampleCount&amp;nbsp;*&amp;nbsp;2,&amp;nbsp;true);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let&amp;nbsp;offset&amp;nbsp;=&amp;nbsp;44;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;chunks.forEach((chunk)&amp;nbsp;=&amp;gt;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;(let&amp;nbsp;index&amp;nbsp;=&amp;nbsp;0;&amp;nbsp;index&amp;nbsp;&amp;lt;&amp;nbsp;chunk.length;&amp;nbsp;index&amp;nbsp;+=&amp;nbsp;1)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;sample&amp;nbsp;=&amp;nbsp;Math.max(-1,&amp;nbsp;Math.min(1,&amp;nbsp;chunk[index]));
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;view.setInt16(offset,&amp;nbsp;sample&amp;nbsp;&amp;lt;&amp;nbsp;0&amp;nbsp;?&amp;nbsp;sample&amp;nbsp;*&amp;nbsp;0x8000&amp;nbsp;:&amp;nbsp;sample&amp;nbsp;*&amp;nbsp;0x7fff,&amp;nbsp;true);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;offset&amp;nbsp;+=&amp;nbsp;2;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;new&amp;nbsp;File([buffer],&amp;nbsp;`recording-${Date.now()}.wav`,&amp;nbsp;{&amp;nbsp;type:&amp;nbsp;&amp;#39;audio/wav&amp;#39;&amp;nbsp;});
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;inputs.forEach((input)&amp;nbsp;=&amp;gt;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;input.addEventListener(&amp;#39;change&amp;#39;,&amp;nbsp;()&amp;nbsp;=&amp;gt;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;files&amp;nbsp;=&amp;nbsp;Array.from(input.files&amp;nbsp;||&amp;nbsp;[]);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;displayFiles(files,&amp;nbsp;input);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;openRecorder.addEventListener(&amp;#39;click&amp;#39;,&amp;nbsp;()&amp;nbsp;=&amp;gt;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;recorderPanel.hidden&amp;nbsp;=&amp;nbsp;false;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;recorderPanel.scrollIntoView({&amp;nbsp;behavior:&amp;nbsp;&amp;#39;smooth&amp;#39;,&amp;nbsp;block:&amp;nbsp;&amp;#39;nearest&amp;#39;&amp;nbsp;});
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;startRecording.addEventListener(&amp;#39;click&amp;#39;,&amp;nbsp;async&amp;nbsp;()&amp;nbsp;=&amp;gt;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;AudioContextClass&amp;nbsp;=&amp;nbsp;window.AudioContext&amp;nbsp;||&amp;nbsp;window.webkitAudioContext;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(!navigator.mediaDevices?.getUserMedia&amp;nbsp;||&amp;nbsp;!AudioContextClass)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;recorderStatus.textContent&amp;nbsp;=&amp;nbsp;&amp;#39;当前浏览器不支持网页录音，请使用下方按钮选择已有音频。&amp;#39;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;microphoneStream&amp;nbsp;=&amp;nbsp;await&amp;nbsp;navigator.mediaDevices.getUserMedia({
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;audio:&amp;nbsp;{&amp;nbsp;channelCount:&amp;nbsp;1,&amp;nbsp;echoCancellation:&amp;nbsp;true,&amp;nbsp;noiseSuppression:&amp;nbsp;true&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;recordingAudioContext&amp;nbsp;=&amp;nbsp;new&amp;nbsp;AudioContextClass();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;await&amp;nbsp;recordingAudioContext.resume();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;recordingSource&amp;nbsp;=&amp;nbsp;recordingAudioContext.createMediaStreamSource(microphoneStream);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;recordingProcessor&amp;nbsp;=&amp;nbsp;recordingAudioContext.createScriptProcessor(4096,&amp;nbsp;1,&amp;nbsp;1);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;recordingGain&amp;nbsp;=&amp;nbsp;recordingAudioContext.createGain();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;recordingGain.gain.value&amp;nbsp;=&amp;nbsp;0;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pcmChunks&amp;nbsp;=&amp;nbsp;[];
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pcmSampleCount&amp;nbsp;=&amp;nbsp;0;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;isRecording&amp;nbsp;=&amp;nbsp;true;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;recordingProcessor.onaudioprocess&amp;nbsp;=&amp;nbsp;(event)&amp;nbsp;=&amp;gt;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(!isRecording)&amp;nbsp;return;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;samples&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Float32Array(event.inputBuffer.length);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;event.inputBuffer.copyFromChannel(samples,&amp;nbsp;0);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pcmChunks.push(samples);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pcmSampleCount&amp;nbsp;+=&amp;nbsp;samples.length;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;};
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;recordingSource.connect(recordingProcessor);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;recordingProcessor.connect(recordingGain);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;recordingGain.connect(recordingAudioContext.destination);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;recordingStartedAt&amp;nbsp;=&amp;nbsp;Date.now();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;recorderStatus.textContent&amp;nbsp;=&amp;nbsp;&amp;#39;正在录音&amp;nbsp;00:00&amp;#39;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;recordingTimer&amp;nbsp;=&amp;nbsp;setInterval(()&amp;nbsp;=&amp;gt;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;elapsed&amp;nbsp;=&amp;nbsp;Math.floor((Date.now()&amp;nbsp;-&amp;nbsp;recordingStartedAt)&amp;nbsp;/&amp;nbsp;1000);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;minutes&amp;nbsp;=&amp;nbsp;String(Math.floor(elapsed&amp;nbsp;/&amp;nbsp;60)).padStart(2,&amp;nbsp;&amp;#39;0&amp;#39;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;seconds&amp;nbsp;=&amp;nbsp;String(elapsed&amp;nbsp;%&amp;nbsp;60).padStart(2,&amp;nbsp;&amp;#39;0&amp;#39;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;recorderStatus.textContent&amp;nbsp;=&amp;nbsp;`正在录音&amp;nbsp;${minutes}:${seconds}`;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;},&amp;nbsp;1000);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;startRecording.disabled&amp;nbsp;=&amp;nbsp;true;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;stopRecording.disabled&amp;nbsp;=&amp;nbsp;false;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;catch&amp;nbsp;(error)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;stopMicrophone();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;insecure&amp;nbsp;=&amp;nbsp;!window.isSecureContext;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;recorderStatus.textContent&amp;nbsp;=&amp;nbsp;insecure
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;?&amp;nbsp;&amp;#39;浏览器只允许&amp;nbsp;HTTPS&amp;nbsp;页面使用麦克风，请通过&amp;nbsp;HTTPS&amp;nbsp;打开本页。&amp;#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;:&amp;nbsp;&amp;#39;未能使用麦克风，请检查浏览器的网站麦克风权限。&amp;#39;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;stopRecording.addEventListener(&amp;#39;click&amp;#39;,&amp;nbsp;()&amp;nbsp;=&amp;gt;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(!isRecording&amp;nbsp;||&amp;nbsp;!recordingAudioContext&amp;nbsp;||&amp;nbsp;!pcmSampleCount)&amp;nbsp;return;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;isRecording&amp;nbsp;=&amp;nbsp;false;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;sampleRate&amp;nbsp;=&amp;nbsp;recordingAudioContext.sampleRate;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;duration&amp;nbsp;=&amp;nbsp;pcmSampleCount&amp;nbsp;/&amp;nbsp;sampleRate;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;recording&amp;nbsp;=&amp;nbsp;createWavFile(pcmChunks,&amp;nbsp;pcmSampleCount,&amp;nbsp;sampleRate);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;stopMicrophone();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;displayFiles([recording]);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;recorderStatus.textContent&amp;nbsp;=&amp;nbsp;`录音处理完成（${duration.toFixed(1)}&amp;nbsp;秒），可在下方下载。`;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;startRecording.disabled&amp;nbsp;=&amp;nbsp;false;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;stopRecording.disabled&amp;nbsp;=&amp;nbsp;true;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;clearButton.addEventListener(&amp;#39;click&amp;#39;,&amp;nbsp;clearSelection);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;window.addEventListener(&amp;#39;pagehide&amp;#39;,&amp;nbsp;()&amp;nbsp;=&amp;gt;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;stopMicrophone();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});
&amp;nbsp;&amp;nbsp;&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/pre&gt;&lt;p&gt;页面中的拍照和录像入口可以分别使用带有 &lt;code&gt;capture&lt;/code&gt; 属性的文件控件。拍照入口设置 &lt;code&gt;accept=&amp;quot;image/*&amp;quot;&lt;/code&gt;，录像入口设置 &lt;code&gt;accept=&amp;quot;video/*&amp;quot;&lt;/code&gt;，安卓APP会根据文件类型打开对应的系统采集页面。&lt;/p&gt;&lt;p&gt;拍摄照片：&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-html&quot;&gt;&amp;lt;input&amp;nbsp;type=&amp;quot;file&amp;quot;&amp;nbsp;accept=&amp;quot;image/*&amp;quot;&amp;nbsp;capture=&amp;quot;environment&amp;quot;&amp;nbsp;/&amp;gt;&lt;/pre&gt;&lt;p&gt;录制视频：&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-html&quot;&gt;&amp;lt;input&amp;nbsp;type=&amp;quot;file&amp;quot;&amp;nbsp;accept=&amp;quot;video/*&amp;quot;&amp;nbsp;capture=&amp;quot;environment&amp;quot;&amp;nbsp;/&amp;gt;&lt;/pre&gt;&lt;p&gt;音频录制可以通过 &lt;code&gt;navigator.mediaDevices.getUserMedia()&lt;/code&gt; 获取麦克风音频流，再使用浏览器音频接口处理并生成音频文件。普通文件入口不设置 &lt;code&gt;capture&lt;/code&gt;，因此会保留系统提供的文件、照片和视频选择方式。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/08/1786193047247_0340306a.png&quot; alt=&quot;拍照录像录音和文件选择页面&quot;/&gt;&lt;/p&gt;&lt;h2&gt;打包APK并配置权限&lt;/h2&gt;&lt;p&gt;打开HTML一键打包APK工具，选择包含 &lt;code&gt;index.html&lt;/code&gt; 的项目目录，填写应用名称、包名等打包信息。&lt;/p&gt;&lt;p&gt;在权限设置中勾选“相机权限”和“麦克风权限”：&lt;/p&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;相机权限用于拍摄照片、录制视频以及网页调用摄像头。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;麦克风权限用于网页录音；录制带声音的视频时也需要该权限。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;普通文件选择通过安卓系统文件选择器完成，用户只会授权当前选择的文件。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/08/1786193047821_97366786.png&quot; alt=&quot;打包APK时配置相机和麦克风权限&quot;/&gt;&lt;/p&gt;&lt;p&gt;确认配置后开始打包，等待工具生成APK文件。&lt;/p&gt;&lt;h2&gt;安装并测试功能&lt;/h2&gt;&lt;p&gt;将生成的APK安装到安卓手机，依次测试页面中的四个入口：&lt;/p&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;点击“拍摄照片”，确认APP直接打开相机拍照页面。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;点击“录制视频”，确认APP直接打开视频录制页面，并且能够正常保存视频。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;点击“录制音频”，首次使用时允许APP访问麦克风，停止录音后确认页面能够显示并下载录音文件。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;点击“选择文件”，确认系统文件选择界面正常打开，并且可以选择一个或多个文件。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;不同品牌手机使用的相机和文件管理应用可能不同，因此系统界面的样式会有差异。&lt;code&gt;capture&lt;/code&gt; 属性用于告诉安卓当前需要拍照还是录像，但最终的采集界面由手机系统和默认相机应用提供。&lt;/p&gt;&lt;p&gt;如果拍照或录像入口没有打开对应页面，请先检查APP的相机权限；如果网页无法开始录音，请检查麦克风权限。对于在线页面，网页录音通常还要求通过HTTPS加载；打包到APP内的本地页面则由WebView和APP权限共同控制。&lt;/p&gt;</description><pubDate>Sat, 08 Aug 2026 20:44:05 +0800</pubDate></item><item><title>HTML项目打包APK：二维码扫码功能实现</title><link>http://ylongsoft.com/post/html-apk-qrcode-scanner-ylongsoft.html</link><description>&lt;p&gt;在HTML项目中，可以使用 &lt;code&gt;html5-qrcode&lt;/code&gt; 调用手机摄像头并识别二维码。完成页面代码后，再通过HTML一键打包APK工具勾选相机权限，即可生成支持扫码的安卓APP。&lt;/p&gt;
&lt;p&gt;如果你不了解HTML打包APK工具, 想要进一步了解, 可以查看官网:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://html2apk.leapever.com/&quot;&gt;HTML一键打包APK工具官网&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;创建扫码页面&lt;/h2&gt;
&lt;p&gt;在HTML项目入口目录中创建 &lt;code&gt;index.html&lt;/code&gt;，写入以下代码。页面点击“开始扫码”后会申请摄像头权限，识别成功时停止摄像头并显示二维码内容。&lt;/p&gt;
&lt;pre class=&quot;prism-highlight prism-language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;!doctype html&amp;gt;
&amp;lt;html lang=&amp;quot;zh-CN&amp;quot;&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
        &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1, viewport-fit=cover&amp;quot; /&amp;gt;
        &amp;lt;title&amp;gt;二维码扫码&amp;lt;/title&amp;gt;
        &amp;lt;script src=&amp;quot;https://unpkg.com/html5-qrcode@2.3.8/html5-qrcode.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
        &amp;lt;style&amp;gt;
            * {
                box-sizing: border-box;
            }

            body {
                min-height: 100vh;
                margin: 0;
                padding: 20px;
                display: grid;
                place-items: center;
                font-family: 'Microsoft YaHei', sans-serif;
                background: #f4f7f5;
                color: #17221c;
            }

            main {
                width: min(100%, 440px);
            }

            h1 {
                margin: 0 0 8px;
                font-size: 30px;
            }

            p {
                margin: 0 0 18px;
                color: #526159;
            }

            #reader {
                width: 100%;
                min-height: 280px;
                overflow: hidden;
                border: 1px solid #cad4ce;
                border-radius: 8px;
                background: #101511;
            }

            #reader video {
                object-fit: cover;
            }

            .actions {
                display: grid;
                grid-template-columns: 1fr 1fr;
                gap: 10px;
                margin-top: 14px;
            }

            button {
                min-height: 48px;
                border: 0;
                border-radius: 6px;
                padding: 0 16px;
                font: inherit;
                font-weight: 700;
            }

            #startButton {
                background: #197447;
                color: white;
            }

            #stopButton {
                background: #dfe6e2;
                color: #26352d;
            }

            button:disabled {
                opacity: 0.5;
            }

            #status {
                min-height: 24px;
                margin-top: 12px;
                font-size: 14px;
                color: #526159;
            }
        &amp;lt;/style&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;main&amp;gt;
            &amp;lt;h1&amp;gt;二维码扫码&amp;lt;/h1&amp;gt;
            &amp;lt;p&amp;gt;点击开始后，将摄像头对准二维码。&amp;lt;/p&amp;gt;
            &amp;lt;div id=&amp;quot;reader&amp;quot; aria-label=&amp;quot;摄像头扫码区域&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
            &amp;lt;div class=&amp;quot;actions&amp;quot;&amp;gt;
                &amp;lt;button id=&amp;quot;startButton&amp;quot; type=&amp;quot;button&amp;quot;&amp;gt;开始扫码&amp;lt;/button&amp;gt;
                &amp;lt;button id=&amp;quot;stopButton&amp;quot; type=&amp;quot;button&amp;quot; disabled&amp;gt;停止扫码&amp;lt;/button&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div id=&amp;quot;status&amp;quot; role=&amp;quot;status&amp;quot;&amp;gt;等待开始&amp;lt;/div&amp;gt;
        &amp;lt;/main&amp;gt;

        &amp;lt;script&amp;gt;
            const reader = new Html5Qrcode('reader');
            const startButton = document.getElementById('startButton');
            const stopButton = document.getElementById('stopButton');
            const status = document.getElementById('status');
            let isScanning = false;
            let resultHandled = false;

            async function stopScanning() {
                if (!isScanning) return;

                await reader.stop();
                isScanning = false;
                startButton.disabled = false;
                stopButton.disabled = true;
                status.textContent = '扫码已停止';
            }

            async function startScanning() {
                startButton.disabled = true;
                status.textContent = '正在请求摄像头权限...';
                resultHandled = false;

                try {
                    await reader.start(
                        { facingMode: 'environment' },
                        { fps: 10, qrbox: { width: 240, height: 240 } },
                        async decodedText =&amp;gt; {
                            if (resultHandled) return;
                            resultHandled = true;
                            await stopScanning();
                            alert(decodedText);
                        },
                        () =&amp;gt; {}
                    );

                    isScanning = true;
                    stopButton.disabled = false;
                    status.textContent = '正在扫描...';
                } catch (error) {
                    startButton.disabled = false;
                    status.textContent = '无法打开摄像头，请检查相机权限。';
                    console.error(error);
                }
            }

            startButton.addEventListener('click', startScanning);
            stopButton.addEventListener('click', () =&amp;gt; {
                stopScanning().catch(console.error);
            });

            window.addEventListener('pagehide', () =&amp;gt; {
                if (isScanning) reader.stop().catch(() =&amp;gt; {});
            });
        &amp;lt;/script&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;示例通过 CDN 加载 &lt;code&gt;html5-qrcode&lt;/code&gt;。如果APP需要完全离线运行，请提前下载 &lt;code&gt;html5-qrcode.min.js&lt;/code&gt; 放入项目目录，并将页面中的脚本地址改为本地相对路径。&lt;/p&gt;
&lt;h2&gt;打包APK并勾选相机权限&lt;/h2&gt;
&lt;p&gt;打开HTML一键打包APK工具，选择包含 &lt;code&gt;index.html&lt;/code&gt; 的项目目录，填写应用名称、包名等打包信息。在权限设置中勾选“相机权限”，否则APP无法调用手机摄像头。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/08/1786109167556_c6b01d9d.png&quot; alt=&quot;打包APK时勾选相机权限&quot;&gt;&lt;/p&gt;
&lt;p&gt;确认配置后开始打包，等待工具生成APK文件。&lt;/p&gt;
&lt;h2&gt;安装并测试扫码&lt;/h2&gt;
&lt;p&gt;将生成的APK安装到安卓手机。首次点击“开始扫码”时，系统会弹出相机授权提示，选择允许后，将摄像头对准二维码。识别成功后，页面会停止扫描并弹出二维码中的文字或链接。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/08/1786109167967_a2eb5094.png&quot; alt=&quot;打包后的APP扫码运行效果&quot;&gt;&lt;/p&gt;
&lt;p&gt;如果页面提示无法打开摄像头，请先在手机的应用权限设置中确认相机权限已经允许。扫码页面使用 CDN 脚本时，还需要确保手机能够正常访问网络；改用本地脚本后则不受此限制。&lt;/p&gt;
</description><pubDate>Fri, 07 Aug 2026 21:26:06 +0800</pubDate></item><item><title>C#混淆选项选择指南：恒盾加密大师详解</title><link>http://ylongsoft.com/post/csharp-obfuscator-options-guide.html</link><description>&lt;p&gt;恒盾C#混淆加密大师提供了基本保护、垃圾代码注入、代码混淆和重命名等多类选项。这些功能面向不同的攻击场景：部分用于阻止调试和反编译工具；另一些增加反编译代码理解难度；还有直接改变类型、函数和字段的命名。&lt;/p&gt;
&lt;p&gt;实际选用时，无需不加区分地启用全部选项。更合理的做法是先理解每类功能的作用域，再根据项目类型（DLL、控制台程序、WinForms、WPF等）逐步启用，并在每次调整后完成启动和核心功能测试。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/08/1785844596803_dc34a40c.png&quot; alt=&quot;界面&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://csharpobf.leapever.com/&quot;&gt;官方网址&lt;/a&gt;&lt;/p&gt;
&lt;!-- truncate --&gt;
&lt;h2&gt;四类混淆选项分别解决什么问题&lt;/h2&gt;
&lt;p&gt;软件中的选项可划分为四类：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;基本保护选项&lt;/strong&gt;：针对调试器、反编译工具、程序篡改和虚拟机运行环境进行防护。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;注入功能选项&lt;/strong&gt;：向程序集注入无用字段、指令、函数、类型或字符串，增加静态分析干扰。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;混淆功能选项&lt;/strong&gt;：改变代码、数据或程序集结构，使反编译结果更难还原。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;重命名选项&lt;/strong&gt;：修改Module、字段、函数、命名空间、参数、类型和Assembly等标识符。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这四类功能可以灵活组合：重命名负责降低代码可读性；字符串和流程类选项保护关键逻辑；基本保护针对常见分析工具和运行环境形成额外防线。&lt;/p&gt;
&lt;h2&gt;基本保护选项&lt;/h2&gt;
&lt;p&gt;基本保护主要面向调试、反编译、篡改和运行环境检测。部分功能在检测到特定行为时会终止程序，因此启用后需在目标用户真实环境中测试。&lt;/p&gt;
&lt;h3&gt;防调试、防DnSpy与防断点&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;防调试&lt;/strong&gt;：检测Visual Studio或其他调试工具附加行为，发现后自动退出。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;防DnSpy&lt;/strong&gt;：针对DnSpy动态调试检测，发现后退出。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;防断点&lt;/strong&gt;：检测程序运行中异常超长停顿，防止通过断点跟踪代码。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这三个选项与运行时分析相关，适合保护授权校验、关键算法和商业逻辑。由于性能较弱的设备或特殊环境可能影响检测效果，建议先在测试版启用，再覆盖不同配置设备验证。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/08/1785844597452_41a148c9.png&quot; alt=&quot;防DnSpy处理效果&quot;&gt;&lt;/p&gt;
&lt;h3&gt;防ILDasm、防ILSpy与防de4dot&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;防ILDasm&lt;/strong&gt;：阻止系统ILDasm正常反编译处理后的DLL或EXE。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;防ILSpy&lt;/strong&gt;：让处理后程序集无法被ILSpy正常解析。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;防de4dot&lt;/strong&gt;：干扰de4dot自动反混淆。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这组功能用于阻止常见静态分析和自动还原工具。可与重命名、流程混淆、字符串加密搭配，让分析者难以打开程序集，也难以理解已反编译代码。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/08/1785844597835_360c19a7.png&quot; alt=&quot;防ILDasm处理效果&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/08/1785844598040_1f85bf03.png&quot; alt=&quot;防de4dot处理效果&quot;&gt;&lt;/p&gt;
&lt;h3&gt;防篡改与加壳&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;防篡改（仅限EXE）&lt;/strong&gt;：校验EXE是否被修改，改动后无法运行。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;加壳（仅限EXE）&lt;/strong&gt;：加密原生程序到外层EXE，避免静态反编译。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;仅适用于EXE。防篡改适合需保证文件完整性的桌面程序；加壳适合整体隐藏原始程序集。处理后需检查数字签名、杀毒兼容性及更新流程。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/08/1785844598260_977664ab.png&quot; alt=&quot;EXE加壳处理效果&quot;&gt;&lt;/p&gt;
&lt;h3&gt;防虚拟机&lt;/h3&gt;
&lt;p&gt;防虚拟机检测程序是否运行在虚拟机中，检测到后停止运行。适合禁止虚拟机运行的应用，但不适合云桌面、虚拟化服务器、自动化测试或沙箱环境。启用前需确认用户部署环境，若正常客户可能使用VMware、Hyper-V等，建议谨慎。&lt;/p&gt;
&lt;h2&gt;注入功能选项&lt;/h2&gt;
&lt;p&gt;注入功能通过增加干扰内容，让反编译后程序集结构更复杂，而非替代关键代码加密。&lt;/p&gt;
&lt;h3&gt;垃圾域、垃圾函数与垃圾类型&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;垃圾域&lt;/strong&gt;：向类型中注入大量无用字段。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;垃圾函数&lt;/strong&gt;：注入不参与业务的函数。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;垃圾类型&lt;/strong&gt;：注入额外类型。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些选项让反编译工具显示更多成员和类型，增加定位真实业务代码的难度。对体积敏感项目需比较处理前后文件大小和加载表现。&lt;/p&gt;
&lt;h3&gt;垃圾空指令与垃圾字符串&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;垃圾空指令&lt;/strong&gt;：在IL指令中注入无用指令，增加阅读难度。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;垃圾字符串&lt;/strong&gt;：注入无用字符串，干扰基于字符串搜索的定位。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;垃圾空指令适合配合流程混淆；垃圾字符串干扰接口地址、提示文本或关键字检索。主要增加分析成本。&lt;/p&gt;
&lt;h2&gt;混淆功能选项&lt;/h2&gt;
&lt;p&gt;混淆功能处理程序集信息、代码流程、常量、调用关系等。一些选项影响较大，建议从常用选项开始逐步增加强度。&lt;/p&gt;
&lt;h3&gt;程序集与文件结构混淆&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;文件信息混淆&lt;/strong&gt;：修改程序内部名称、Assembly信息。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;文件时间混淆&lt;/strong&gt;：修改加密文件时间戳。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DOS头混淆&lt;/strong&gt;：修改EXE的DOS头，去除常见可识别字符串。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;混淆引用列表&lt;/strong&gt;：处理程序集引用列表，使依赖关系更模糊。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;加密资源文件&lt;/strong&gt;：加密程序集中的资源，防止直接查看。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;若程序依赖程序集名称、文件版本、反射、插件扫描或资源加载路径，需重点验证。加密资源文件还需测试图片、语言资源等能否正常读取。&lt;/p&gt;
&lt;h3&gt;流程与函数调用混淆&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;流程混淆&lt;/strong&gt;：打乱函数内部指令并增加跳转，使控制流程难以理解。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;函数调用混淆&lt;/strong&gt;：通过函数token调用目标函数，降低可读性。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;链式函数混淆&lt;/strong&gt;：将一次调用转为多层嵌套调用，增加分析难度。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;异步混淆&lt;/strong&gt;：将部分代码转换为异步调用形式，使静态跟踪更困难。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;错误函数体混淆&lt;/strong&gt;：加入错误指令，干扰静态分析工具解析函数体。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;流程混淆是核心算法保护的首选。函数调用、链式和异步混淆进一步改变结构，适合不依赖特殊调用顺序的业务代码。对UI线程、启动入口、COM调用、反射调用等需逐项测试。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/08/1785844598480_e7f65e34.png&quot; alt=&quot;流程混淆前后对比&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/08/1785844598703_7d0947f6.png&quot; alt=&quot;函数调用混淆效果&quot;&gt;&lt;/p&gt;
&lt;h3&gt;字符串保护&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;字符串加密&lt;/strong&gt;：加密字符串，避免静态分析直接找到关键文本。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;字符串拆分&lt;/strong&gt;：将完整字符串拆成多个组合，降低搜索效率。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;字符串隐藏&lt;/strong&gt;：加密后存放到原生函数，使反编译工具无法获取值。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;字符串加密适合保护接口地址、协议字段、算法参数；拆分可干扰简单检索；隐藏强度更高，适合少量重要字符串。配置文件中的密码、令牌需用专门密钥管理方案。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/08/1785844598917_70fe88f8.png&quot; alt=&quot;字符串加密效果&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/08/1785844599129_9039d35a.png&quot; alt=&quot;字符串拆分效果&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/08/1785844599333_fbc0f121.png&quot; alt=&quot;字符串隐藏效果&quot;&gt;&lt;/p&gt;
&lt;h3&gt;数字、布尔值与小数保护&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;布尔混淆&lt;/strong&gt;：将布尔值转为更难识别的基础类型表达。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数字混淆&lt;/strong&gt;：将整型数字转化为复杂运算，隐藏常量。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数字隐藏&lt;/strong&gt;：将整型数字加密存放到原生函数。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;小数混淆&lt;/strong&gt;：加密代码中的小数，避免直接看到原始值。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;适合保护算法常量、状态标识、限制数值和业务参数。与流程混淆配合，静态分析难度更高。涉及浮点精度或高频计算的代码需重点验证。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/08/1785844599550_be995e0e.png&quot; alt=&quot;数字混淆效果&quot;&gt;&lt;/p&gt;
&lt;h3&gt;函数体隐藏与原生化EXE&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;函数体隐藏&lt;/strong&gt;：加密函数体，使反编译工具无法查看内部代码。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;原生化EXE&lt;/strong&gt;：将EXE转为原生程序，C#反编译工具无法分析IL。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;适合保护核心实现。函数体隐藏可加强保护重要算法；原生化EXE从整体改变程序形态，仅适用于EXE。启用后需完整测试启动、异常处理、依赖加载等。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/08/1785844599754_7b29091b.png&quot; alt=&quot;函数体隐藏效果&quot;&gt;&lt;/p&gt;
&lt;h2&gt;重命名选项&lt;/h2&gt;
&lt;p&gt;重命名是C#混淆中最直观的保护，将具有业务含义的标识符替换为难以理解的新名称，移除语义。&lt;/p&gt;
&lt;h3&gt;重命名模式怎么选&lt;/h3&gt;
&lt;p&gt;软件提供五种模式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;字母数字模式&lt;/strong&gt;：字母数字组合，适合常规项目。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UUID模式&lt;/strong&gt;：长名称，增加阅读和检索难度。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特殊字符模式&lt;/strong&gt;：使用特殊字符，适合更高阅读干扰。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Base64模式&lt;/strong&gt;：Base64风格字符串。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;英文单词模式&lt;/strong&gt;：英文单词替换，表面可读但失去真实含义。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不同模式影响外观，不改变勾选对象。日常可从字母数字模式开始；若外部工具或日志系统对字符有限制，避免使用特殊字符模式。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/08/1785844599997_802d3c4b.png&quot; alt=&quot;字母数字重命名模式&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/08/1785844600232_0a9be64e.png&quot; alt=&quot;UUID重命名模式&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/08/1785844600437_f99ee60b.png&quot; alt=&quot;特殊字符重命名模式&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/08/1785844600642_c39f068d.png&quot; alt=&quot;Base64重命名模式&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/08/1785844600846_bc4a0eca.png&quot; alt=&quot;英文单词重命名模式&quot;&gt;&lt;/p&gt;
&lt;h3&gt;可以重命名哪些对象&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Module重命名&lt;/strong&gt;：修改全局Module名称。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;域重命名&lt;/strong&gt;：修改类中字段名称。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;函数重命名&lt;/strong&gt;：修改方法名称。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;命名空间重命名&lt;/strong&gt;：修改类型所在命名空间。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参数重命名&lt;/strong&gt;：修改函数参数名称。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;类型重命名&lt;/strong&gt;：修改类、结构体等类型名称。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Assembly重命名&lt;/strong&gt;：修改程序集名称。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;普通内部代码可优先启用字段、函数、参数和类型重命名。涉及反射、依赖注入、序列化、XAML等时，名称可能是运行时契约，需先确认。Assembly重命名影响依赖程序集、插件加载等，处理多个DLL时需验证引用解析。&lt;/p&gt;
&lt;h2&gt;不同项目可以怎样组合&lt;/h2&gt;
&lt;h3&gt;普通桌面程序&lt;/h3&gt;
&lt;p&gt;先启用函数、字段、参数和类型重命名，加入字符串加密、数字混淆和流程混淆。确认功能正常后，再根据需要增加防调试、防ILSpy、防DnSpy或加壳。&lt;/p&gt;
&lt;h3&gt;供第三方调用的类库&lt;/h3&gt;
&lt;p&gt;优先保护内部类型和实现。公开API、反射入口、序列化模型需谨慎重命名，否则可能导致调用方无法找到成员。字符串加密、流程混淆和函数体隐藏可保护不对外暴露的核心逻辑。&lt;/p&gt;
&lt;h3&gt;包含重要算法的EXE&lt;/h3&gt;
&lt;p&gt;组合流程混淆、字符串隐藏、数字隐藏、函数调用混淆和函数体隐藏。完成兼容性测试后，考虑加壳、防篡改或原生化EXE。高强度选项应逐项增加并保留测试结果。&lt;/p&gt;
&lt;h2&gt;推荐的启用和测试顺序&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;保留原始DLL或EXE，准备覆盖核心功能的测试用例。&lt;/li&gt;
&lt;li&gt;先启用字段、函数、参数和类型重命名，验证程序启动及运行。&lt;/li&gt;
&lt;li&gt;加入字符串加密、数字混淆和流程混淆，检查核心业务结果和性能。&lt;/li&gt;
&lt;li&gt;按需启用垃圾代码注入和反编译工具防护。&lt;/li&gt;
&lt;li&gt;最后测试函数体隐藏、加壳、原生化EXE等大范围选项。&lt;/li&gt;
&lt;li&gt;每增加一组功能都重新测试启动、登录、配置读写、网络请求等。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;若出现异常，可回退到上一个可用组合再逐个启用。&lt;/p&gt;
&lt;h2&gt;使用时需要注意什么&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;保护强度越高并不代表越适合所有项目，稳定运行是前提。&lt;/li&gt;
&lt;li&gt;依赖反射、序列化、XAML、插件机制等项目，需重点测试重命名兼容性。&lt;/li&gt;
&lt;li&gt;防篡改、加壳和原生化EXE仅限EXE，不能用于DLL。&lt;/li&gt;
&lt;li&gt;防虚拟机可能影响云桌面、虚拟化服务器中的正常用户。&lt;/li&gt;
&lt;li&gt;处理后文件应在目标操作系统、架构和性能配置上验证。&lt;/li&gt;
&lt;li&gt;混淆用于提高逆向成本，不能替代权限控制、签名校验、密钥管理和服务端安全。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;恒盾C#混淆加密大师各类选项承担不同作用：基本保护应对调试和反编译工具；注入功能增加干扰；混淆功能隐藏代码流程和关键数据；重命名移除业务语义。&lt;/p&gt;
&lt;p&gt;对多数项目，可从重命名、字符串加密、数字混淆和流程混淆开始，再根据程序类型增加防调试、工具防护、函数体隐藏、加壳或原生化EXE。逐步启用、逐步测试，在保护强度和兼容性之间取得平衡。&lt;/p&gt;
</description><pubDate>Tue, 04 Aug 2026 19:56:39 +0800</pubDate></item><item><title>C#混淆随机种子的作用与使用教程</title><link>http://ylongsoft.com/post/csharp-obfuscator-random-seed-guide-zblog-ylongsoft.html</link><description>&lt;p&gt;在混淆 C# 应用时，很多开发者会发现，即使输入文件和配置一模一样，每次生成的结果也可能不同。这是因为恒盾C#混淆加密大师默认引入了随机性，避免每次构建都产生完全相同的结果。但在某些场景下——比如排查问题、做回归测试、或者对接 CI/CD 流程——我们反而希望结果是可复现的，以便对比和定位。&lt;/p&gt;
&lt;p&gt;这时，随机种子就派上用场了。它本质上是一个控制混淆过程随机行为的开关。填上一个固定的值后，对于同一个 EXE 或 DLL，在配置完全一致的情况下，每次处理都会得到相同的结果；如果不填，软件就会自动随机处理，保持构建之间的差异。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/08/1785763880134_c7a11028.png&quot; alt=&quot;恒盾C#混淆加密大师随机种子配置&quot;&gt;&lt;/p&gt;
&lt;h2&gt;随机种子有什么作用&lt;/h2&gt;
&lt;p&gt;随机种子让开发者可以在“可复现的结果”和“每次结果随机”之间灵活选择。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;填写固定种子&lt;/strong&gt;：适用于重复构建、对比结果、排查问题，或者需要保持发布流程一致。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;留空不填&lt;/strong&gt;：软件自动随机，适用于希望不同构建产生不同混淆结果的日常发布。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;需要特别注意：随机种子不会改变你启用了哪些保护功能，也不会替代字符串加密、流程混淆或重命名。它只影响结果是否容易复现。&lt;/p&gt;
&lt;h2&gt;哪些场景适合设置固定种子&lt;/h2&gt;
&lt;h3&gt;1. 排查混淆后的运行问题&lt;/h3&gt;
&lt;p&gt;如果程序在混淆后出现异常，保留输入文件、配置和随机种子，重新处理一次。固定种子能减少重复测试时的结果差异，让问题更容易定位。&lt;/p&gt;
&lt;h3&gt;2. 进行版本回归测试&lt;/h3&gt;
&lt;p&gt;在同一个版本上反复验证启动、登录、授权、读写等核心功能时，固定种子可以提供更稳定的测试条件，方便对比不同保护选项的效果。&lt;/p&gt;
&lt;h3&gt;3. 接入自动化构建和 CI/CD&lt;/h3&gt;
&lt;p&gt;将混淆步骤集成到构建脚本或持续集成流水线中后，为项目指定固定种子，可以让开发机、构建机和流水线使用相同的条件，便于复现和排错。&lt;/p&gt;
&lt;h3&gt;4. 团队共享混淆配置&lt;/h3&gt;
&lt;p&gt;多人维护同一个项目时，把随机种子和其他选项一起保存到配置文件里，团队成员加载统一配置后，就能减少因随机结果带来的额外沟通成本。&lt;/p&gt;
&lt;h3&gt;5. 保存可复现的发布记录&lt;/h3&gt;
&lt;p&gt;对于重要版本，把当次使用的输入文件、配置、软件版本和种子一起归档。以后要回溯或者重新生成该版本时，可以直接恢复当时的条件。&lt;/p&gt;
&lt;h2&gt;如何使用随机种子&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;打开恒盾C#混淆加密大师，选择需要处理的 DLL 或 EXE。&lt;/li&gt;
&lt;li&gt;根据项目需求，勾选代码保护、代码混淆和重命名等选项。&lt;/li&gt;
&lt;li&gt;在主界面下方找到“随机种子”输入框。&lt;/li&gt;
&lt;li&gt;输入便于识别的字母或数字，例如项目名称与版本号的组合。&lt;/li&gt;
&lt;li&gt;选择输出路径，点击“开始处理”。&lt;/li&gt;
&lt;li&gt;如果需要保留当前设置，点击“保存配置”。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;随机种子支持字母和数字。建议使用团队容易识别、能长期保存的内容，例如：&lt;/p&gt;
&lt;pre class=&quot;prism-highlight prism-language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;MyProduct150
Release202608
ClientA2026
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果不需要复现结果，保持输入框为空即可。&lt;/p&gt;
&lt;h2&gt;固定种子与留空随机应该怎么选&lt;/h2&gt;
&lt;p&gt;如果当前工作重点是测试、排错、回归或自动化构建，建议设置固定种子。这样多次处理的结果更容易比较，也便于其他开发者复现。&lt;/p&gt;
&lt;p&gt;如果重点是正式发布，并且希望不同批次的混淆结果保持差异，可以留空或者每个版本使用不同的种子。&lt;/p&gt;
&lt;p&gt;一个常见的策略是：测试阶段固定使用项目级种子，正式发布时按版本换用不同的种子。这样既方便日常验证，也能让不同版本之间保持足够的差异。&lt;/p&gt;
&lt;h2&gt;使用时需要注意什么&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;相同种子需要搭配相同的输入文件和配置，才能得到一致的结果。&lt;/li&gt;
&lt;li&gt;输入程序、保护选项或软件版本变化后，结果也可能跟着变。&lt;/li&gt;
&lt;li&gt;随机种子不是密码，不要在里面填写账号、密码等敏感信息。&lt;/li&gt;
&lt;li&gt;换随机种子不等于提高保护强度，实际效果取决于你启用的混淆和加密功能。&lt;/li&gt;
&lt;li&gt;需要复现正式版本时，记得同时保存输入文件、配置、版本号和种子。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;恒盾C#混淆加密大师的随机种子功能，让开发者可以根据实际需求决定混淆结果的稳定性。在排查问题、回归测试、团队协作和 CI/CD 场景中，固定种子可以大幅简化构建管理；而在需要不同版本保持差异的正式发布中，则可以留空或更换种子。合理使用这个配置项，可以让 C# 代码混淆的流程更加清晰、可控，也更适合长期的维护和自动化发布。&lt;/p&gt;
</description><pubDate>Mon, 03 Aug 2026 21:31:19 +0800</pubDate></item><item><title>在.NET项目集成混淆加密：自动化保护DLL和EXE</title><link>http://ylongsoft.com/post/integrate-obfuscator-cli-into-dotnet-build-ylongsoft.html</link><description>&lt;p&gt;为了保护C#程序, 在.NET项目交付DLL或EXE之前，通常需要先编译，再手动打开混淆工具、选择程序集、加载配置并导出结果。对于经常发布的项目，这套操作不仅重复，还容易漏选文件或使用错误配置。&lt;/p&gt;
&lt;p&gt;更稳定的方式是把&lt;a href=&quot;https://csharpobf.leapever.com/&quot;&gt;恒盾C#混淆加密大师&lt;/a&gt;的CLI接入MSBuild：Release构建完成后自动获取当前项目的真实输出路径，调用CLI生成保护版本。由于Visual Studio、Rider、&lt;code&gt;dotnet build&lt;/code&gt;和多数CI平台最终都会调用MSBuild，因此只需在项目文件中维护一套规则。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/07/1785063360170_0823c148.png&quot; alt=&quot;C#混淆加密大师1.5.0截图&quot;&gt;&lt;/p&gt;
&lt;!-- truncate --&gt;
&lt;h2&gt;集成后的构建流程&lt;/h2&gt;
&lt;p&gt;本文实现的流程如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;正常编译.NET项目，不修改原始构建产物。&lt;/li&gt;
&lt;li&gt;仅在Release配置且已经配置CLI路径时执行混淆。&lt;/li&gt;
&lt;li&gt;将&lt;code&gt;.deps.json&lt;/code&gt;、&lt;code&gt;.runtimeconfig.json&lt;/code&gt;和依赖DLL复制到&lt;code&gt;protected&lt;/code&gt;目录。&lt;/li&gt;
&lt;li&gt;使用&lt;code&gt;--input&lt;/code&gt;和&lt;code&gt;--output&lt;/code&gt;覆盖配置文件中的路径，由CLI生成受保护的主程序集。&lt;/li&gt;
&lt;li&gt;CLI返回非零退出码或未生成目标文件时让构建失败，避免误发布未保护的程序。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;最终目录类似下面这样：&lt;/p&gt;
&lt;pre class=&quot;prism-highlight prism-language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;bin/Release/net8.0/
├─ DotNetObfuscationSample.dll          # 原始构建产物
├─ DotNetObfuscationSample.deps.json
├─ DotNetObfuscationSample.runtimeconfig.json
└─ protected/
   ├─ DotNetObfuscationSample.dll       # 已混淆的主程序集
   ├─ DotNetObfuscationSample.deps.json
   └─ DotNetObfuscationSample.runtimeconfig.json
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;原始目录可继续用于调试和问题定位，发布时只取&lt;code&gt;protected&lt;/code&gt;目录即可。&lt;/p&gt;
&lt;h2&gt;第一步：准备CLI和混淆配置&lt;/h2&gt;
&lt;p&gt;安装恒盾C#混淆加密大师后，在安装目录中找到&lt;code&gt;CLI.exe&lt;/code&gt;。然后在图形界面中配置需要启用的保护功能，通过“文件 &amp;gt; 导出配置”得到&lt;code&gt;obfuscation.csop&lt;/code&gt;，并把它放在&lt;code&gt;.csproj&lt;/code&gt;同一目录。配置文件中的&lt;code&gt;ModulePath&lt;/code&gt;和&lt;code&gt;OutputPath&lt;/code&gt;可以保留示例值，因为构建时会通过CLI参数覆盖：&lt;/p&gt;
&lt;pre class=&quot;prism-highlight prism-language-jsonc&quot;&gt;&lt;code class=&quot;language-jsonc&quot;&gt;{
    &amp;quot;ModulePath&amp;quot;: &amp;quot;bin/Release/net8.0/MyApp.dll&amp;quot;,
    &amp;quot;OutputPath&amp;quot;: &amp;quot;bin/Release/net8.0/protected/MyApp.dll&amp;quot;,
    &amp;quot;AntiILDasm&amp;quot;: true,
    &amp;quot;StringConfusor&amp;quot;: true,
    &amp;quot;IntConfusor&amp;quot;: true,
    &amp;quot;FieldRename&amp;quot;: true,
    &amp;quot;MethodRename&amp;quot;: true,
    &amp;quot;ParamRename&amp;quot;: true,
    &amp;quot;TypeRename&amp;quot;: true,
    &amp;quot;RenameMode&amp;quot;: 1,
    &amp;quot;Seed&amp;quot;: &amp;quot;my-project-release&amp;quot;,
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;实际使用时建议保留软件导出的完整配置，不要只复制上面的精简片段。可先启用少量保护选项，验证程序正常启动和主要功能后，再逐步增加保护强度。&lt;/p&gt;
&lt;h2&gt;第二步：在项目文件中加入MSBuild目标&lt;/h2&gt;
&lt;p&gt;打开需要保护的&lt;code&gt;.csproj&lt;/code&gt;，在&lt;code&gt;&amp;lt;/Project&amp;gt;&lt;/code&gt;之前加入下面的内容：&lt;/p&gt;
&lt;pre class=&quot;prism-highlight prism-language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;PropertyGroup&amp;gt;
  &amp;lt;ObfuscatorCliPath Condition=&amp;quot;'$(ObfuscatorCliPath)' == ''&amp;quot;&amp;gt;$(OBFUSCATOR_CLI_PATH)&amp;lt;/ObfuscatorCliPath&amp;gt;
  &amp;lt;RunObfuscator Condition=&amp;quot;'$(RunObfuscator)' == '' and '$(Configuration)' == 'Release' and '$(ObfuscatorCliPath)' != ''&amp;quot;&amp;gt;true&amp;lt;/RunObfuscator&amp;gt;
  &amp;lt;RunObfuscator Condition=&amp;quot;'$(RunObfuscator)' == ''&amp;quot;&amp;gt;false&amp;lt;/RunObfuscator&amp;gt;
  &amp;lt;ObfuscatorConfigPath&amp;gt;$(MSBuildProjectDirectory)\obfuscation.csop&amp;lt;/ObfuscatorConfigPath&amp;gt;
&amp;lt;/PropertyGroup&amp;gt;

&amp;lt;Target Name=&amp;quot;ObfuscateReleaseOutput&amp;quot;
        AfterTargets=&amp;quot;Build&amp;quot;
        Condition=&amp;quot;'$(Configuration)' == 'Release' and '$(RunObfuscator)' == 'true'&amp;quot;&amp;gt;
  &amp;lt;PropertyGroup&amp;gt;
    &amp;lt;ObfuscatedOutputDir Condition=&amp;quot;'$(ObfuscatedOutputDir)' == ''&amp;quot;&amp;gt;$(TargetDir)protected\&amp;lt;/ObfuscatedOutputDir&amp;gt;
    &amp;lt;ObfuscatedOutputPath Condition=&amp;quot;'$(ObfuscatedOutputPath)' == ''&amp;quot;&amp;gt;$(ObfuscatedOutputDir)$(TargetFileName)&amp;lt;/ObfuscatedOutputPath&amp;gt;
  &amp;lt;/PropertyGroup&amp;gt;

  &amp;lt;Error Condition=&amp;quot;'$(ObfuscatorCliPath)' == ''&amp;quot;
         Text=&amp;quot;RunObfuscator=true, but ObfuscatorCliPath or OBFUSCATOR_CLI_PATH is not configured.&amp;quot; /&amp;gt;
  &amp;lt;Error Condition=&amp;quot;!Exists('$(ObfuscatorCliPath)')&amp;quot;
         Text=&amp;quot;Obfuscator CLI was not found: $(ObfuscatorCliPath)&amp;quot; /&amp;gt;
  &amp;lt;Error Condition=&amp;quot;!Exists('$(ObfuscatorConfigPath)')&amp;quot;
         Text=&amp;quot;Obfuscator config was not found: $(ObfuscatorConfigPath)&amp;quot; /&amp;gt;

  &amp;lt;RemoveDir Directories=&amp;quot;$(ObfuscatedOutputDir)&amp;quot; /&amp;gt;
  &amp;lt;MakeDir Directories=&amp;quot;$(ObfuscatedOutputDir)&amp;quot; /&amp;gt;
  &amp;lt;ItemGroup&amp;gt;
    &amp;lt;ObfuscatorCompanionFile Include=&amp;quot;$(TargetDir)**\*&amp;quot;
                                                                                                   Exclude=&amp;quot;$(ObfuscatedOutputDir)**\*;$(TargetPath)&amp;quot; /&amp;gt;
  &amp;lt;/ItemGroup&amp;gt;
  &amp;lt;Copy SourceFiles=&amp;quot;@(ObfuscatorCompanionFile)&amp;quot;
        DestinationFiles=&amp;quot;@(ObfuscatorCompanionFile-&amp;gt;'$(ObfuscatedOutputDir)%(RecursiveDir)%(Filename)%(Extension)')&amp;quot;
        SkipUnchangedFiles=&amp;quot;true&amp;quot; /&amp;gt;

  &amp;lt;Message Importance=&amp;quot;high&amp;quot; Text=&amp;quot;Obfuscating $(TargetPath)&amp;quot; /&amp;gt;
  &amp;lt;Exec Command=&amp;quot;&amp;amp;quot;$(ObfuscatorCliPath)&amp;amp;quot; obf --config &amp;amp;quot;$(ObfuscatorConfigPath)&amp;amp;quot; --input &amp;amp;quot;$(TargetPath)&amp;amp;quot; --output &amp;amp;quot;$(ObfuscatedOutputPath)&amp;amp;quot;&amp;quot; /&amp;gt;
  &amp;lt;Error Condition=&amp;quot;!Exists('$(ObfuscatedOutputPath)')&amp;quot;
         Text=&amp;quot;Obfuscator did not create the expected output: $(ObfuscatedOutputPath)&amp;quot; /&amp;gt;
  &amp;lt;Message Importance=&amp;quot;high&amp;quot; Text=&amp;quot;Protected output: $(ObfuscatedOutputDir)&amp;quot; /&amp;gt;
&amp;lt;/Target&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这里使用的都是MSBuild内置属性，因此不需要硬编码项目名称或目标框架：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$(MSBuildProjectDirectory)&lt;/code&gt;是当前项目目录。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$(TargetPath)&lt;/code&gt;是本次构建产生的主程序集完整路径。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$(TargetFileName)&lt;/code&gt;会自动适配不同项目名称。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AfterTargets=&amp;quot;Build&amp;quot;&lt;/code&gt;确保只有编译成功后才执行CLI。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Exec&lt;/code&gt;检查CLI退出码，随后的&lt;code&gt;Error&lt;/code&gt;还会确认目标文件确实已经生成。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;复制主程序集以外的完整输出内容，再由CLI生成受保护的主程序集，是为了保留程序运行需要的配置和依赖文件。不要只把混淆后的DLL单独交付。排除原始主程序集也能确保后续文件检查不会把未经保护的副本误判为成功结果。&lt;/p&gt;
&lt;h2&gt;第三步：配置CLI路径&lt;/h2&gt;
&lt;p&gt;不建议把个人电脑上的绝对安装路径写入&lt;code&gt;.csproj&lt;/code&gt;。可以使用环境变量，让不同开发机和CI环境使用各自的CLI位置。&lt;/p&gt;
&lt;p&gt;在PowerShell中为当前终端设置：&lt;/p&gt;
&lt;pre class=&quot;prism-highlight prism-language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;$env:OBFUSCATOR_CLI_PATH = 'C:\Tools\CSharpObfuscator\CLI.exe'
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如需永久写入当前Windows用户的环境变量：&lt;/p&gt;
&lt;pre class=&quot;prism-highlight prism-language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;[Environment]::SetEnvironmentVariable(
    'OBFUSCATOR_CLI_PATH',
    'C:\Tools\CSharpObfuscator\CLI.exe',
    'User'
)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;永久设置后需要重新打开Visual Studio、Rider或终端。也可以在单次构建中直接传入路径：&lt;/p&gt;
&lt;pre class=&quot;prism-highlight prism-language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;dotnet build -c Release -p:ObfuscatorCliPath='C:\Tools\CSharpObfuscator\CLI.exe'
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;第四步：执行Release构建&lt;/h2&gt;
&lt;p&gt;在命令行中执行：&lt;/p&gt;
&lt;pre class=&quot;prism-highlight prism-language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;dotnet build -c Release
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在Visual Studio中将解决方案配置切换为&lt;code&gt;Release&lt;/code&gt;后执行“生成解决方案”，或者在Rider中构建Release配置，都会触发同一个MSBuild目标。Debug构建默认不会执行混淆。&lt;/p&gt;
&lt;p&gt;如需临时跳过混淆，可以显式关闭开关：&lt;/p&gt;
&lt;pre class=&quot;prism-highlight prism-language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;dotnet build -c Release -p:RunObfuscator=false
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;构建日志中出现下面两行，表示集成目标已执行：&lt;/p&gt;
&lt;pre class=&quot;prism-highlight prism-language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Obfuscating ...\bin\Release\net8.0\MyApp.dll
Protected output: ...\bin\Release\net8.0\protected\
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;在CI中使用&lt;/h2&gt;
&lt;p&gt;CI使用的仍然是相同命令，只需提前安装并注册CLI，再通过环境变量或MSBuild属性传入路径：&lt;/p&gt;
&lt;pre class=&quot;prism-highlight prism-language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;$env:OBFUSCATOR_CLI_PATH = &amp;quot;$env:OBFUSCATOR_HOME\CLI.exe&amp;quot;
dotnet restore
dotnet build -c Release --no-restore
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;建议把&lt;code&gt;protected&lt;/code&gt;目录作为发布制品，并在上传前增加一个文件存在性检查：&lt;/p&gt;
&lt;pre class=&quot;prism-highlight prism-language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;$protectedFile = '.\bin\Release\net8.0\protected\MyApp.dll'
if (-not (Test-Path $protectedFile)) {
    throw &amp;quot;未生成混淆后的程序集: $protectedFile&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;WPF、WinForms、&lt;a href=&quot;http://xn--ASP-8i4ek27bpw7b.NET&quot;&gt;类库和ASP.NET&lt;/a&gt; Core项目&lt;/h2&gt;
&lt;p&gt;这套写法不依赖控制台项目：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;WPF和WinForms项目的&lt;code&gt;$(TargetPath)&lt;/code&gt;会指向其主程序集。&lt;/li&gt;
&lt;li&gt;类库项目可以直接保护生成的DLL。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://ASP.NET&quot;&gt;ASP.NET&lt;/a&gt; Core项目应在完整发布目录中保留依赖、配置文件和静态资源，仅替换需要保护的业务程序集。&lt;/li&gt;
&lt;li&gt;一个解决方案包含多个项目时，只在真正需要保护的项目中加入目标，或者把公共目标提取到单独的&lt;code&gt;.targets&lt;/code&gt;文件后按条件导入。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;使用反射、依赖注入扫描、JSON序列化、XAML绑定、插件加载或P/Invoke时，重命名可能影响运行。应通过忽略列表保留外部依赖的名称，并对启动、登录、核心业务、配置加载和自动更新等流程进行回归测试。&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;dotnet publish&lt;/code&gt;和单文件发布注意事项&lt;/h2&gt;
&lt;p&gt;本文示例挂接在&lt;code&gt;Build&lt;/code&gt;目标之后，适合普通DLL、EXE构建。执行&lt;code&gt;dotnet publish&lt;/code&gt;时，发布过程还会在&lt;code&gt;publish&lt;/code&gt;目录重新整理文件，因此不要直接把未经检查的&lt;code&gt;publish&lt;/code&gt;目录当成混淆结果。&lt;/p&gt;
&lt;p&gt;常规发布可先执行&lt;code&gt;dotnet publish -c Release&lt;/code&gt;，再用CLI的&lt;code&gt;--input&lt;/code&gt;和&lt;code&gt;--output&lt;/code&gt;处理发布目录中的主程序集。启用&lt;code&gt;PublishSingleFile&lt;/code&gt;时，应将发布得到的单文件EXE作为CLI输入，而不是构建阶段的DLL。建议为单文件项目单独建立发布后目标，并验证解包、混淆、重构后的程序能够在干净环境启动。&lt;/p&gt;
&lt;h2&gt;完整示例代码&lt;/h2&gt;
&lt;p&gt;可以访问GitHub获取完整项目代码: &lt;a href=&quot;https://github.com/leapever/csharp-obfuscator-cli-integration&quot;&gt;https://github.com/leapever/csharp-obfuscator-cli-integration&lt;/a&gt;&lt;/p&gt;
</description><pubDate>Sun, 26 Jul 2026 18:56:01 +0800</pubDate></item><item><title>恒盾C#混淆加密大师1.5.0：EXE保护升级与.NET 10兼容</title><link>http://ylongsoft.com/post/hengdun-obfuscator-1-5-0-protection-upgrade.html</link><description>&lt;p&gt;恒盾C#混淆加密大师 1.5.0 版本现已发布。本次更新聚焦于保护效果、运行兼容性和处理效率，增强了原生化EXE保护能力，适配最新的 .NET 10，并对多项常用混淆功能进行了集中优化。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/07/1784961803611_43d5594f.png&quot; alt=&quot;C#混淆加密大师1.5.0截图&quot;/&gt;&lt;/p&gt;&lt;h2&gt;下载最新版本&lt;/h2&gt;&lt;p&gt;&lt;a href=&quot;https://csharpobf.leapever.com&quot;&gt;点击进入下载页面&lt;/a&gt;&lt;/p&gt;&lt;p&gt;对于需要保护 C# DLL、EXE、WPF、WinForms、控制台程序以及其他 .NET 应用的开发者来说，1.5.0 是一次以稳定性和实用性为重点的升级。无论是单独启用某项保护，还是组合使用多项混淆功能，新版本都能更好地适应复杂项目和实际发布环境。&lt;/p&gt;&lt;h2&gt;1.5.0 版本重点更新&lt;/h2&gt;&lt;h3&gt;1. 增强原生化EXE保护效果&lt;/h3&gt;&lt;p&gt;1.5.0 进一步增强了原生化EXE的保护效果，可以提高程序被分析和还原的难度，为需要重点保护的 Windows EXE 程序提供更强的安全保障。&lt;/p&gt;&lt;p&gt;这项更新适合用于商业桌面软件、企业内部工具、授权客户端以及包含重要业务逻辑的应用程序。通过升级后的原生化EXE功能，开发者可以在保持程序正常交付和运行的同时，进一步加强对核心代码和软件成果的保护。&lt;/p&gt;&lt;h3&gt;2. 更新混淆内核，适配最新.NET 10&lt;/h3&gt;&lt;p&gt;随着 .NET 平台持续更新，混淆工具也需要及时适配新的程序格式和开发环境。1.5.0 更新了混淆内核，现已适配最新的 .NET 10，为使用新版本 .NET 构建项目的开发者提供支持。&lt;/p&gt;&lt;p&gt;对于计划升级到 .NET 10，或已经开始使用 .NET 10 开发和发布程序的团队，新版本可以更顺畅地接入现有保护流程。同时，原有 .NET 项目仍可继续使用，便于开发者在不同版本的项目之间保持统一的混淆加密方案。&lt;/p&gt;&lt;h3&gt;3. 提升防篡改功能的兼容性&lt;/h3&gt;&lt;p&gt;防篡改功能可以帮助开发者降低程序文件被修改后继续运行的风险。1.5.0 提升了该功能的兼容性，使其在更多EXE程序以及多种保护功能组合使用时表现得更加稳定。&lt;/p&gt;&lt;p&gt;对于同时启用函数体隐藏、资源文件混淆或其他保护选项的项目，这项改进可以减少功能之间相互影响的情况，让开发者更方便地根据项目需求组合不同的保护能力。&lt;/p&gt;&lt;h3&gt;4. 提升资源文件混淆的兼容性&lt;/h3&gt;&lt;p&gt;.NET 程序经常会包含界面资源、文本资源、配置资源以及其他随程序集发布的内容。1.5.0 提升了资源文件混淆的兼容性，使资源保护功能能够适应更多类型的项目。&lt;/p&gt;&lt;p&gt;这项更新对于资源使用较多的 WPF、WinForms 和桌面应用尤其有价值。开发者可以继续保护程序中的资源内容，同时降低混淆后出现资源读取异常或程序运行异常的概率。&lt;/p&gt;&lt;h3&gt;5. 提升隐藏函数体的兼容性&lt;/h3&gt;&lt;p&gt;隐藏函数体是保护关键业务逻辑的重要功能之一。1.5.0 对该功能进行了兼容性优化，使其能够更稳定地应用于包含复杂调用关系和多种程序结构的项目。&lt;/p&gt;&lt;p&gt;如果程序中包含授权验证、业务规则、数据处理或其他不希望被直接查看的关键逻辑，可以使用隐藏函数体功能提高分析难度。新版本让这项保护能力在实际项目中更易于使用，也更适合与其他混淆选项配合启用。&lt;/p&gt;&lt;h3&gt;6. 提升隐藏字符串和数字的兼容性&lt;/h3&gt;&lt;p&gt;程序中的字符串和数字常常包含接口地址、业务参数、授权信息、状态标识或其他敏感内容。1.5.0 提升了隐藏字符串和数字功能的兼容性，使其可以覆盖更多使用场景，并改善与其他保护功能同时启用时的稳定性。&lt;/p&gt;&lt;p&gt;对于敏感常量较多，或希望减少程序静态信息暴露的项目，这项更新可以提供更完整的保护体验。开发者无需改变原有业务代码，即可继续通过统一配置保护相关内容。&lt;/p&gt;&lt;h3&gt;7. 优化核心混淆速度&lt;/h3&gt;&lt;p&gt;除了保护效果和兼容性提升之外，1.5.0 还优化了核心混淆速度。对于程序集较多、项目体积较大或需要频繁执行混淆的开发者，新版本可以缩短等待时间，提高日常构建和发布效率。&lt;/p&gt;&lt;p&gt;这项优化也更适合自动化构建、持续集成和批量处理场景。团队可以将混淆步骤继续保留在现有发布流程中，同时获得更流畅的处理体验。&lt;/p&gt;&lt;h2&gt;多项保护功能配合使用更加稳定&lt;/h2&gt;&lt;p&gt;真实项目通常不会只使用单一保护功能，而是会根据程序特点组合启用原生化EXE、防篡改、资源文件混淆、隐藏函数体以及隐藏字符串和数字等功能。&lt;/p&gt;&lt;p&gt;1.5.0 对这些常用能力进行了整体兼容性提升，重点改善多项功能同时启用时的稳定性。对于结构复杂、资源较多或保护要求较高的项目，新版本能够减少反复调整配置的成本，让混淆加密流程更容易融入日常开发和发布工作。&lt;/p&gt;&lt;h2&gt;这次更新适合哪些用户&lt;/h2&gt;&lt;p&gt;如果你有以下需求，建议升级到 1.5.0：&lt;/p&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;需要增强 Windows EXE 程序的原生化保护效果&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;正在使用或准备升级到 .NET 10&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;需要对 C# DLL 或 EXE 进行混淆加密&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;需要组合使用防篡改、资源文件混淆和函数体隐藏等功能&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;需要保护程序中的字符串、数字和关键业务逻辑&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;需要提升大型项目或自动化发布流程中的混淆处理效率&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;总结&lt;/h2&gt;&lt;p&gt;恒盾C#混淆加密大师 1.5.0 是一次围绕保护强度、平台适配、功能兼容性和处理效率展开的重要更新。新版本不仅增强了原生化EXE保护效果并适配最新的 .NET 10，还集中提升了防篡改、资源文件混淆、隐藏函数体、隐藏字符串和数字等核心功能的稳定性。&lt;/p&gt;&lt;p&gt;对于需要在真实项目中长期使用 C# 混淆、DLL加密和EXE保护的开发者来说，1.5.0 能够提供更完整、更稳定且更高效的代码保护体验。&lt;/p&gt;</description><pubDate>Sat, 25 Jul 2026 14:43:23 +0800</pubDate></item><item><title>EXE加密原生内核使用指南：无需系统环境即可运行</title><link>http://ylongsoft.com/post/exe-encryptor-native-kernel-guide.html</link><description>&lt;h1&gt;EXE加密原生内核使用指南：无需系统环境即可运行&lt;/h1&gt;&lt;p&gt;使用EXE一机一码打包加密大师时，软件默认使用普通内核进行打包，可以满足绝大多数电脑的运行需求。但少数电脑由于系统版本较旧、经过精简、处于内网隔离环境，或安装了较为严格的安全防护软件，打包后的程序可能出现双击没有反应、启动后立即关闭或运行异常的情况。遇到这类问题时，可以尝试切换为&lt;strong&gt;原生内核&lt;/strong&gt;重新打包。如果你不了解EXE一机一码打包加密大师, 可以访问官网查看具体介绍和说明.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://exeenc.leapever.com/&quot;&gt;点击进入EXE一机一码打包加密大师官网&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;什么是原生内核&lt;/h2&gt;&lt;p&gt;在&lt;strong&gt;快速打包&lt;/strong&gt;页面的设置区域中，“加密壳”一项提供两种内核可选：&lt;strong&gt;普通内核&lt;/strong&gt;和&lt;strong&gt;原生内核&lt;/strong&gt;。普通内核是默认选项，需要一定的系统环境支持，适合绝大多数Windows电脑；原生内核则不依赖额外的系统环境，程序可以直接运行，通用性更好，尤其适合运行环境比较特殊的电脑。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/07/1783858465053_0af29b25.png&quot; alt=&quot;原生内核&quot;/&gt;&lt;/p&gt;&lt;p&gt;两种内核只是加密壳的运行载体不同，不会影响你选择的一机一码、静态密码或网络验证等授权方式，也不会改变激活码的计算方式。切换内核后原有的授权设置可以继续沿用，如果已经生成过激活码，建议切换内核后重新打包并在目标电脑上测试。&lt;/p&gt;&lt;h2&gt;适用场景&lt;/h2&gt;&lt;p&gt;以下情况可以优先考虑切换到原生内核重新打包：&lt;/p&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;打包后的程序在部分电脑上双击没有反应、启动后一闪而过，或者提示缺少组件。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;目标电脑系统版本较旧（例如仍在使用Windows 7）、是精简版系统镜像，或者是刚安装不久、组件不全的系统。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;软件需要部署在完全离线、无法连接网络更新系统组件的电脑上。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;目标电脑处于企业内网、政府机关等环境，安装策略较为严格，无法自由安装额外的系统组件。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;已经尝试过兼容模式，问题依然存在，希望进一步提高打包程序的通用性。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;img src=&quot;http://ylongsoft.com/zb_users/upload/2026/07/1783858465698_7245898c.png&quot; alt=&quot;打包问题&quot;/&gt;&lt;/p&gt;&lt;p&gt;如果打包后的程序在普通内核下已经可以正常运行，无需切换，保持默认设置即可。&lt;/p&gt;&lt;h2&gt;使用方法&lt;/h2&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;打开EXE一机一码打包加密大师，进入&lt;strong&gt;快速打包&lt;/strong&gt;页面。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;按照正常流程选择需要打包的EXE文件，设置软件名称、加密模式等选项。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;在设置区域的“加密壳”一项中，选择&lt;strong&gt;原生内核&lt;/strong&gt;（默认使用的是普通内核）。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;点击“打包”按钮生成加密程序。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;将生成的程序复制到目标电脑上测试运行效果，确认可以正常启动和使用。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;使用建议&lt;/h2&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;建议先使用默认的普通内核打包测试，只有在目标电脑上出现无法启动、运行异常等问题时，再切换为原生内核重新打包。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;原生内核和兼容模式可以配合使用：如果单独切换原生内核后问题仍未解决，可以同时勾选兼容模式一起测试。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;每次调整内核或其他打包设置后，建议在实际使用的电脑上重新测试一遍，确认无误后再正式对外分发。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;</description><pubDate>Sun, 12 Jul 2026 20:14:25 +0800</pubDate></item></channel></rss>