<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>小林技术之路</title>
        <link>https://blog.lintech1024.com/</link>
        <description>且看一名普通程序员, 如何修炼技术, 成为技术大佬</description>
        <lastBuildDate>Wed, 11 Feb 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>zh-CN</language>
        <item>
            <title><![CDATA[Playwright 使用教程]]></title>
            <link>https://blog.lintech1024.com/playwright-tutorial</link>
            <guid>https://blog.lintech1024.com/playwright-tutorial</guid>
            <pubDate>Wed, 11 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Playwright是微软开源的浏览器自动化工具, 支持多浏览器引擎, 提供Python、JavaScript等多语言API. 它通过真实浏览器渲染页面获取数据, 操作接近真实用户, 在数据采集方面可以有效规避反爬机制. 下面我将介绍如何使用这一工具.]]></description>
            <content:encoded><![CDATA[<p>Playwright是微软开源的浏览器自动化工具, 支持多浏览器引擎, 提供Python、JavaScript等多语言API. 它通过真实浏览器渲染页面获取数据, 操作接近真实用户, 在数据采集方面可以有效规避反爬机制. 下面我将介绍如何使用这一工具.</p>
<ul>
<li>官方文档: <a href="https://playwright.dev/" target="_blank" rel="noopener noreferrer">https://playwright.dev</a></li>
<li>官方仓库: <a href="https://github.com/microsoft/playwright" target="_blank" rel="noopener noreferrer">https://github.com/microsoft/playwright</a> 80K+ 个 star</li>
</ul>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="what-is-playwright">什么是 Playwright?<a href="https://blog.lintech1024.com/playwright-tutorial#what-is-playwright" class="hash-link" aria-label="什么是 Playwright?的直接链接" title="什么是 Playwright?的直接链接">​</a></h2>
<ul>
<li>Web自动化测试工具, 也可以用来作为爬虫工具</li>
<li>通过真实浏览器渲染页面后获取数据, 对比通过 API 调用获取数据的方式速度会慢点, 但是简单粗暴, 接近真实用户浏览网页, 较难被反爬针对</li>
</ul>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="compare">工具对比<a href="https://blog.lintech1024.com/playwright-tutorial#compare" class="hash-link" aria-label="工具对比的直接链接" title="工具对比的直接链接">​</a></h2>
<ul>
<li>Selenium: 基于 Webdriver, 需要中间层转发, 性能较差. 驱动安装麻烦</li>
<li>Puppeteer: 谷歌出品, 基于Chrome DevTools Protocol(CDP), 性能高. 针对 Chrome 浏览器, 语言支持Javascript</li>
<li>Playwright: 微软出品, Puppeteer 增强版, 接口与 Puppeteer 几乎一样. 社区活跃文档质量高, 容易上手, 支持多浏览器, 支持多语言(Javascript,Python,Java,C#), 自动管理浏览器安装, 支持Docker部署.</li>
</ul>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="pre">前置准备<a href="https://blog.lintech1024.com/playwright-tutorial#pre" class="hash-link" aria-label="前置准备的直接链接" title="前置准备的直接链接">​</a></h2>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="test-and-lib">test和lib模式<a href="https://blog.lintech1024.com/playwright-tutorial#test-and-lib" class="hash-link" aria-label="test和lib模式的直接链接" title="test和lib模式的直接链接">​</a></h3>
<p>Playwright 支持两种代码模式, test 和lib 模式, test 模式对于测试方面会更加方便强大, 而我们用于爬虫更适合使用 lib 模式</p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="install">安装<a href="https://blog.lintech1024.com/playwright-tutorial#install" class="hash-link" aria-label="安装的直接链接" title="安装的直接链接">​</a></h3>
<h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="install-playwright">安装 Python 库<a href="https://blog.lintech1024.com/playwright-tutorial#install-playwright" class="hash-link" aria-label="安装 Python 库的直接链接" title="安装 Python 库的直接链接">​</a></h4>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">pip </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> playwright</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="install-browser">安装浏览器<a href="https://blog.lintech1024.com/playwright-tutorial#install-browser" class="hash-link" aria-label="安装浏览器的直接链接" title="安装浏览器的直接链接">​</a></h4>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">playwright </span><span class="token function" style="color:#d73a49">install</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="repl">交互模式(REPL)<a href="https://blog.lintech1024.com/playwright-tutorial#repl" class="hash-link" aria-label="交互模式(REPL)的直接链接" title="交互模式(REPL)的直接链接">​</a></h3>
<p>Python 是解释型语言, 它不需要把整个文件代码编译后才能执行, 可以写一行代码执行一行, 这在开发阶段提供了极大的便利</p>
<p>在命令行输入 python 进入交互界面, 然后就可以一行一行输入代码</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> playwright</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sync_api </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> sync_playwright</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">playwright </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> sync_playwright</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">start</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">browser </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> playwright</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">chromium</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">launch</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">page </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> browser</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">new_page</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">goto</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"https://playwright.dev/"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">screenshot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">path</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"example.png"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">browser</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">close</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">playwright</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">stop</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="sync-and-async">同步和异步<a href="https://blog.lintech1024.com/playwright-tutorial#sync-and-async" class="hash-link" aria-label="同步和异步的直接链接" title="同步和异步的直接链接">​</a></h3>
<ul>
<li>Python 异步编程<!-- -->
<ul>
<li>异步函数需要在 <code>def</code> 前加 <code>async</code></li>
<li>每次调用异步函数需要在前面加 <code>await</code></li>
<li><code>main</code> 函数需要放在 <code>asyncio.run()</code> 里执行</li>
</ul>
</li>
<li>可以开发阶段先在交互模式用同步代码边调试边写, 待验证没问题了改成异步代码</li>
</ul>
<p>上面的代码在异步模式代码如下</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> asyncio</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> playwright</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">async_api </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> async_playwright</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Playwright</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">playwright</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Playwright</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    chromium </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> playwright</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">chromium </span><span class="token comment" style="color:#999988;font-style:italic"># or "firefox" or "webkit".</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    browser </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> chromium</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">launch</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    page </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> browser</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">new_page</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">goto</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"http://example.com"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># other actions...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> browser</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">close</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> async_playwright</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> playwright</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">playwright</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">asyncio</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="with-and-start">with 和 start<a href="https://blog.lintech1024.com/playwright-tutorial#with-and-start" class="hash-link" aria-label="with 和 start的直接链接" title="with 和 start的直接链接">​</a></h3>
<p>Python 的 with 语句可以自动管理资源的回收, 建议代码使用 <code>with</code> 语句, 交互模式使用 <code>start()</code></p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># 用 with</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> sync_playwright</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> playwright</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">playwright</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 用 start</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">playwright </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> sync_playwright</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">start</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">playwright</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">playwright</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">stop</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># 需要主动调用资源回收</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="type-annotation">类型注解<a href="https://blog.lintech1024.com/playwright-tutorial#type-annotation" class="hash-link" aria-label="类型注解的直接链接" title="类型注解的直接链接">​</a></h3>
<p>Python 是动态类型语言, 对象的类型在运行时才确定, 在写代码阶段, 编辑器可能因为没有足够的类型信息而无法进行类型补全, 如果把类型注解补上, 编辑器就能知道对象什么类型, 从而进行智能补全</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">playwright</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Playwright</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="python-exception">Python 异常<a href="https://blog.lintech1024.com/playwright-tutorial#python-exception" class="hash-link" aria-label="Python 异常的直接链接" title="Python 异常的直接链接">​</a></h3>
<p>Playwright 有些操作会抛出异常, 在 Python 中异常要用 <code>try ... except</code> 捕获, 不然会程序终止退出</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">try</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">except</span><span class="token plain"> Exception </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> e</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># except Exception as e: 捕获特定异常</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">finally</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># 可不需要</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="first-code">第一个代码<a href="https://blog.lintech1024.com/playwright-tutorial#first-code" class="hash-link" aria-label="第一个代码的直接链接" title="第一个代码的直接链接">​</a></h2>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> playwright</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sync_api </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> sync_playwright</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">playwright </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> sync_playwright</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">start</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">browser </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> playwright</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">chromium</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">launch</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">headless</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">False</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">context </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> browser</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">new_context</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">page </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> context</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">new_page</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">goto</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"https://example.com"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">locator</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'p'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">all_inner_texts</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">locator</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'a'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">click</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">context</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">close</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">browser</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">close</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">playwright</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">stop</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="class-playwright">Playwright<a href="https://blog.lintech1024.com/playwright-tutorial#class-playwright" class="hash-link" aria-label="Playwright的直接链接" title="Playwright的直接链接">​</a></h3>
<p>根对象, 使用 <code>with</code> 语句不用调用 <code>stop()</code> 方法</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># 异步使用 with</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> async_playwright</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> playwright</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 同步使用 start()</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">playwright </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> sync_playwright</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">start</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">playwright</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">stop</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="class-browsertype">BrowserType<a href="https://blog.lintech1024.com/playwright-tutorial#class-browsertype" class="hash-link" aria-label="BrowserType的直接链接" title="BrowserType的直接链接">​</a></h3>
<p>对应一个浏览器类型, 通过 Playwright 对象的属性获得</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">chromium </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> playwright</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">chromium</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="class-browser">Browser<a href="https://blog.lintech1024.com/playwright-tutorial#class-browser" class="hash-link" aria-label="Browser的直接链接" title="Browser的直接链接">​</a></h3>
<ul>
<li>对应一个浏览器实例</li>
<li>通过 <code>BrowserType</code> 对象的 <code>launch()</code> 方法创建 <code>Browser</code> 对象</li>
<li><code>launch()</code> 方法可以带上参数, 默认浏览器以无头模式启动, 在开发时可以设置参数为 <code>headless=False</code> 让浏览器可见, 生产爬取时需要去掉</li>
</ul>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">browser </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> chromium</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">launch</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">headless</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">False</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>记得调用 <code>close()</code> 方法回收资源</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> browser</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">close</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="class-browsercontext">BrowserContext<a href="https://blog.lintech1024.com/playwright-tutorial#class-browsercontext" class="hash-link" aria-label="BrowserContext的直接链接" title="BrowserContext的直接链接">​</a></h3>
<p>context 的设计提供了一种隔离浏览器会话的机制, 不同的 context 不会共享 Cookies 和缓存, <code>Browser</code> 对象提供 <code>new_context()</code> 方法创建 <code>BrowserContext</code> 对象</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">context </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> browser</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">new_context</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>同样需要记得调用 <code>close()</code> 方法回收资源</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> context</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">close</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>一个 <code>Browser</code> 对象可以创建多个 <code>BrowserContext</code> 对象</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">context1 </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> browser</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">new_context</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">context2 </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> browser</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">new_context</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="class-page">Page<a href="https://blog.lintech1024.com/playwright-tutorial#class-page" class="hash-link" aria-label="Page的直接链接" title="Page的直接链接">​</a></h3>
<ul>
<li>对应一个浏览器标签页</li>
<li><code>Page</code> 对象可以通过 <code>BrowserContext</code> 对象的 <code>new_page()</code> 方法创建, 也可以直接通过 <code>Browser</code> 对象的 <code>new_page()</code> 方法创建(自动在内部创建新的 <code>BrowserContext</code>)</li>
</ul>
<p>使用 <code>goto()</code> 方法请求URL</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">goto</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"https://example.com"</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>有时为了防止代码执行完浏览器直接关闭, 可以使用 <code>pause()</code> 方法暂停页面(需要设置参数<code>headless=False</code>)</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">pause</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="class-locator">Locator<a href="https://blog.lintech1024.com/playwright-tutorial#class-locator" class="hash-link" aria-label="Locator的直接链接" title="Locator的直接链接">​</a></h3>
<p><code>Locator</code> 对象代表 <code>Page</code> 被选中的节点元素, <code>Page</code> 对象提供多种方法获取 <code>Locator</code>, 如 <code>get_by_...()</code>, <code>query_selector()</code> 方法, 推荐统一用 <code>locator()</code> 方法</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">locator</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'#id'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 根据id选择</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">locator</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'.class'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># 根据class选择</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">locator</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'h1'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic"># 根据元素类型选择</span><br></span></code></pre></div></div>
<p>选中元素后可以像人工操作一样任意操作, <code>Locator</code> 提供了这些方法</p>
<h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="locator-text">获取文本<a href="https://blog.lintech1024.com/playwright-tutorial#locator-text" class="hash-link" aria-label="获取文本的直接链接" title="获�取文本的直接链接">​</a></h4>
<p>建议用 <code>inner_text()</code> 方法</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># 获取所有可见文本</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">texts </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_by_role</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"link"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">all_inner_texts</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 获取所有文本</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">texts </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_by_role</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"link"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">all_text_contents</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 获取所有文本(所有子元素拼在一起)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">text </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_by_role</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"link"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">inner_text</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="locator-fill">文本输入 fill()<a href="https://blog.lintech1024.com/playwright-tutorial#locator-fill" class="hash-link" aria-label="文本输入 fill()的直接链接" title="文本输入 fill()的直接链接">​</a></h4>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_by_role</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"textbox"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fill</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Peter"</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="locator-click">鼠标点击 click()<a href="https://blog.lintech1024.com/playwright-tutorial#locator-click" class="hash-link" aria-label="鼠标点击 click()的直接链接" title="鼠标点击 click()的直接链接">​</a></h4>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># 单击</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_by_role</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"button"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">click</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 双击</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_by_role</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"button"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">dblclick</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>通过 <code>Mouse</code> 对象</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">mouse</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">down</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">mouse</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">move</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">100</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">mouse</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">up</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">mouse</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">wheel</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># 滚动</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="locator-select-option">下拉框选择 select_option()<a href="https://blog.lintech1024.com/playwright-tutorial#locator-select-option" class="hash-link" aria-label="下拉框选择 select_option()的直接链接" title="下拉框选择 select_option()的直接链接">​</a></h4>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> element</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">select_option</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"blue"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 选中多个</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> element</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">select_option</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">value</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"red"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"green"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"blue"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="locator-press">键盘输入 press()<a href="https://blog.lintech1024.com/playwright-tutorial#locator-press" class="hash-link" aria-label="键盘输入 press()的直接链接" title="键盘输入 press()的直接链接">​</a></h4>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_by_text</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Submit"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">press</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Enter"</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>通过 <code>Keyboard</code> 对象</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># 输入单个字符</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keyboard</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">press</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"a"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 输入多个字符</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keyboard</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">type</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Hello"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 按下某个键</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keyboard</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">down</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Shift"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 放开某个键</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">keyboard</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">up</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Shift"</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="locator-file">文件上传 set_input_files()<a href="https://blog.lintech1024.com/playwright-tutorial#locator-file" class="hash-link" aria-label="文件上传 set_input_files()的直接链接" title="文件上传 set_input_files()的直接链接">​</a></h4>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_by_label</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Upload file"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">set_input_files</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'myfile.pdf'</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="locator-drag-to">拖拽元素 drag_to()<a href="https://blog.lintech1024.com/playwright-tutorial#locator-drag-to" class="hash-link" aria-label="拖拽元素 drag_to()的直接链接" title="拖拽元素 drag_to()的直接链接">​</a></h4>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">locator</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"#item-to-be-dragged"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">drag_to</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">locator</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"#item-to-drop-at"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="scroll">滚动<a href="https://blog.lintech1024.com/playwright-tutorial#scroll" class="hash-link" aria-label="滚动的直接链接" title="滚动的直接链接">​</a></h4>
<p>点击滚动按钮自动滚动</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_by_role</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"button"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">click</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>手动滚动</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_by_text</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Footer text"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">scroll_into_view_if_needed</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>用鼠标滚动</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_by_test_id</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"scrolling-container"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">hover</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">mouse</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">wheel</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>用 Javascript 脚本</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_by_test_id</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"scrolling-container"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">evaluate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"e =&gt; e.scrollTop += 100"</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="element-select">元素选择原则<a href="https://blog.lintech1024.com/playwright-tutorial#element-select" class="hash-link" aria-label="元素选择原则的直接链接" title="元素选择原则的直接链接">​</a></h2>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="优先使用-id-选择器">优先使用 ID 选择器<a href="https://blog.lintech1024.com/playwright-tutorial#%E4%BC%98%E5%85%88%E4%BD%BF%E7%94%A8-id-%E9%80%89%E6%8B%A9%E5%99%A8" class="hash-link" aria-label="优先使用 ID 选择器的直接链接" title="优先使用 ID 选择器的直接链接">​</a></h3>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2026-02-11-17-46-07-31b7c372d284dee7adc8400ed45576f8.png" width="1008" height="535" class="img_CujE"></p>
<p>这里应该用ID选择器, 语法 "#article-title"</p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="类选择器">类选择器<a href="https://blog.lintech1024.com/playwright-tutorial#%E7%B1%BB%E9%80%89%E6%8B%A9%E5%99%A8" class="hash-link" aria-label="类选择器的直接链接" title="类选择器的直接链接">​</a></h3>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2026-02-11-17-47-05-c9244ba736cd2a523a90779ef46db701.png" width="1160" height="562" class="img_CujE"></p>
<p>在没有ID选择器时可退而求其次, 使用类选择器, 语法 ".rich_media_content"</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>注意</div><div class="admonitionContent_BuS1"><p>应该使用用于定位元素的类选择器, 而不是用于样式的选择器, 后者非常不稳定, 容易变动</p></div></div>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2026-02-11-17-48-50-311b6535bef106a6e52d482e6bf4a8e0.png" width="1245" height="375" class="img_CujE"></p>
<p>".author-avatar" 是个不错的选择器, ".guYtcLAhfKCGOc2ZHrdq" 不太适合</p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="选择父级">选择父级<a href="https://blog.lintech1024.com/playwright-tutorial#%E9%80%89%E6%8B%A9%E7%88%B6%E7%BA%A7" class="hash-link" aria-label="选择父级的直接链接" title="选择父级的直接链接">​</a></h3>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2026-02-11-17-49-59-6b628f57fc8a83287926094c0cdf58bf.png" width="793" height="358" class="img_CujE"></p>
<p>如果没有ID选择器和类选择器, 可以先选中父级元素, 通过其获取子元素, 而不是直接选择 span 元素</p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="time-wait">元素等待<a href="https://blog.lintech1024.com/playwright-tutorial#time-wait" class="hash-link" aria-label="元素等待的直接链接" title="元素等待的直接链接">​</a></h2>
<p>因为网络请求和节点渲染需要时间, 经常会有元素未加载出来的问题, 这就需要进行等待, 下面是元素等待的要点</p>
<ul>
<li>任何时候不要用 <code>time.sleep()</code></li>
<li>操作类的方法不需要提前等待, Playwright 有自动等待和重试的能力. 比如下面一些方法(未完全列举). 这些方法有个 timeout 参数可设置超时时间, 但是建议一般情况不用设置, 用全局超时就行.<!-- -->
<ul>
<li><code>click()</code></li>
<li><code>fill()</code></li>
<li><code>press()</code></li>
</ul>
</li>
<li>获取元素文本不会自动等待和重试, 这就需要进行等待, 下面是方法对比<!-- -->
<ul>
<li><code>page.wait_for_timeout()</code> 和 <code>page.wait_for_selector()</code>,已弃用, 不推荐</li>
<li><code>expect(locator).to_be_visible()</code>, <code>expect()</code> 函数多用于测试, 这里不推荐</li>
<li><strong><code>locator.wait_for()</code>, 推荐用这个方法</strong></li>
</ul>
</li>
<li>*超时时间不建议在每个方法里设置, 可以通过下面方法设置<!-- -->
<ul>
<li><code>page.set_default_timeout()</code></li>
<li><code>browser_context.set_default_timeout()</code></li>
</ul>
</li>
<li>超时后会抛出 <code>TimeoutError</code> 异常, 记得要用 <code>try</code> 语句捕获</li>
</ul>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="event">事件<a href="https://blog.lintech1024.com/playwright-tutorial#event" class="hash-link" aria-label="事件的直接链接" title="事件的直接链接">​</a></h2>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">on</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"dialog"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">lambda</span><span class="token plain"> dialog</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> dialog</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">accept</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="call-js">调用 Javascript<a href="https://blog.lintech1024.com/playwright-tutorial#call-js" class="hash-link" aria-label="调用 Javascript的直接链接" title="调用 Javascript的直接链接">​</a></h2>
<ol>
<li>运行启动脚本</li>
</ol>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// preload.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token known-class-name class-name">Math</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method-variable function-variable method function property-access" style="color:#d73a49">random</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">42</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">add_init_script</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">path</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"./preload.js"</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<ol start="2">
<li>通过 <code>evaluate()</code> 方法运行 Javascript</li>
</ol>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">evaluate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"([x, y]) =&gt; Promise.resolve(x * y)"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">7</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">result</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># prints "56"</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="configuration">有用的配置<a href="https://blog.lintech1024.com/playwright-tutorial#configuration" class="hash-link" aria-label="有用的配置的直接链接" title="有用的配置的直接链接">​</a></h2>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="config-headless">Headless<a href="https://blog.lintech1024.com/playwright-tutorial#config-headless" class="hash-link" aria-label="Headless的直接链接" title="Headless的直接链接">​</a></h3>
<p>控制浏览器是否可见, 开发调试时可以设置为 <code>False</code>, 正式不是要设置为 <code>True</code></p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">browser </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> chromium</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">launch</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    headless</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">False</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="config-webdriver">防止 navigator.webdriver 检测<a href="https://blog.lintech1024.com/playwright-tutorial#config-webdriver" class="hash-link" aria-label="防止 navigator.webdriver 检测的直接链接" title="防止 navigator.webdriver 检测的直接链接">​</a></h3>
<p>参考文档: <a href="https://juejin.cn/post/7520687447237181503#heading-4" target="_blank" rel="noopener noreferrer">https://juejin.cn/post/7520687447237181503#heading-4</a>
当 Chrome 浏览器正在由自动化软件驱动时, 会将 <code>navigator.webdriver</code> 设置为 <code>true</code>, 在正常的浏览器中该值应该为 <code>false</code>.</p>
<p>绕过方法很简单, 启动 Chrome 时加一个 <code>--disable-blink-features=AutomationControlled</code> 开关即可</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">browser </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> chromium</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">launch</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    args</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"--disable-blink-features=AutomationControlled"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="config-viewport">防止 viewport 检测<a href="https://blog.lintech1024.com/playwright-tutorial#config-viewport" class="hash-link" aria-label="防止 viewport 检测的直接链接" title="防止 viewport 检测的直接链接">​</a></h3>
<p>参考文档: <a href="https://juejin.cn/post/7520687447237181503#heading-5" target="_blank" rel="noopener noreferrer">https://juejin.cn/post/7520687447237181503#heading-5</a>
当运行 Puppeteer 时, 它默认使用 800x600 的视口大小, 运行 Playwright 时, 默认使用 1280x720 的视口大小. 这个固定值非常明显, 很容易被检测到, 因为几乎没有哪个正常用户的浏览器会有这样的视口大小</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># 在 context 设置</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">context </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> browser</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">new_context</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    viewport</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"width"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1234</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"height"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">642</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># 只要不是 1280x720 就行</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 也可以页面级别设置</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">page </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> browser</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">new_page</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    viewport</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"width"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1234</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"height"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">642</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="config-user-agent">设置 user-agent<a href="https://blog.lintech1024.com/playwright-tutorial#config-user-agent" class="hash-link" aria-label="设置 user-agent的直接链接" title="设置 user-agent的直接链接">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">context </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> browser</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">new_context</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    user_agent</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="config-proxy">代理<a href="https://blog.lintech1024.com/playwright-tutorial#config-proxy" class="hash-link" aria-label="代理的直接链接" title="代理的直接链接">​</a></h3>
<p>可通过 <code>launch()</code> 和 <code>new_context()</code> 方法的参数设置</p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="config-cookies">Cookies<a href="https://blog.lintech1024.com/playwright-tutorial#config-cookies" class="hash-link" aria-label="Cookies的直接链接" title="Cookies的直接链接">​</a></h3>
<p>BrowserContext 有提供 <code>add_cookies()</code> 方法</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> browser_context</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">add_cookies</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">cookie_object1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cookie_object2</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="error">失败处理<a href="https://blog.lintech1024.com/playwright-tutorial#error" class="hash-link" aria-label="失败处理的直接链接" title="失败处理的直接链接">​</a></h2>
<ol>
<li>用 <code>try</code> 捕获异常, 将错误原因记录日志</li>
<li>截屏. 因为正式环境用的无头模式, 可能出错了都不知道怎么回事, 截屏可方便排查问题</li>
</ol>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">screenshot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">path</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"example.png"</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<ol start="3">
<li>Playwright 提供 tracing 机制, 可记录这个浏览器操作过程的追踪, 但是可能比较耗性能, 正常情况下不开启</li>
</ol>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> context</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">tracing</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">start</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">screenshots</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> snapshots</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">page </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> context</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">new_page</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">goto</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"https://playwright.dev"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> context</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">tracing</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">stop</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">path </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"trace.zip"</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="docker-deploy">Docker 部署<a href="https://blog.lintech1024.com/playwright-tutorial#docker-deploy" class="hash-link" aria-label="Docker 部署的直接链接" title="Docker 部署的直接链接">​</a></h2>
<p>Playwright 支持 Docker 部署, 官方有提供容器镜像, 已经预装好浏览器和相关依赖, 省去我们自己安装浏览器的繁琐.</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>注意</div><div class="admonitionContent_BuS1"><p>镜像未安装 Playwright Python 包, 需要我们自己安装依赖</p></div></div>
<div class="language-txt codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">requirements.txt</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-txt codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">playwright==1.58.0</span><br></span></code></pre></div></div>
<p>可以将整个程序打包成镜像, 下面是 Dockerfile 示例</p>
<div class="language-dockerfile codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-dockerfile codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token instruction keyword" style="color:#00009f">FROM</span><span class="token instruction"> mcr.microsoft.com/playwright/python:v1.58.0-noble</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token instruction keyword" style="color:#00009f">WORKDIR</span><span class="token instruction"> /app</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token instruction keyword" style="color:#00009f">COPY</span><span class="token instruction"> requirements.txt /app</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token instruction keyword" style="color:#00009f">RUN</span><span class="token instruction"> python -m pip install --upgrade pip &amp;&amp; pip install -r requirements.txt </span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token instruction keyword" style="color:#00009f">COPY</span><span class="token instruction"> . /app</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token instruction keyword" style="color:#00009f">CMD</span><span class="token instruction"> [</span><span class="token instruction string" style="color:#e3116c">"python"</span><span class="token instruction">, </span><span class="token instruction string" style="color:#e3116c">"main.py"</span><span class="token instruction">]</span><br></span></code></pre></div></div>]]></content:encoded>
            <author>lintech1024@hotmail.com (小林)</author>
            <category>基础教程</category>
            <category>网络爬虫</category>
            <category>Python</category>
        </item>
        <item>
            <title><![CDATA[推荐一个PDF转换神器 - Gotenberg]]></title>
            <link>https://blog.lintech1024.com/gotenberg</link>
            <guid>https://blog.lintech1024.com/gotenberg</guid>
            <pubDate>Wed, 21 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[发现一个PDF转换神器推荐给大家, 支持转换在线网页、Markdown、Word、Excel等等]]></description>
            <content:encoded><![CDATA[<p>发现一个PDF转换神器推荐给大家, 支持转换在线网页、Markdown、Word、Excel等等</p>
<p>Gotenberg 支持通过 Docker 部署为一个服务, 然后通过 curl 请求即可非常方便转换成PDF</p>
<ul>
<li>官方网站: <a href="https://gotenberg.dev/" target="_blank" rel="noopener noreferrer">gotenberg.dev</a></li>
<li>官方仓库: <a href="https://github.com/gotenberg/gotenberg" target="_blank" rel="noopener noreferrer">gotenberg/gotenberg</a></li>
</ul>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="install">安装<a href="https://blog.lintech1024.com/gotenberg#install" class="hash-link" aria-label="安装的直接链接" title="安装的直接链接">​</a></h2>
<p>通过 Docker 安装</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> run </span><span class="token parameter variable" style="color:#36acaa">--rm</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-p</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"3000:3000"</span><span class="token plain"> gotenberg/gotenberg:8</span><br></span></code></pre></div></div>
<p>或者用 Docker Compose</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">services</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># Your other services.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">gotenberg</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> gotenberg/gotenberg</span><span class="token punctuation" style="color:#393A34">:</span><span class="token number" style="color:#36acaa">8</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"3000:3000"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic"># Or more secure:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic"># - "127.0.0.1:3000:3000"</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="convert">转换<a href="https://blog.lintech1024.com/gotenberg#convert" class="hash-link" aria-label="转换的直接链接" title="转换的直接链接">​</a></h2>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="chromium">基于 Chromium<a href="https://blog.lintech1024.com/gotenberg#chromium" class="hash-link" aria-label="基于 Chromium的直接链接" title="基于 Chromium的直接链接">​</a></h3>
<p>可转换 URL</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">curl</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token parameter variable" style="color:#36acaa">--request</span><span class="token plain"> POST http://localhost:3000/forms/chromium/convert/url </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token parameter variable" style="color:#36acaa">--form</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">url</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">https://my.url </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token parameter variable" style="color:#36acaa">--form</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">landscape</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">true </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token parameter variable" style="color:#36acaa">--form</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">marginTop</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token parameter variable" style="color:#36acaa">--form</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">marginBottom</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token parameter variable" style="color:#36acaa">-o</span><span class="token plain"> my.pdf</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="libreoffice">基于 LibreOffice<a href="https://blog.lintech1024.com/gotenberg#libreoffice" class="hash-link" aria-label="基于 LibreOffice的直接链接" title="基于 LibreOffice的直接链接">​</a></h3>
<p>转换 Word 或 Excel</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">curl</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token parameter variable" style="color:#36acaa">--request</span><span class="token plain"> POST http://localhost:3000/forms/libreoffice/convert </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token parameter variable" style="color:#36acaa">--form</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">files</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">@/path/to/file.docx </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token parameter variable" style="color:#36acaa">--form</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">files</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">@/path/to/file.xlsx </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token parameter variable" style="color:#36acaa">--form</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">merge</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">true </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token parameter variable" style="color:#36acaa">--form</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">pdfa</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">PDF/A-1b </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token parameter variable" style="color:#36acaa">-o</span><span class="token plain"> my.pdf</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="pdfengines">基于 PDF Engines<a href="https://blog.lintech1024.com/gotenberg#pdfengines" class="hash-link" aria-label="基于 PDF Engines的直接链接" title="基于 PDF Engines的直接链接">​</a></h3>
<p>可合并 PDF</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">curl</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token parameter variable" style="color:#36acaa">--request</span><span class="token plain"> POST http://localhost:3000/forms/pdfengines/merge </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token parameter variable" style="color:#36acaa">--form</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">files</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">@/path/to/file1.pdf </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token parameter variable" style="color:#36acaa">--form</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">files</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">@/path/to/file2.pdf </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token parameter variable" style="color:#36acaa">--form</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">files</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">@/path/to/file3.pdf </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token parameter variable" style="color:#36acaa">--form</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">files</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">@/path/to/file4.pdf </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token parameter variable" style="color:#36acaa">-o</span><span class="token plain"> my.pdf</span><br></span></code></pre></div></div>]]></content:encoded>
            <author>lintech1024@hotmail.com (小林)</author>
            <category>优秀资源</category>
        </item>
        <item>
            <title><![CDATA[Vagrant 使用教程]]></title>
            <link>https://blog.lintech1024.com/vagrant</link>
            <guid>https://blog.lintech1024.com/vagrant</guid>
            <pubDate>Tue, 02 Dec 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[学习 Kubernetes 等分布式系统时, 硬件资源限制常成为实践瓶颈, 有个方法是用虚拟机模拟机器节点. 本文将介绍如何使用 Vagrant 工具管理虚拟机, 在单机环境中快速构建多节点虚拟机集群]]></description>
            <content:encoded><![CDATA[<p>学习 Kubernetes 等分布式系统时, 硬件资源限制常成为实践瓶颈, 有个方法是用虚拟机模拟机器节点. 本文将介绍如何使用 Vagrant 工具管理虚拟机, 在单机环境中快速构建多节点虚拟机集群</p>
<p><img decoding="async" loading="lazy" alt="cool-one.png" src="https://blog.lintech1024.com/assets/images/cool-one-be54b217aff699bd2c11b7f5369407a0.png" width="432" height="553" class="img_CujE"></p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="why">为什么不用 Docker 搭建集群?<a href="https://blog.lintech1024.com/vagrant#why" class="hash-link" aria-label="为什么不用 Docker 搭建集群?的直接链接" title="为什么不用 Docker 搭建集群?的直接链接">​</a></h2>
<p>Docker 容器间共享宿主机内核, 隔离仅限于命名空间, 而虚拟机拥有独立的系统内核, 有自己的网卡, 节点间完全隔离, 用多台虚拟机模拟更接近真实场景</p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="what">Vagrant 是什么?<a href="https://blog.lintech1024.com/vagrant#what" class="hash-link" aria-label="Vagrant 是什么?的直接链接" title="Vagrant 是什么?的直接链接">​</a></h2>
<p>Vagrant 是一个用于构建和管理虚拟机环境的命令行工具, 它通过 Vagrantfile 配置文件, 自动化定义虚拟机的计算资源(CPU、内存)、存储配置、网络拓扑以及操作系统等关键参数, 只要使用相同的 Vagrantfile, 就能创建出完全一致的开发环境, 避免手动操作导致的环境差异</p>
<p>Vagrant 本身不是虚拟机管理程序, 不直接创建或运行虚拟机. 它是一个虚拟化环境的抽象层和自动化工具, 依赖于底层虚拟化平台(如 VirtualBox、VMware、Hyper-V 或 Docker 等)来实际执行虚拟机的创建和管理操作</p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="install">安装<a href="https://blog.lintech1024.com/vagrant#install" class="hash-link" aria-label="安装的直接链接" title="安装的直接链接">​</a></h2>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="install-vagrant">安装 Vagrant<a href="https://blog.lintech1024.com/vagrant#install-vagrant" class="hash-link" aria-label="安装 Vagrant的直接链接" title="安装 Vagrant的直接链接">​</a></h3>
<p>Windows 系统可通过安装包安装 <a href="https://releases.hashicorp.com/vagrant/2.4.9/vagrant_2.4.9_windows_amd64.msi" target="_blank" rel="noopener noreferrer">vagrant_2.4.9_windows_amd64.msi</a>, 其他系统参考 <a href="https://developer.hashicorp.com/vagrant/install" target="_blank" rel="noopener noreferrer">Install Vagrant</a></p>
<p>通过下面命令校验是否成功安装</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">vagrant </span><span class="token parameter variable" style="color:#36acaa">--help</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="install-vm">安装虚拟化软件<a href="https://blog.lintech1024.com/vagrant#install-vm" class="hash-link" aria-label="安装虚拟化软件的直接链接" title="安装虚拟化软件的直接链接">​</a></h3>
<p>Vagrant 支持多种虚拟化软件, VirtualBox, Hyper-V, VMware 和 Docker, 个人建议使用 VirtualBox, 这也是 Vagrant 的默认虚拟化软件</p>
<p>可通过官网下载安装包 <a href="https://www.virtualbox.org/wiki/Downloads" target="_blank" rel="noopener noreferrer">Download VirtualBox</a></p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="start">开始<a href="https://blog.lintech1024.com/vagrant#start" class="hash-link" aria-label="开始的直接链接" title="开始的直接链接">​</a></h2>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="mkdir">创建项目目录并进入<a href="https://blog.lintech1024.com/vagrant#mkdir" class="hash-link" aria-label="创建项目目录并进入的��直接链接" title="创建项目目录并进入的直接链接">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">mkdir</span><span class="token plain"> learn-vagrant-get-started </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> </span><span class="token builtin class-name">cd</span><span class="token plain"> learn-vagrant-get-started</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="vagrant-init">初始化 Vagrant 环境<a href="https://blog.lintech1024.com/vagrant#vagrant-init" class="hash-link" aria-label="初始化 Vagrant 环境的直接链接" title="初始化 Vagrant 环境的直接链接">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">vagrant init hashicorp-education/ubuntu-24-04 --box-version </span><span class="token number" style="color:#36acaa">0.1</span><span class="token plain">.0</span><br></span></code></pre></div></div>
<p>Vagrant 会在本地创建一个 Vagrantfile 配置文件, 省略了注释后内容如下</p>
<div class="language-ruby codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ruby codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">Vagrant</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">configure</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal string" style="color:#e3116c">"2"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">do</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain">config</span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">box </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">"hashicorp-education/ubuntu-24-04"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">box_version </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">"0.1.0"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">end</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="vagrant-box">Box<a href="https://blog.lintech1024.com/vagrant#vagrant-box" class="hash-link" aria-label="Box的直接链接" title="Box的直接链接">​</a></h4>
<p>Vagrant 的 Box 类似于 Docker 的容器镜像, Vagrant 使用配置文件中指定的基础镜像构建虚拟机, 基础镜像通常从官方镜像仓库拉取</p>
<p>上面配置表示, 使用官方镜像仓库上的 <code>hashicorp-education/ubuntu-24-04</code> 镜像, 版本为 <code>0.1.0</code></p>
<h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="vagrant-registry">官方镜像仓库<a href="https://blog.lintech1024.com/vagrant#vagrant-registry" class="hash-link" aria-label="官方镜像仓库的直接链接" title="官方镜像仓库的直接链接">​</a></h4>
<p>Vagrant 也有类似 Docker Hub 的官方镜像仓库, 地址为 <a href="https://portal.cloud.hashicorp.com/vagrant/discover" target="_blank" rel="noopener noreferrer">HCP Vagrant Registry</a></p>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-12-02-14-46-56-f3ed31e3a14fe6c3a73e6b0ebf6a494b.png" width="2425" height="537" class="img_CujE"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="vagrant-up">启动虚拟机<a href="https://blog.lintech1024.com/vagrant#vagrant-up" class="hash-link" aria-label="启动虚拟机的直接链接" title="启动虚拟机的直接链接">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">vagrant up</span><br></span></code></pre></div></div>
<p>如果没有明显报错, 输出完下面日志后就代表虚拟机创建成功</p>
<details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary><p>vagrant up 日志</p></summary><div><div class="collapsibleContent_i85q"><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">Bringing machine </span><span class="token string" style="color:#e3116c">'default'</span><span class="token plain"> up with </span><span class="token string" style="color:#e3116c">'virtualbox'</span><span class="token plain"> provider</span><span class="token punctuation" style="color:#393A34">..</span><span class="token plain">.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">==</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> default: Importing base box </span><span class="token string" style="color:#e3116c">'hashicorp-education/ubuntu-24-04'</span><span class="token punctuation" style="color:#393A34">..</span><span class="token plain">.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">==</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> default: Matching MAC address </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> NAT networking</span><span class="token punctuation" style="color:#393A34">..</span><span class="token plain">.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">==</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> default: Checking </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> box </span><span class="token string" style="color:#e3116c">'hashicorp-education/ubuntu-24-04'</span><span class="token plain"> version </span><span class="token string" style="color:#e3116c">'0.1.0'</span><span class="token plain"> is up to date</span><span class="token punctuation" style="color:#393A34">..</span><span class="token plain">.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">==</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> default: Setting the name of the VM: learn-vagrant-get-started_default_1764658229098_38119</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">==</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> default: Fixed port collision </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">22</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2222</span><span class="token plain">. Now on port </span><span class="token number" style="color:#36acaa">2202</span><span class="token plain">.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">==</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> default: Clearing any previously </span><span class="token builtin class-name">set</span><span class="token plain"> network interfaces</span><span class="token punctuation" style="color:#393A34">..</span><span class="token plain">.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">==</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> default: Preparing network interfaces based on configuration</span><span class="token punctuation" style="color:#393A34">..</span><span class="token plain">.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    default: Adapter </span><span class="token number" style="color:#36acaa">1</span><span class="token plain">: nat</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">==</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> default: Forwarding ports</span><span class="token punctuation" style="color:#393A34">..</span><span class="token plain">.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    default: </span><span class="token number" style="color:#36acaa">22</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">guest</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2202</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">host</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">adapter </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">==</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> default: Booting VM</span><span class="token punctuation" style="color:#393A34">..</span><span class="token plain">.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">==</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> default: Waiting </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> machine to boot. This may take a few minutes</span><span class="token punctuation" style="color:#393A34">..</span><span class="token plain">.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    default: SSH address: </span><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:2202</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    default: SSH username: vagrant</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    default: SSH auth method: private key</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    default: </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    default: Vagrant insecure key detected. Vagrant will automatically replace</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    default: this with a newly generated keypair </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> better security.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    default: </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    default: Inserting generated public key within guest</span><span class="token punctuation" style="color:#393A34">..</span><span class="token plain">.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    default: Removing insecure key from the guest </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> it's present</span><span class="token punctuation" style="color:#393A34">..</span><span class="token plain">.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    default: Key inserted</span><span class="token operator" style="color:#393A34">!</span><span class="token plain"> Disconnecting and reconnecting using new SSH key</span><span class="token punctuation" style="color:#393A34">..</span><span class="token plain">.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">==</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> default: Machine booted and ready</span><span class="token operator" style="color:#393A34">!</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">==</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> default: Checking </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> guest additions </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> VM</span><span class="token punctuation" style="color:#393A34">..</span><span class="token plain">.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    default: The guest additions on this VM </span><span class="token keyword" style="color:#00009f">do</span><span class="token plain"> not match the installed version of</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    default: VirtualBox</span><span class="token operator" style="color:#393A34">!</span><span class="token plain"> In </span><span class="token function" style="color:#d73a49">most</span><span class="token plain"> cases this is fine, but </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> rare cases it can</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    default: prevent things such as shared folders from working properly. If you see</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    default: shared folder errors, please </span><span class="token function" style="color:#d73a49">make</span><span class="token plain"> sure the guest additions within the</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    default: virtual machine match the version of VirtualBox you have installed on</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    default: your </span><span class="token function" style="color:#d73a49">host</span><span class="token plain"> and reload your VM.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    default: </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    default: Guest Additions Version: </span><span class="token number" style="color:#36acaa">7.1</span><span class="token plain">.0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    default: VirtualBox Version: </span><span class="token number" style="color:#36acaa">7.2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">==</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> default: Mounting shared folders</span><span class="token punctuation" style="color:#393A34">..</span><span class="token plain">.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    default: D:/workspace/learn-vagrant-get-started </span><span class="token operator" style="color:#393A34">=</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> /vagrant</span><br></span></code></pre></div></div></div></div></details>
<p>Vagrant 做了什么?</p>
<ul>
<li>拉取基础镜像, 通常会从官方镜像仓库拉取</li>
<li>使用虚拟化软件创建虚拟机, 无特殊配置的话默认使用 VirtualBox</li>
<li>网络配置, 配置 NAT 网络使虚拟机可访问外部网络</li>
<li>开启 SSH 服务并将虚拟机的 <code>22</code> 端口转发到宿主机, 使宿主机可以通过 SSH 连上虚拟机</li>
<li>生成 SSH 密钥对并将公钥注入虚拟机</li>
<li>挂载共享文件夹, 将项目目录映射到虚拟机的 <code>/vagrant</code> 目录</li>
</ul>
<p>打开 VirtualBox 软件可以发现 Vagrant 成功创建了新的虚拟机</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-12-02-15-15-09-ebc20366a79eadcc5a69d82ef0487e4c.png" width="2542" height="1367" class="img_CujE"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="access-vm">访问虚拟机<a href="https://blog.lintech1024.com/vagrant#access-vm" class="hash-link" aria-label="访问虚拟机的直接链接" title="访问虚拟机的直接链接">​</a></h3>
<p>因为 Vagrant 已为虚拟机开启 SSH 服务并且将公钥注入虚拟机, 所以宿主机可通过 SSH 连接免密访问虚拟机, 通过下面命令即可连上虚拟机</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">vagrant </span><span class="token function" style="color:#d73a49">ssh</span><br></span></code></pre></div></div>
<p>在虚拟机内尝试运行命令</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">vagrant@vagrant-ubuntu:~$ lsb_release </span><span class="token parameter variable" style="color:#36acaa">-a</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">No LSB modules are available.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Distributor ID: Ubuntu</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Description:    Ubuntu </span><span class="token number" style="color:#36acaa">24.04</span><span class="token plain">.1 LTS</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Release:        </span><span class="token number" style="color:#36acaa">24.04</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Codename:       noble</span><br></span></code></pre></div></div>
<p>从虚拟机返回宿主机可运行下面命令</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token builtin class-name">logout</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="vagrant-suspend">休眠虚拟机<a href="https://blog.lintech1024.com/vagrant#vagrant-suspend" class="hash-link" aria-label="休眠虚拟机的直接链接" title="休眠虚拟机的直接链接">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">vagrant </span><span class="token function" style="color:#d73a49">suspend</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-12-02-16-57-25-e2b8dac64c822b705954fbca2578ff62.png" width="1087" height="815" class="img_CujE"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="vagrant-resume">唤醒虚拟机<a href="https://blog.lintech1024.com/vagrant#vagrant-resume" class="hash-link" aria-label="唤醒虚拟机的直接链接" title="唤醒虚拟机的直接链接">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">vagrant resume</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-12-02-16-59-45-910a34d5e61b9a52f034f76983ecb19e.png" width="1058" height="726" class="img_CujE"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="vagrant-halt">关闭虚拟机<a href="https://blog.lintech1024.com/vagrant#vagrant-halt" class="hash-link" aria-label="关闭虚拟机的直接链接" title="关闭虚拟机的直接链接">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">vagrant </span><span class="token function" style="color:#d73a49">halt</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-12-02-17-04-32-84d93b297d90bf4cbc8d58a0461433fa.png" width="1073" height="581" class="img_CujE"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="vagrant-destroy">销毁虚拟机<a href="https://blog.lintech1024.com/vagrant#vagrant-destroy" class="hash-link" aria-label="销毁虚拟机的直接链接" title="销毁虚拟机的直接链接">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">vagrant destroy</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>注意</div><div class="admonitionContent_BuS1"><p>销毁虚拟机后并不会删除拉取的镜像文件, 如果需要删除镜像需要执行下面命令</p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">vagrant box remove hashicorp-education/ubuntu-24-04</span><br></span></code></pre></div></div></div></div>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="network">网络<a href="https://blog.lintech1024.com/vagrant#network" class="hash-link" aria-label="网络的直接链接" title="网络的直接链接">​</a></h2>
<table><thead><tr><th style="text-align:center">网络配置</th><th style="text-align:center">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Mode&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</th><th style="text-align:center">VM→Host</th><th style="text-align:center">&nbsp;&nbsp;VM←Host&nbsp;&nbsp;</th><th style="text-align:center">VM1↔VM2</th><th style="text-align:center">VM→Net/LAN</th><th style="text-align:center">VM←Net/LAN</th></tr></thead><tbody><tr><td style="text-align:center">private_network</td><td style="text-align:center">Host-only</td><td style="text-align:center">✅</td><td style="text-align:center">✅</td><td style="text-align:center">✅</td><td style="text-align:center">❌</td><td style="text-align:center">❌</td></tr><tr><td style="text-align:center">private_network + virtualbox__intnet</td><td style="text-align:center">Internal</td><td style="text-align:center">❌</td><td style="text-align:center">❌</td><td style="text-align:center">✅</td><td style="text-align:center">❌</td><td style="text-align:center">❌</td></tr><tr><td style="text-align:center">public_network</td><td style="text-align:center">Bridged</td><td style="text-align:center">✅</td><td style="text-align:center">✅</td><td style="text-align:center">✅</td><td style="text-align:center">✅</td><td style="text-align:center">✅</td></tr><tr><td style="text-align:center">forwarded_port</td><td style="text-align:center">NAT</td><td style="text-align:center">✅</td><td style="text-align:center">Port forward</td><td style="text-align:center">❌</td><td style="text-align:center">✅</td><td style="text-align:center">Port forward</td></tr><tr><td style="text-align:center">❌</td><td style="text-align:center">NAT Network</td><td style="text-align:center">✅</td><td style="text-align:center">Port forward</td><td style="text-align:center">✅</td><td style="text-align:center">✅</td><td style="text-align:center">Port forward</td></tr></tbody></table>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="forwarded-port">Forwarded Port<a href="https://blog.lintech1024.com/vagrant#forwarded-port" class="hash-link" aria-label="Forwarded Port的直接链接" title="Forwarded Port的直接链接">​</a></h3>
<p>Vagrant 的 Forwarded Port 网络模式对应 VirtualBox 的 NAT 网络模式, 这种模式下</p>
<ul>
<li>虚拟机可以访问外部网络</li>
<li>外部网络无法访问虚拟机</li>
<li>虚拟机之间不互通</li>
</ul>
<p>在 VirtualBox 中 NAT 网络模式可选开放端口映射让外部网络访问虚拟机, 而在 Vagrant 中 Forwarded Port 网络模式的端口映射是必选项</p>
<p>Vagrant 启动虚拟机时默认会开启 Forwarded Port 网络模式, 并且将虚拟机的 <code>22</code> 端口映射到宿主机, 这样保证宿主机可以通过 <code>vagrant ssh</code> 命令连接到虚拟机</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-12-04-11-18-29-4c11c3e8bac83654a66570a4cb729179.png" width="1590" height="975" class="img_CujE"></p>
<p>在 VirtualBox 的管理器中可以看到 Vagrant 启动的虚拟机中默认有一张 NAT 网卡, 并且有条名称为 <code>ssh</code> 的端口转发规则, 将虚拟机的 <code>22</code> 端口映射到宿主机的 <code>2222</code> 端口</p>
<p>Forwarded Port 网络模式配置</p>
<div class="language-ruby codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ruby codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">network </span><span class="token string-literal string" style="color:#e3116c">"forwarded_port"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">guest</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">80</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">host</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8080</span><br></span></code></pre></div></div>
<p>如果希望除了宿主机的其他外网无法访问虚拟机</p>
<div class="language-ruby codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ruby codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">network </span><span class="token string-literal string" style="color:#e3116c">"forwarded_port"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">guest</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">81</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">host</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8081</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">host_ip</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">"127.0.0.1"</span><br></span></code></pre></div></div>
<p>可以发现在同块 NAT 网卡上增加了两条端口转发规则</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-12-04-11-28-50-dec8c6b874f85b2a2a90884094d38a83.png" width="1594" height="965" class="img_CujE"></p>
<p>Forwarded Port 网络模式会给虚拟机分配 <code>10.0.2.15</code> 地址, 并且可通过 <code>10.0.2.2</code> 地址直接访问宿主机</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-12-04-14-18-47-f4b9a135161a2bc41b3714a5f1829fd5.png" width="1567" height="512" class="img_CujE"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="private-network">Private Network<a href="https://blog.lintech1024.com/vagrant#private-network" class="hash-link" aria-label="Private Network的直接链接" title="Private Network的直接链接">​</a></h3>
<p>Vagrant 的 Private Network 网络模式对应 VirtualBox 的 Host-only 网络模式, 这种模式下</p>
<ul>
<li>同一宿主机上的虚拟机之间可互相访问</li>
<li>虚拟机与宿主机之间可相互访问</li>
<li>其他外部网络无法访问该私有网络</li>
</ul>
<p>Private Network 网络模式可配置静态 IP 或动态 IP</p>
<h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="private-network-static-ip">静态 IP<a href="https://blog.lintech1024.com/vagrant#private-network-static-ip" class="hash-link" aria-label="静态 IP的直接链接" title="静态 IP的直接链接">​</a></h4>
<div class="language-ruby codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ruby codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">network </span><span class="token string-literal string" style="color:#e3116c">"private_network"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">ip</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">"192.168.50.4"</span><br></span></code></pre></div></div>
<p>可以看到虚拟机多了一张 Host-Only 网络模式的网卡</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-12-04-13-48-46-559291c3c7f2c910de2194dbe05a3716.png" width="1600" height="976" class="img_CujE"></p>
<p>在宿主机可以看到 VirtualBox 创建的虚拟网卡, IP 地址跟虚拟机设置的 IP 在同个网段</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-12-04-13-47-51-509051a2f4df40f3983d5fe51e20e355.png" width="1525" height="698" class="img_CujE"></p>
<p>在虚拟机查看 IP 可以发现正是我们说配置的静态 IP</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-12-04-14-15-21-423fe182947f871d493bf5dc03dccb35.png" width="1628" height="723" class="img_CujE"></p>
<h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="private-network-hdcp">动态 IP<a href="https://blog.lintech1024.com/vagrant#private-network-hdcp" class="hash-link" aria-label="动态 IP的直接链接" title="动态 IP的直接链接">​</a></h4>
<div class="language-ruby codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ruby codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">network </span><span class="token string-literal string" style="color:#e3116c">"private_network"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">"dhcp"</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="private-network-internal">Internal 网络<a href="https://blog.lintech1024.com/vagrant#private-network-internal" class="hash-link" aria-label="Internal 网络的直接链接" title="Internal 网络的直接链接">​</a></h4>
<p>当配置 virtualbox__intnet 参数时, Private Network 则对应 VirtualBox 的 Internal 网络模式, 这种模式下</p>
<ul>
<li>虚拟机之间可相互通信</li>
<li>虚拟机和宿主机以及外部网络不互通</li>
</ul>
<div class="language-ruby codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ruby codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">network </span><span class="token string-literal string" style="color:#e3116c">"private_network"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">ip</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">"192.168.50.4"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token symbol" style="color:#36acaa">virtualbox__intnet</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">"mynetwork"</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>注意</div><div class="admonitionContent_BuS1"><p>virtualbox__intnet 有两条下划线, 要求网络名称相同的虚拟机才可相互通信</p></div></div>
<p>可以看到虚拟机的网卡变成了 Internal 网络模式, 并且名称正是我们所设置的 <code>mynetwork</code></p>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-12-04-14-06-29-48701ea0216f3ce3ca1a79b7252c3649.png" width="1600" height="976" class="img_CujE"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="public-network">Public Network<a href="https://blog.lintech1024.com/vagrant#public-network" class="hash-link" aria-label="Public Network的直接链接" title="Public Network的直接链接">​</a></h3>
<p>Vagrant 的 Public Network 网络模式对应 VirtualBox 的桥接(Bridged)网络模式, 这种模式下</p>
<ul>
<li>虚拟机相当于局域网的一台真实机器, 可跟局域网内的任意机器互通</li>
<li>会消耗局域网一个 IP 地址</li>
</ul>
<h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="public-network-static-ip">静态 IP<a href="https://blog.lintech1024.com/vagrant#public-network-static-ip" class="hash-link" aria-label="静态 IP的直接链接" title="静态 IP的直接链接">​</a></h4>
<div class="language-ruby codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ruby codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">network </span><span class="token string-literal string" style="color:#e3116c">"public_network"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">ip</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">"192.168.0.17"</span><br></span></code></pre></div></div>
<p>Public Network 网络模式会直接使用宿主机的网卡, 所以在启动虚拟机后需要你选择一张网卡</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-12-04-14-38-54-e46f41995f111fdba2aeb0853c1be033.png" width="984" height="825" class="img_CujE"></p>
<p>可以看到虚拟机使用了桥接模式, 名称正是我们选择的网卡名称</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-12-04-15-25-17-971c8c5a47d069e1586a87165b4edf4d.png" width="1600" height="976" class="img_CujE"></p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>注意</div><div class="admonitionContent_BuS1"><p>设置静态 IP 时要处于局域网的网段, 否则外部网络无法访问虚拟机</p></div></div>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-12-04-14-32-32-b38a143832f4cf66c9e33937f26e906a.png" width="1362" height="1339" class="img_CujE"></p>
<h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="public-network-dhcp">动态 IP<a href="https://blog.lintech1024.com/vagrant#public-network-dhcp" class="hash-link" aria-label="动态 IP的直接链接" title="动态 IP的直接链接">​</a></h4>
<p>如下配置即为设置动态 IP, 局域网会自动分配一个 IP 地址给我们</p>
<div class="language-ruby codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ruby codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">network </span><span class="token string-literal string" style="color:#e3116c">"public_network"</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="cpu-and-memory">CPU和内存<a href="https://blog.lintech1024.com/vagrant#cpu-and-memory" class="hash-link" aria-label="CPU和内存的直接链接" title="CPU和内存的直接链接">​</a></h2>
<p>我们在使用虚拟机时经常对 CPU 和内存有要求, 可通过下面配置设置</p>
<div class="language-ruby codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ruby codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">provider </span><span class="token string-literal string" style="color:#e3116c">"virtualbox"</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">do</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain">v</span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  v</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">memory </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1024</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  v</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cpus </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">end</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>提示</div><div class="admonitionContent_BuS1"><p>内存的单位是 <em>MB</em>, 不进行设置虚拟机默认为1个CPU和2048MB内存</p></div></div>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="defining-multiple-machines">多节点<a href="https://blog.lintech1024.com/vagrant#defining-multiple-machines" class="hash-link" aria-label="多节点的直接链接" title="多节点的直接链接">​</a></h2>
<p>多台机器通过 <code>config.vm.define</code> 方法在配置文件中定义</p>
<div class="language-ruby codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ruby codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># 全局公共配置</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">box </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">"hashicorp-education/ubuntu-24-04"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">box_version </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">"0.1.0"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">define </span><span class="token string-literal string" style="color:#e3116c">"web"</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">define </span><span class="token string-literal string" style="color:#e3116c">"db"</span><br></span></code></pre></div></div>
<p>多节点命令使用变化</p>
<table><thead><tr><th style="text-align:center">命令</th><th>说明</th></tr></thead><tbody><tr><td style="text-align:center"><code>vagrant up</code></td><td>默认启动所有定义的机器</td></tr><tr><td style="text-align:center"><code>vagrant up web</code></td><td>仅启动名为"web"的机器</td></tr><tr><td style="text-align:center"><code>vagrant ssh db</code></td><td>连接到名为"db"的机器(必须指定机器名)</td></tr></tbody></table>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">$ vagrant </span><span class="token function" style="color:#d73a49">ssh</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">This </span><span class="token builtin class-name">command</span><span class="token plain"> requires a specific VM name to target </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> a multi-VM environment.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ vagrant </span><span class="token function" style="color:#d73a49">ssh</span><span class="token plain"> db</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Welcome to Ubuntu </span><span class="token number" style="color:#36acaa">24.04</span><span class="token plain">.1 LTS </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">GNU/Linux </span><span class="token number" style="color:#36acaa">6.8</span><span class="token plain">.0-51-generic x86_64</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>可通过 <code>primary</code> 参数设置主机器, <code>vagant ssh</code> 可省略机器名, 默认连到主机器上</p>
<div class="language-ruby codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ruby codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">define </span><span class="token string-literal string" style="color:#e3116c">"web"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">primary</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">define </span><span class="token string-literal string" style="color:#e3116c">"db"</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="1-master-2-node">一主双从节点<a href="https://blog.lintech1024.com/vagrant#1-master-2-node" class="hash-link" aria-label="一主双从节点的直接链接" title="一主双从节点的直接链接">​</a></h3>
<p>下面我们将配置一个一主双从节点, 每个节点设置各自的主机名、CPU和内存、私有静态IP</p>
<div class="language-ruby codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ruby codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">Vagrant</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">configure</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal string" style="color:#e3116c">"2"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">do</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain">config</span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">box </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">"hashicorp-education/ubuntu-24-04"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">box_version </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">"0.1.0"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">define </span><span class="token string-literal string" style="color:#e3116c">"master"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">primary</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">do</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain">master</span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    master</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">hostname </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">"master"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    master</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">network </span><span class="token string-literal string" style="color:#e3116c">"private_network"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">ip</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">"192.168.50.10"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    master</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">provider </span><span class="token string-literal string" style="color:#e3116c">"virtualbox"</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">do</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain">vb</span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      vb</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">name </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">"vagrant-master"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      vb</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">memory </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">"2048"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      vb</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cpus </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">end</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">end</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">define </span><span class="token string-literal string" style="color:#e3116c">"worker1"</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">do</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain">worker1</span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    worker1</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">hostname </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">"worker1"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    worker1</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">network </span><span class="token string-literal string" style="color:#e3116c">"private_network"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">ip</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">"192.168.50.11"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    worker1</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">provider </span><span class="token string-literal string" style="color:#e3116c">"virtualbox"</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">do</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain">vb</span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      vb</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">name </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">"vagrant-worker1"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      vb</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">memory </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">"1024"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      vb</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cpus </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">end</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">end</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">define </span><span class="token string-literal string" style="color:#e3116c">"worker2"</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">do</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain">worker2</span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    worker2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">hostname </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">"worker2"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    worker2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">network </span><span class="token string-literal string" style="color:#e3116c">"private_network"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">ip</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">"192.168.50.12"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    worker2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">provider </span><span class="token string-literal string" style="color:#e3116c">"virtualbox"</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">do</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain">vb</span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      vb</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">name </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">"vagrant-worker2"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      vb</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">memory </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">"1024"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      vb</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cpus </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">end</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">end</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">end</span><br></span></code></pre></div></div>
<p>成功启动了三台虚拟机</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-12-04-17-39-51-5e59fb126d4474b93a2c02dcad4817e0.png" width="1600" height="976" class="img_CujE"></p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="var-and-each">使用变量和each简化配置<a href="https://blog.lintech1024.com/vagrant#var-and-each" class="hash-link" aria-label="使用变量和each简化配置的直接链接" title="使用变量和each简化配置的直接链接">​</a></h2>
<p>将每个节点的专有配置提取到 <code>NODES</code> 变量中, 然后用 <code>each</code> 语法定义每台机器</p>
<div class="language-ruby codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ruby codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token constant" style="color:#36acaa">NODES</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string-literal string" style="color:#e3116c">'master'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token symbol" style="color:#36acaa">ip</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">'192.168.50.10'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token symbol" style="color:#36acaa">cpus</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token symbol" style="color:#36acaa">memory</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2048</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string-literal string" style="color:#e3116c">'worker1'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token symbol" style="color:#36acaa">ip</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">'192.168.50.11'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token symbol" style="color:#36acaa">cpus</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token symbol" style="color:#36acaa">memory</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1024</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string-literal string" style="color:#e3116c">'worker2'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token symbol" style="color:#36acaa">ip</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">'192.168.50.12'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token symbol" style="color:#36acaa">cpus</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token symbol" style="color:#36acaa">memory</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1024</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Vagrant</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">configure</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal string" style="color:#e3116c">"2"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">do</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain">config</span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">box </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">"hashicorp-education/ubuntu-24-04"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">box_version </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">"0.1.0"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token constant" style="color:#36acaa">NODES</span><span class="token punctuation" style="color:#393A34">.</span><span class="token keyword" style="color:#00009f">each</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">do</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain">name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cfg</span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    config</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">define name </span><span class="token keyword" style="color:#00009f">do</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain">node</span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      node</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">hostname </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      node</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">network </span><span class="token string-literal string" style="color:#e3116c">"private_network"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token symbol" style="color:#36acaa">ip</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> cfg</span><span class="token punctuation" style="color:#393A34">[</span><span class="token symbol" style="color:#36acaa">:ip</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      node</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">provider </span><span class="token string-literal string" style="color:#e3116c">"virtualbox"</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">do</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain">vb</span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        vb</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">name </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string-literal string" style="color:#e3116c">"vagrant-</span><span class="token string-literal interpolation delimiter punctuation" style="color:#393A34">#{</span><span class="token string-literal interpolation content">name</span><span class="token string-literal interpolation delimiter punctuation" style="color:#393A34">}</span><span class="token string-literal string" style="color:#e3116c">"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        vb</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cpus </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cfg</span><span class="token punctuation" style="color:#393A34">[</span><span class="token symbol" style="color:#36acaa">:cpus</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        vb</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">memory </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cfg</span><span class="token punctuation" style="color:#393A34">[</span><span class="token symbol" style="color:#36acaa">:memory</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">end</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">end</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">end</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">end</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="ref">参考<a href="https://blog.lintech1024.com/vagrant#ref" class="hash-link" aria-label="参考的直接链接" title="参考的直接链接">​</a></h2>
<ul>
<li><a href="https://developer.hashicorp.com/vagrant/tutorials/get-started/development-environment" target="_blank" rel="noopener noreferrer">Vagrant Quick Start</a></li>
<li><a href="https://developer.hashicorp.com/vagrant/install" target="_blank" rel="noopener noreferrer">Install Vagrant</a></li>
<li><a href="https://www.virtualbox.org/wiki/Downloads" target="_blank" rel="noopener noreferrer">Download VirtualBox</a></li>
<li><a href="https://portal.cloud.hashicorp.com/vagrant/discover" target="_blank" rel="noopener noreferrer">HCP Vagrant Registry</a></li>
<li><a href="https://developer.hashicorp.com/vagrant/docs/networking" target="_blank" rel="noopener noreferrer">Vagrant Networking</a></li>
<li><a href="https://docs.oracle.com/en/virtualization/virtualbox/7.2/user/networkingdetails.html#networkingmodes" target="_blank" rel="noopener noreferrer">VirtualBox 7.2 - Virtual Networking</a></li>
</ul>]]></content:encoded>
            <author>lintech1024@hotmail.com (小林)</author>
            <category>基础教程</category>
            <category>虚拟机</category>
        </item>
        <item>
            <title><![CDATA[nginx 实战配置]]></title>
            <link>https://blog.lintech1024.com/nginx-conf</link>
            <guid>https://blog.lintech1024.com/nginx-conf</guid>
            <pubDate>Thu, 25 Sep 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[本文详解 nginx 核心配置结构，涵盖简单指令、块指令与context作用域层级。重点解析worker_processes、include等核心模块指令，以及静态资源服务、SPA部署、反向代理和负载均衡等实战配置]]></description>
            <content:encoded><![CDATA[<p>本文详解 nginx 核心配置结构，涵盖简单指令、块指令与context作用域层级。重点解析worker_processes、include等核心模块指令，以及静态资源服务、SPA部署、反向代理和负载均衡等实战配置</p>
<p><img decoding="async" loading="lazy" alt="jetpack.png" src="https://blog.lintech1024.com/assets/images/jetpack-2ca35d32ab325c6b0d32b6dbe09ca5ed.png" width="507" height="636" class="img_CujE"></p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="example">示例<a href="https://blog.lintech1024.com/nginx-conf#example" class="hash-link" aria-label="示例的直接链接" title="示例的直接链接">​</a></h2>
<div class="language-nginx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">nginx.conf</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-nginx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token directive keyword" style="color:#00009f">user</span><span class="token directive">  nginx</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token directive keyword" style="color:#00009f">worker_processes</span><span class="token directive">  auto</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token directive keyword" style="color:#00009f">error_log</span><span class="token directive">  /var/log/nginx/error.log notice</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token directive keyword" style="color:#00009f">pid</span><span class="token directive">        /run/nginx.pid</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token directive keyword" style="color:#00009f">events</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">worker_connections</span><span class="token directive">  </span><span class="token directive number" style="color:#36acaa">1024</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token directive keyword" style="color:#00009f">http</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">include</span><span class="token directive">       /etc/nginx/mime.types</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">default_type</span><span class="token directive">  application/octet-stream</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">log_format</span><span class="token directive">  main  </span><span class="token directive string" style="color:#e3116c">'</span><span class="token directive string variable" style="color:#36acaa">$remote_addr</span><span class="token directive string" style="color:#e3116c"> - </span><span class="token directive string variable" style="color:#36acaa">$remote_user</span><span class="token directive string" style="color:#e3116c"> [</span><span class="token directive string variable" style="color:#36acaa">$time_local]</span><span class="token directive string" style="color:#e3116c"> "</span><span class="token directive string variable" style="color:#36acaa">$request</span><span class="token directive string" style="color:#e3116c">" '</span><span class="token directive"></span><br></span><span class="token-line" style="color:#393A34"><span class="token directive">                      </span><span class="token directive string" style="color:#e3116c">'</span><span class="token directive string variable" style="color:#36acaa">$status</span><span class="token directive string" style="color:#e3116c"> </span><span class="token directive string variable" style="color:#36acaa">$body_bytes_sent</span><span class="token directive string" style="color:#e3116c"> "</span><span class="token directive string variable" style="color:#36acaa">$http_referer</span><span class="token directive string" style="color:#e3116c">" '</span><span class="token directive"></span><br></span><span class="token-line" style="color:#393A34"><span class="token directive">                      </span><span class="token directive string" style="color:#e3116c">'"</span><span class="token directive string variable" style="color:#36acaa">$http_user_agent</span><span class="token directive string" style="color:#e3116c">" "</span><span class="token directive string variable" style="color:#36acaa">$http_x_forwarded_for</span><span class="token directive string" style="color:#e3116c">"'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">access_log</span><span class="token directive">  /var/log/nginx/access.log  main</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">sendfile</span><span class="token directive">        </span><span class="token directive boolean" style="color:#36acaa">on</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">#tcp_nopush     on;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">keepalive_timeout</span><span class="token directive">  </span><span class="token directive number" style="color:#36acaa">65</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">#gzip  on;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">include</span><span class="token directive"> /etc/nginx/conf.d/*.conf</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="conf-file-structure">配置文件结构<a href="https://blog.lintech1024.com/nginx-conf#conf-file-structure" class="hash-link" aria-label="配置文件结构的直接链接" title="配置文件结构的直接链接">​</a></h2>
<p>nginx 的配置文件由指令(directives)组成, 分为简单指令(simple directives)和块指令(block directives)</p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="simple-directives">简单指令<a href="https://blog.lintech1024.com/nginx-conf#simple-directives" class="hash-link" aria-label="简单指令的直接链接" title="简单指令的直接链接">​</a></h3>
<p><strong>简单指令</strong>由指令名称、参数和末尾分号 <code>;</code> 组成, 名称和参数用空格分隔.</p>
<p>例如下面指令属于简单指令</p>
<div class="language-nginx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-nginx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token directive keyword" style="color:#00009f">user</span><span class="token directive">  nginx</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token directive keyword" style="color:#00009f">worker_processes</span><span class="token directive">  auto</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token directive keyword" style="color:#00009f">error_log</span><span class="token directive">  /var/log/nginx/error.log notice</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token directive keyword" style="color:#00009f">pid</span><span class="token directive">        /run/nginx.pid</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="block-directives">块指令<a href="https://blog.lintech1024.com/nginx-conf#block-directives" class="hash-link" aria-label="块指令的直接链接" title="块指令的直接链接">​</a></h3>
<p><strong>块指令</strong>使用大括号 <code>{}</code> 包裹其他指令, 并且不用分号结尾, 这点跟 C 语言有点像</p>
<p>下面指令中 <code>events</code>、<code>http</code> 都属于块指令</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">events {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    worker_connections  1024;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">http {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    include       /etc/nginx/mime.types;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    default_type  application/octet-stream;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                      '$status $body_bytes_sent "$http_referer" '</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                      '"$http_user_agent" "$http_x_forwarded_for"';</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    access_log  /var/log/nginx/access.log  main;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    sendfile        on;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    #tcp_nopush     on;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    keepalive_timeout  65;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    #gzip  on;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    include /etc/nginx/conf.d/*.conf;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="context">context<a href="https://blog.lintech1024.com/nginx-conf#context" class="hash-link" aria-label="context的直接链接" title="context的直接链接">​</a></h3>
<p>nginx 的一个块指令内部可以形成一个作用域, 称为 <strong>context</strong></p>
<h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="main-context">main context<a href="https://blog.lintech1024.com/nginx-conf#main-context" class="hash-link" aria-label="main context的直接链接" title="main context的直接链接">​</a></h4>
<p>没有被任何块指令包裹, 位于最外层的作用域称为 <strong>main context</strong></p>
<p>示例</p>
<div class="language-nginx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-nginx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># main context</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token directive keyword" style="color:#00009f">http</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># http context</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">server</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># server context</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">location</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token comment" style="color:#999988;font-style:italic"># location context</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="module">模块<a href="https://blog.lintech1024.com/nginx-conf#module" class="hash-link" aria-label="模块的直接链接" title="模块的直接链接">​</a></h2>
<p>nginx 由模块组成, 每个模块有对应的指令用于配置模块的功能</p>
<p>nginx 内置以下核心模块, 用于配置 <code>HTTP</code>、邮件、<code>TCP/UDP</code> 服务</p>
<ul>
<li><a href="https://nginx.org/en/docs/ngx_core_module.html" target="_blank" rel="noopener noreferrer">ngx_core_module</a></li>
<li><a href="https://nginx.org/en/docs/http/ngx_http_core_module.html" target="_blank" rel="noopener noreferrer">ngx_http_core_module</a></li>
<li><a href="https://nginx.org/en/docs/mail/ngx_mail_core_module.html" target="_blank" rel="noopener noreferrer">ngx_mail_core_module</a></li>
<li><a href="https://nginx.org/en/docs/stream/ngx_stream_core_module.html" target="_blank" rel="noopener noreferrer">ngx_stream_core_module</a></li>
</ul>
<p>还有其他扩展模块, 详细列表可参考官方文档 <a href="https://nginx.org/en/docs/#:~:text=Development%20guide-,Modules%20reference,-Alphabetical%20index%20of" target="_blank" rel="noopener noreferrer">Modules reference</a></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="core-module">core module<a href="https://blog.lintech1024.com/nginx-conf#core-module" class="hash-link" aria-label="core module的直接链接" title="core module的直接链接">​</a></h3>
<p><code>ngx_core_module</code> 是 nginx 最核心的一个模块, 包含 nginx 最基础的指令, 用于配置 nginx 的全局参数</p>
<p>基础配置指令</p>
<ul>
<li><code>user</code> - 定义 worker 进程运行的用户和组</li>
<li><code>worker_processes</code> - 设置 worker 进程数量(可设为具体数值或 auto)</li>
<li><code>error_log</code> - 配置错误日志文件路径和日志级别</li>
<li><code>pid</code> - 指定存储主进程 PID 的文件路径</li>
<li><code>include</code> - 包含其他配置文件或匹配特定掩码的文件</li>
<li><code>env</code> - 保留、修改或创建环境变量</li>
<li><code>daemon</code> - 确定 Nginx 是否以守护进程方式运行</li>
</ul>
<p>进程管理指令</p>
<ul>
<li><code>master_process</code> - 控制是否启动 worker 进程</li>
<li><code>worker_cpu_affinity</code> - 将 worker 进程绑定到特定 CPU 核心</li>
<li><code>worker_priority</code> - 设置 worker 进程的调度优先级</li>
<li><code>worker_rlimit_core</code> - 设置 core 文件大小限制</li>
<li><code>worker_rlimit_nofile</code> - 设置 worker 进程可打开文件描述符数量限制</li>
<li><code>worker_shutdown_timeout</code> - 配置 worker 进程优雅关闭的超时时间</li>
<li><code>working_directory</code> - 定义 worker 进程的工作目录</li>
</ul>
<p>高级功能指令</p>
<ul>
<li><code>load_module</code> - 加载动态模块(.so 文件)</li>
<li><code>thread_pool</code> - 定义用于多线程读取和发送文件的线程池</li>
<li><code>pcre_jit</code> - 启用或禁用正则表达式的"即时编译"功能</li>
<li><code>timer_resolution</code> - 降低 worker 进程中的计时器精度</li>
<li><code>lock_file</code> - 指定锁文件前缀(用于实现 accept_mutex 等功能)</li>
<li><code>ssl_engine</code> - 定义硬件 SSL 加速器名称</li>
<li><code>ssl_object_cache_inheritable</code> - 配置 SSL 对象在配置重载时是否继承</li>
</ul>
<p>调试相关指令</p>
<ul>
<li><code>debug_points</code> - 用于内部错误检测时创建 core 文件或停止进程</li>
<li><code>debug_connection</code> - 为特定客户端连接启用调试日志</li>
</ul>
<h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="include">include<a href="https://blog.lintech1024.com/nginx-conf#include" class="hash-link" aria-label="include的直接链接" title="include的直接链接">​</a></h4>
<p>需要特别介绍一下 <code>include</code> 指令, 它允许将配置拆分为多个文件,实现配置的模块化管理</p>
<p>基础语法</p>
<div class="language-nginx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-nginx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token directive keyword" style="color:#00009f">include</span><span class="token directive"> file | mask</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<ul>
<li><code>file</code>: 单个配置文件的绝对/相对路径(如 <code>mime.types</code>)</li>
<li><code>mask</code>: 支持通配符的文件匹配模式(如 <code>conf.d/*.conf</code>)</li>
<li><code>include</code> 指令可以在任何上下文(context)中使用</li>
</ul>
<p>下面是基于 <code>include</code> 指令进行模块化管理的最佳实践</p>
<p>标准目录结构</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">/etc/nginx/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── nginx.conf                </span><span class="token comment" style="color:#999988;font-style:italic"># 主配置</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── conf.d/                   </span><span class="token comment" style="color:#999988;font-style:italic"># 通用配置</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   ├── gzip.conf</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   └── security.conf</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── sites-available/          </span><span class="token comment" style="color:#999988;font-style:italic"># 所有站点配置</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   ├── example.com.conf</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   └── api.example.com.conf</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">└── sites-enabled/            </span><span class="token comment" style="color:#999988;font-style:italic"># 启用的站点(软链接到 sites-available)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    └── example.com.conf → </span><span class="token punctuation" style="color:#393A34">..</span><span class="token plain">/sites-available/example.com.conf</span><br></span></code></pre></div></div>
<div class="language-nginx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">nginx.conf</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-nginx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token directive keyword" style="color:#00009f">http</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">include</span><span class="token directive">       mime.types</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">include</span><span class="token directive">       conf.d/*.conf</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">include</span><span class="token directive">       sites-enabled/*.conf</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="http-module">HTTP module<a href="https://blog.lintech1024.com/nginx-conf#http-module" class="hash-link" aria-label="HTTP module的直接链接" title="HTTP module的直接链接">​</a></h3>
<p>HTTP 的核心模块为 <a href="https://nginx.org/en/docs/http/ngx_http_core_module.html" target="_blank" rel="noopener noreferrer">ngx_http_core_module</a></p>
<p>最简配置</p>
<div class="language-nginx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">nginx.conf</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-nginx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token directive keyword" style="color:#00009f">events</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token directive keyword" style="color:#00009f">http</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">server</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">location</span><span class="token directive"> /</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>配置说明</p>
<ul>
<li><code>http</code><br>
所有 HTTP/HTTPS 服务相关配置必须置于 <code>http</code> 块内. 该块作为核心配置层级, 用于定义全局 HTTP 规则(如 MIME 类型、默认内容类型等)</li>
<li><code>server</code><br>
定义虚拟主机(Virtual Host), 用于处理特定域名或端口的请求. 当前配置中省略了 <code>listen</code> 和 <code>server_name</code> 指令, 因此将采用以下默认值:<!-- -->
<ul>
<li><code>listen</code>: 默认监听 80 端口(HTTP 标准端口)</li>
<li><code>server_name</code>: 默认为空字符串, 可匹配所有未明确指定的域名(即通过 IP 地址或任意域名访问的请求)</li>
</ul>
</li>
<li><code>location</code><br>
定义 URI 路径匹配规则, 然后返回块内的文件给到请求, 其中 <code>/</code> 表示匹配所有请求路径. 当前配置中:<!-- -->
<ul>
<li><code>root</code>: 未显式配置时, 默认路径为 <code>html</code></li>
<li><code>index</code>: 未指定时, 默认索引文件为 <code>index.html</code></li>
</ul>
</li>
</ul>
<p>上面配置的实际效果</p>
<ul>
<li>用户访问 <code>http://ip:80/index.html</code>(可省略为 <code>http://ip</code>), 返回 <code>html/index.html</code> 文件内容</li>
</ul>
<p>添加页面</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">mkdir</span><span class="token plain"> html </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> </span><span class="token builtin class-name">echo</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Hello, world!"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> html/index.html</span><br></span></code></pre></div></div>
<p>访问页面</p>
<p><img decoding="async" loading="lazy" alt="img" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAApoAAAEACAIAAACce0EQAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAACRYSURBVHhe7d17XFTV/v/xNcNthoty7UAggpoCYSZoX0UzwxuhlqEekymtxAuJmXoyu3jM46mflWl4TPSoqd/EepSYeUGNIjuV4gU9v6OC6ElIMSkJ0RkFFeH7x5ZhWA53ULe9no/zx8xnrb3OjI8HvWevvdbemoCAAIG6ODk5tWrVSqvVyg0AmpvJZLpw4YJcBVAr8qluzs7Orq6uZDlwazg7O7u7u8tVALUioupga2vbqlUruQqgJen1er1eL1cB1Iw4r4Obm5tGo5GrAFoYU2JAg/DXUht7e3t7e3u5CqDlabVaJycnuQqgBsR5bchy4DbiDxCoP+K8NvzXBLiN+AME6o84r42Dg4NcAnCraLVaW1tbuQrAGk2j9523bds2MjJy7969WVlZctvdwtfXVy7VQK/XR0VFPfTQQyEhIR4eHkKIoqKio0ePZmZmbt26tbS0VD4AQD0UFhZeuXJFrtabh4eHyWRqygiAWjQ4znU6XVRUVGxsbFhYWHZ2dkhIyMCBA3/66Se5Xwvw9PSMj48PCgqysbGR2yxcv349KysrKSmpqKhIbmugOuNco9FUVFRER0c///zzjo6OrVq1unDhwpIlS4QQ8fHx7u7uRqOxpKRk5cqVqampSmd5CAA1a2Kcr1u3LiMjQ/mTBO5uDYjzLl26jBw58sknnzQajevXr1+/fn1hYeFrr7320UcfFRUV3XPPPfn5+fIxzcfT03PHjh3KiW99nD17dtCgQUajUW5oiNrjXInnadOmDR8+fMWKFRcvXpw0adLUqVOPHTsmhAgJCVm8eHFiYqKrq+vEiRM3bty4cOFCEh1okCbG+aeffpqRkfHBBx/IDcBdp+5r57a2ts8888yOHTs2bdrUrl276dOn9+rVa/HixYWFhUKITz75JD4+/uDBg999992ECRPkg5vPhAkT6p/lQggfH5+4uDi52ny0Wm1FRcWMGTMef/zx6dOnr127duTIkd98882xY8ccHBx0Ol1WVtauXbsMBsPHH388c+bMwYMHT58+vaKigq20AIBmV0e0ODo6fvrpp6+++urBgwcHDhwYGxu7c+fO69eva7Xa/v37r1u3Lj09PSIiIjEx8bnnnktISKh9GrwpgoOD5VJdHnzwQbnUfMrLy6Oiop544om4uLh9+/Z17tzZ1dV1xYoVQogrV64oF8uXLl3aunXr0NDQ3bt3jxs37sknn4yKiiovL5fHqkFiYuKGDRvkahNYDvjDDz/Ex8fLPQAA6lRHnPfu3btjx479+vV77bXXTpw4odyqKT4+/vvvv09MTMzPz4+JiRkwYMCKFSv27t3bo0ePNm3avPnmm8nJyVOmTJHHaho7Ozu5VJcWXZfu7Ow8adKk48ePd+rUadiwYU899ZS9vf3DDz/8xBNPPF4pIiLCzs5uxIgRw4YNu//++3/66aeJEyc6OzvLY6nZjh075syZI1fxx6bT6caOHStXhRBC9OrVKyQkRK4CaLI64jwoKCg7O/vs2bPK24SEhH379vXv3z8xMbFbt26zZs06dOiQMvMcGRn5z3/+89tvv+3Tp09aWtqYMWOio6Pl4ZomJSVl9OjRo0ePFkLMmzdPeV2TlJQU+fhm1a9fv9atW7u5ub300ksvvPDCI488UlZWlpCQMHny5IRKU6dOLS8v79+/f3x8/NSpU11cXFxdXfv27SuPBdxdRo0aNX78+JsTvVevXu+8886oUaOkOoCmqyPOlSvEyusxY8bExsYOGTJk+PDhn332WUlJiXJL80mTJv3rX/9auHDhL7/8MnLkyMjIyDVr1vz000+dOnWSh2ua/Pz8jIyMjIwMIURWVpbyuiYtui5PifPvvvtu+PDhAwcOHDhwYH5+/qpVq5TXZsq8RX5+/qBBgwYOHDh8+PDvvvtu4MCB8lj1k5iY+EOlxMREy6YNGzaYm4YMGSKEWL16tVSxytzHcuI9Pj7eXJdm+8311atXK92cnZ0HDBjA1D0srV27dsWKFVKiK1n+zTffMJ0DtIQ64txS3759v/jii+PHj5srCQkJe/fuHTBgwOLFi8PDw2fOnHngwIFqx9y9goKCMjMzlde2trYeHh7K2kDzSjflRUFBgZeXl/lKQUZGRlBQUNUo9bZ69epOnTr1rhQeHm5OdCVxlXpaWpoQQslvpZKZmZmQkCAPJ4QQwmAwmPsYDAalOGfOHIPBMH/+fKXJPL4yr56WlqbUL168mJSU1Lt3b5PJpBSTkpKqjY4/NinRWzrLfX19e1jTqlUrPz8/udqjR48ePVxcXORRADVrQJw7Ojpeu3bN/NZgMIwaNcp8sn758uVqve92jo6OZ86cUV5rNBq9Xn/+/Hm5kxAmk8nyEv65c+ccHR2r9aiHIUOG3HfffZZ7Z9PS0pTJj/j4eG9v7xEjRij1uXPnbt26devWrc8995y5Z01X65XsN79QfgT07NkzLS1t69atStOaNWu8vb2VJmdn599++02pT506tWogwBpzos+ePbtFs1wIkZSU9Ik1wcHBw4cPl6uffPLJJ580+/oe4PZqQJxL+vfvv2XLFsuTdcld/1xR6Qta/b7SLvPGbTpv06aNyWQyR6wQIjMzUwnpoKAgZYmiZMiQIcqs+KxZs8xRLTFnszJymzZtlMw2zzooTSaTSWlSTuKbd7E97m5r167duXPnoEGDTp482XJZLoR4/PHHA63Zu3dvYmKiXA0MDAwMfPvtt+VRADVrfJzr9fqrV6/KVQtW4+2uUVJS4uXlZX575coVqyfBbm5uJSUl5hT39fW9BdMYiYmJs2bNUibM58+fLzc31tSpU5Xp95sv3gNW9erVS8nydu3a3bwyDkAzanyc/8FlZ2f36NFDeX3t2rVz5855e3sr+9GVovLC09Pz999/LysrU4rh4eHZ2dlVo9TP6dOnnZ2dLc+ww8PDTSaTcmd4Hx+far2F8PX1NU+YKyfW9WcymcLDw81vhwwZ4uzsfPr0aXNlxIgRaWlpln0Aq8zXy8eMGXPzyjgAzYs4b6SvvvrqkUceSUlJ+eqrr3bu3Onn5zd+/Pi0tLSvqhs3bpyfn5/yOiUl5ZFHHklPT5fHqsvWrVtPnDhhuaJtwIABe/bsUS6WKwvllPqcOXOU1Dffu/eJJ54wH1Ufe/bsGTBggPmnw7PPPnvixAnll4H5/0VJffMLd3d3cx1QSGvfrK51B9CM6ojz4uJiDw+PDh06yA31UP/bn6nRrl27Lly4YDQaExMTk5KSvv76a61Wm5iYuKTShx9+uGTJEltb2++++27p0qUffPDB+fPnL1y48M0338hj1cNzzz139uxZ8z6xtLQ0JciFEFFRUT4+Pkq9Z8+eW7duXbNmzX333adUvvzyS3msWs2dOzctLW3WrFnK4Uaj0byqzjzmgAEDoqKilOLhw4fDw8PZqAZLVtexk+hAi6rjESwuLi6fffZZUFBQYGCg9DCD2p9tsHXr1m3btjXj5qX169fv379/0aJFQojc3NzRo0crG9BrMm3atO7du8fGxsoNDVH7I1iioqJef/31Z555Ji8vLzQ09L333jMYDJaPcXNzc0tOTn7llVcOHz7cvn371atXv/322zt27Kg2CnDXCQkJGTVqlNW1b2PHji0qKtqyZYvcUAMewQLUUx1n50aj8bHHHgsMDFRee3h4eHl5zZw5U+5noUuXLu+9917btm137twptzXBqVOnwsLC5GrNunfvnpeXJ1ebj1ar3bFjxxdffLFq1aqIiIgjR45cuHDhhRdeUO5wqWxOGz9+fHFx8eHDhx966KGVK1d++eWXO3bs4BEsuOtlZWVZzXLlHL3+WQ6g/uo4O7f02GOPLV26VAhx8uTJfv36ST97HR0dhw4dOnbs2LZt23755ZfLli07deqUPEQTdO7cedOmTTt27Dhx4sTUqVNTUlJque9bcHBwv379hgwZojyrtNFqPztXnnY6ffr0mJiY5cuXFxcXT5069cUXX8zKylK2kCUmJi5btszFxWXChAkpKSmLFi3iAalAgzTx7Hz16tUZGRnLly+XG4C7TgPiXAjRsWPHixcvFhQUWM5+d+nSxWAwDB069NixYx9//HFqaqryPLFm16dPn/j4+PrsfysvL//ggw/27dsnNzRQ7XFuTvTo6Oi4uDi9Xu/i4lJUVKRcYkhISGjduvXFixcvX7780UcfpaamkuVAQzUxzj08PEwmU1NGANSiYXFu6a9//Wt0dPSFCxf8/Py+/PLLtWvX5uTkyJ1Urs44N9PpdEOHDg0LC7v//vuVld5FRUVHjx7dt2/fjh07lPvbA2ioJsY58MfR+Di3sbExGAxGo3H79u0tdDp+29U/zgG0BOIcqKfGx/kfAXEO3F7EOVBPrLIGAED1iPMa2djYyCUAtxZ/hkA9Eec1sre3l0sAbi07Ozu5BMAa4rxG/HcEuO34MwTqiTivEWfnwG3HnyFQT8S5dfb29vx3BLjtNBqNs7OzXAVwE+LcCq1W6+7uXp/bzwFoaa1bt+a3NVAn4twKNzc31tMCdw53d3f+JIHa2bi6usq1PzA7OzsvLy9OBYA7ilardXR0vH79ellZmdwGQAjivJpWrVq5u7vzAFPgDqTRaPR6vZ2d3ZUrV3iUEXAzzQOdH5BrfyS2trY2NjY2NjZarZaL5YAqXL9+vby8vKysjJN1wEzTq1cvuQYAAFSFiWUAAFSPOAcAQPWIcwAAVI84BwBA9VgK10hOTk7Ozs56vV6n09nb27O9DQBwGxHnDePg4ODu7u7u7m5nZ2cymUwmU0lJydWrV9kwAwC4jYjz+rKzs7vnnnu8vLxMJtO5c+eKioquXr0qdwIA4HYgzuvF3d3dz8/v8uXLZ86cKSoqkpsBALituOJbN19fX39///z8/MOHD5PlAIA7EHFeh4CAgNatWx89evTMmTNyGwAAdwbivDYBAQEODg5ZWVkXL16U2wAAuGMQ5zXy9fV1dHTMyckpKSmR2wAAuJMQ59a5u7t7eXmdOHGCLAcA3PmIcyvs7Oz8/PxOnTrFHDsAQBXqu1FNp9P17t3766+/lhtuCa1W269fvz59+gQFBfn4+Fy6dElZZ75v376MjIzy8nIhxKBBgx588MF33nlHPrjhfH199Xr94cOH5QYAAO5I9YrzVq1avf/++66uriNHjpTbWl6fPn2mTJni4eGRnp5+/Pjx33//3Wg0ent7BwUFRUVF/fbbb6tXr965c+fzzz8fFhaWkJAgH99ADg4OwcHBOTk59dmTptVqAwICOnbs2KlTJyFETk5OTk7Ozz//rPzCAADg1qg7zj08PBYvXmxjY/Piiy/+9ttvcnNL0mg0cXFxo0aNWrZsWWpq6uXLl6UOTk5OY8eOHT169OrVq4UQzRLnPj4+Tk5O9Tk1DwsLmzhxol6vP3XqVF5enrIS3t/f/9KlS8uXL//3v/8tH1AvszdkDvM8sKjvxHVyS3N4evmuad0KN4WPmCeEMCzbNT3o2MK+k5LlbgAAdanj2rmfn9+KFSsuXbo0ceLEW5zlQohJkyb17dt33LhxGzZsuDnLhRCXLl1aunTpCy+8YDAYoqKi5OZGcXd3P3funFytzsXF5ZVXXpk1a9aWLVvGjBnzxhtvrFy5cuXKlW+88cbYsWO3b9/+2muvzZgxw9nZWT4SAIAWUFuct2/ffvny5Xl5eVOmTLlw4YLc3ML69esXExPz4osv/vzzz3JbdYcPH54xY4aXl5fc0HBOTk52dnZ1TrMnJCS0bdv25Zdf3rp1q9RUUVGxefPmmTNndujQoelTBepjWLYrc9cyg1wGALSoGuO8c+fOSUlJ+/fvf/nll69cuSI3tzAbG5spU6YsW7bs999/l9sqDRw48IdKS5YssbOzu379utypgZydnU0mU+3PVomMjOzates777xTy++MvLy8+fPnh4WF9e3bV24DAKC5WY/ziIiIxYsXb9++fe7cuU3PyEaIjIx0dHTctGmT3GDhwIEDU6pbuHCh3KmB9Hq9yWSSqxa8vLyef/75zz//XMryV199ddasWZaVn3/+eePGjePGjXN3d7esAwDQ7KzEef/+/efPn79y5cpFixZVVFTIzbdEnz59Dhw4UPsviaKiokPV1XK6XE86na72+8b06tXL3t5+y5YtUv3ChQs3b1LfsmWLXq+vc7FhXWZvyKyya/nT1VufXrbLojlltpXirmXSMZLZKeauG5TjK1kdvNIcy8+1YbbSeXp3F+HSfXqmtY8KAGgpcpw/+eSTb7755vbt27Oysro2hL+/vzRUU7Rr127fvn1ytS4ajcbHx0euNoS9vX3tM+2enp6nT58uLS2V6mvWrFmzZo1UvHTp0pkzZzw8PKR6A8zZkJk5zPPAonDFwv2i2zSLWJ29IXNad7G/snlT7o2jhgXlVNbCN+W6dJ8mJXEVl+7TM7v8R+m5aL8xcJg5+w3LdmVO6/77pqpxAoZV/TKYsyHzcc/9Cyvb8oQQ6yb1DQ9fuN8ojEq9hRbnAwBuVi3OtVrtxIkThRCDBw/+RwMlJSVZDtVEXl5ejbhg/+CDD96cqQ2i1WrLysrkqgVPT8/jx4/LVSEMBoPBYGUB2LFjx5qwRu/pZY8GGi03rSVP6rs5VwT0U9aazU4ZFmjcv6jvpMrmeSOGzxNCiLkjLKJ03qYDRuHhW9OZsvHAohFzlZfrJn2bK1yCeiuDx3R3ydsUrgwohBDzRizcb3TpPmyOEEI8fa+nMB77oXKH27zhI8z9AAC3XrU4Ly8vnz59uslk2r59e58+fXo3xODBgy2HaqKrV6+2atVKrtbF39//2rVrcrVZXblyxeoFCAcHBwcHB7kqxPXr1xv/kQy9g1yMx/5V/Rx37v/PFS5BfZ4WYnaXAGHM+aGmU2DzFPq0bi7CxTNQbldUH/+/hUbh4tlBKIPn/qd6Rif/cMwoAh+YLYRY90uhqO2kHwBwS8mT7VlZWRMnTuzRo8f8+fOt5tOtUVJS4uTkJFfr4ufnZ3V7ev2Vl5fb2trKVQt5eXnt2rWTqzVr3779yZMn5Wo9dfB0kUsWDL6eQhT+YiXNn16+KzMzc5i4MU++6IBR7lEng6+nXKpu7ojwhfuNAcMqL5wDAG4nOc6VJdkTJkwIDAz8xz/+cbtuhJKTkxMRESFX6+Lr63vixAm52hBXr161t7eXqxZyc3MDAwO1Wiv/bjdTbgHb+Dj/b2FtOZx8plAuCSGEMCyL6+aSuzncYp684Woa3FLypL7mK+7sNQeA28p6LJ09e3bChAlOTk7Lly9v0kquxtq+fXtQUFDnzp3lhprdc889Dz300K5du+SGhigtLdXr9XLVwtGjRwsLCwcNGiTVT548eXNsDx48+Ndff83Ozpbq9ZV8pvDGvLqFOV0Cb8yQ5xZWTn3X6unenWo7ya+BtcENvYNcbpqBF+sm9d2Ue2OKHgBwe1iPcyHE+fPnJ06ceOnSpRUrVvj5+cnNLezHH388fvz4mDFj5Iaavfnmm5cuXWpinJeUlNQ+IXHt2rWkpKSxY8e2bdvWsp6ampqammpZCQgIMBgMH374Ye3b7Wo1b8TmXJdu06p2fBmW7Xo80Hhg5aRkIcS6SSv2GwOGVVvonjJbObE2J/HTy+O6NyLNrQ4+vbtL3iZl3dzTyzdUnY7P6RIojIX/FaKmnyAAgBZWY5wLIUwm05QpU/Lz85cvX96xY0e5uYV98MEHPXv2DAoKkhusGTJkyAMPPJCYmNj4dWdCKF/Z2dm59vn2I0eO7Nmz55VXXgkMrGF5mRDt2rWbNWvWjz/+mJOTI7c1yNwRNzanKaZ3L9xssQEseVJfZf/YDcPEf+bdWIJeWYwTKxtz7dza4J4HFllM4Hsqm8szMzMzH/fcX/Ucl3mbDhhduk1j3zkA3Ep1P1HN1tZ27ty5//M//zNz5syDBw/KzS1p9OjRzz777F//+te9e/fKbZU0Gs1TTz01YcKEtLS0t99+W25uuPvvvz8/P7+goEBusNCqVauXXnopNDR03bp1mzdvtmzSaDRPPvnkU089deTIkYULF9Z+jzkAAJpF3XGuRNTLL788cODA/v37y20t7M9//nN8fPy6des++uijm3eI3XPPPbNnz+7atevXX3/9t7/9rVmeMl7PB6RqNJqhQ4caDIYrV66cOnUqNzfXxsbG39/f39/fwcEhOTlZinkAAFpOveJc0b59+59++kmutjzl2WUdOnQ4evRoVlZWdna2o6NjaGhoaGhox44djUbj4sWLd+7cKR/WWA4ODsHBwTk5OXU+V00I8ac//SkkJOTee++99957hRC//PLLL7/8kpWV9euvv8pdAQBoMQ2I89urc+fOTz31VLdu3ZT96CaT6dChQ1999dWPP/5Y+21ZG8HX11ev19d5gg4AwB1CNXF+K9nZ2QUHB+fn5585c0ZuAwDgzlPbyvY/rGvXruXn5/v7+zfiRrMAANx6xLl1RUVF586du++++2q/qwwAAHcC4rxGZ86cuXz5cqdOnUh0AMAdjjivTV5e3pUrV0JCQph1BwDcyWz8/f3lGiwUFxfb29sHBARUVFQYjY26vRoAAC2MOK+b0Wi8evWqn5+fu7t7WVlZSUmJ3AMAgNuKOK+XkpKSoqIiBwcHf39/Nzc3rVZ79erVJjxbBQCA5sS+84ZxcHBwd3d3d3e3s7MzmUwmk6mkpOTq1atlZWVyVwAAbhXivJGcnJycnZ31er1Op7O3t9dqWVQIALhtiHMAAFSPc0oAAFSPOAcAQPVs/P39b36OOAAAUBFNRESEEKKiokIJdfMLAACgFpoePXr4+Ph06NDhgQce8Pf39/f3t7W1lXsBAIA7mCY5Ofmxxx5zc3MTQpSWljo4OGg0GrkXAAC4g2nKy8vJbwAAVE3DlXIAANSOjWoAAKgecQ4AgOoR5wAAqB5xDgCA6hHnAACoHnEOAIDqEecAAKgecQ4AgOoR5wAAqB5xDgCA6hHnAACoHnEOAIDqEecAAKhey8f5FoNGowl5L7fyfe77nTUajSG1ei/VkL+OVakGjUbT+f3aOwEA0FysxvmhVRPi4ualFsh1If69Km5C3FvbrbSgVir/EQMAuLNZjfOz6StWrfosu0SuC3E6fdWKVclHrLQAAIDbxWqco2V4B/rIJQAAmkHzxXlZaXFBcWmZXG4YU3FBcalcbEmlxQXFJrl4s9LCenwuiw+vl9uEEEJ4urnKJQAAmkFzxHnhnvdjfDR2ejcfN72dxifmw+w6k09Wmvt5XJheo3Fx83HTa+x8hr+3p1juY13p9oR2bXz6/cNy2Vn2Wz19fNrEbbTM6e0JPm18DJ9UjlpWvOe94T52Gr2bj5uLRqMPi/ui+oKAvA8Ht/H5S5oQx1YN9tHovXzc/ry+pq9VnPH+8DaVH14fFveFLjBU6qJ39ZIqAAA0mybHeXGq4b6Iv2xxi303OeWzlOTZ0eKLhJCHG7aoO/u9iHZ/XnXIteeMpSkp/7sgtkPBxpkRgWNS65PouocjgvML0r9Ir0rj/6YmZxQU5K9K+baq26HvUwry3cK6K6fHxanPB0bM3FjQIXbB/6akLJ3R0/XQqhifsPeyqw4oK83NL0hNe9/QM+5gh9hx48e9Pj5SV9VcpXR7XHDPv2zM9+45bUnKZylL4nXbYvq9cUTq5e3jJ1UAAGg+FVZsixVC6LrGjB83TvrfgEAhRPC7Jyt7nk8eKoTouiC76uDzG2N1QsRurny/Obb6IScXhAohYreZD/h+sqsQotuCrGvm0vmU0TohRMz6EnOpZmdXPiqEiN1WefjZf0aKoNjYUKGbsruyz8kFoUJ4v37Q3EEI12e2na9srriW9fdQIUTg64crKycWBAshhOj6dla1DyF9nWu7J7sKIQInp1cNVnFuZbQQInSB+TtXVFRse0auAADQXGqO85pVhdmJBcFC6OK/qXb0tW2xwiJK64rzbc8IIXST0yvfK04v6SmEeHTl2eplq06+GyyEbvL3yruS5KFCPLdt9xSdOb8rjMnRVR9J+QA9l5y2GKKioiJ9ss7yYytxHvD6waofGRUVN3+dzbHi5n8B5R+QOAcA3Co1T7ZbzZ7N1YP+8MFsISK7hhRYKnQLDBKlxeer9axR9qH9QoiY6Eerl/0iBwcJ8W36weplqwIfjfEWpenfZwshRFn6ti0iekBkz0djREFqep4QQohvt6UKETOgpxBClB06eESI0OHR0uz3w5ExQpR+u7vaZYKHI7raWr6X5R47JISIGRApN1jl5Wp9iRwAAE1Tc5zXQ+5/DwkhUif4VBfx1jEhympaNybJzToml4QQQuh0teZoNd0GD9eJ7LTdBUKIjNSNIjLmUZ3oGRkpDqXvKRVCHNqTLkT0YOUXQ17uIfl4IYQQtlavjNch+3C2EELU86P6+XjLJQAAmkGT4tzHL1AIEW31Cvf6GLm3dT6BzRBxPaNHCvFt+u4ycSg1pdS7Z5i3EN6R0aEidXu6ELnpWwrEozGRzkIIIbx9AuXDGy8wWLnCXrewka+Pe5ht5wCAFtGkONfd19VbiPTvd8sNDRAc0l0Ikb77QPWyaU/6ESFCw+qZlpGPRQuxMT2j4GBGgW7k4K5CCBEYOdRbfJ66x7Qn/YgIHhBx42eDc3BXbyGOpO+Rtpsf2J0uhOga3KCwd/P0EUKk77F+wm/Je+jfV47vKlcBAGgOTYpz0W3cX0JFaVLCW9YnzOtDFzM+VicK3vrbeottaaXpMyenChH5ouFGuBZnb3zv/VVHaty5pns0JlKU7t6TnP5t5TVyIbo+PFiUpqev3p0udJEPm38YdB03PViI1DfetsjgstwPZ75VIFwnT4iuKtaD92MxPYUoWLRgo+WnP3Lo5ngvSHv//dXZNX4BAACaQp4kr7C+MPsGeZl6RUX2AuVUOObdbVlnz57NPZjybmyw3+Td5gXh30/WCaEbnXzWqLxX9pUF/33/+co5+vPbnnEVQrgOeD05Pevk/pQFIwNF9a1r255TPmxM8o1BbnZyQagQOp1ORFf1MSZHC+Hq6mq5ja2ioqLiWtaCbkIIEfjckm37T2alL5nRw1XeuqasbH+maj/dDfLXqdg9xVUIIbyjX//s4MnD3yyZ1tNbuZpu+Q94+HXld8m4VHMJAIBm0+Q4r6goOZEy7kHLdWS6ruNTTpqvp9/YmS28X72xa0zZ9i0sc/fa+d3vRlteQw8en3LWIoDPfqQc4jpjT1VRcvBVbyF/bOWngxBDk+XL+yUnU8ZbTuR7R7+722LneM1xftPXqbh2fts0i6G8oxd8fz5F2pZ2dmWkqOPzAwDQaJqKioqqKGqC0hs3LNe5errKi9KVNp2rt+uN1C8tLCguu6lnWWlxYXGpsOxo4dhbIcEp43IPzgiQWxrvxoe+6ZPU7qavU1WUqxZMxQVlupoaAQBoimaL85Z26LV2YVsmnzw8o0FL1QAA+CNo2lK4W6OsYOPz7cL+n+uCz8lyAACsUEOc24pS15iU0wdnBMktAABATZPtAACgJmo4OwcAALUizgEAUD3iHAAA1SPOAQBQPeIcAADVI84BAFA94hwAANUjzgEAUD3iHAAA1SPOAQBQPeIcAADVI84BAFA94hwAANUjzgEAUD3iHAAA1SPOAQBQPeIcAADVI84BAFA94hwAANUjzgEAUD3iHAAA1SPOAQBQPeIcAADVI84BAFA94hwAANUjzgEAUD3iHAAA1SPOAQBQPeIcAADVI84BAFA94hwAANUjzgEAUD3iHAAA1SPOAQBQPeIcAADVI84BAFA94hwAANUjzgEAUD3iHAAA1SPOAQBQPeIcAADVI84BAFA94hwAANUjzgEAUD3iHAAA1SPOAQBQPeIcAADVI84BAFA94hwAANUjzgEAUD3iHAAA1SPOAQBQPeIcAADVI84BAFA94hwAANUjzgEAUD3iHAAA1SPOAQBQPeIcAADVI84BAFA94hwAANUjzgEAUD3iHAAA1SPOAQBQPeIcAADVI84BAFA94hwAANUjzgEAUD3iHAAA1SPOAQBQPeIcAADVI84BAFA94hwAANUjzgEAUD3iHAAA1SPOAQBQPeIcAADVI84BAFA94hwAANUjzgEAUD3iHAAA1SPOAQBQPeIcAADVI84BAFA94hwAANUjzgEAUD3iHAAA1SPOAQBQPeIcAADVI84BAFA94hwAANUjzgEAUD3iHAAA1SPOAQBQPeIcAADVI84BAFA94hwAANUjzgEAUD3iHAAA1SPOAQBQPeIcAADVI84BAFA94hwAANUjzgEAUD3iHAAA1SPOAQBQPeIcAADVI84BAFA94hwAANUjzgEAUD3iHAAA1SPOAQBQPeIcAADVI84BAFA94hwAANUjzgEAUD3iHAAA1SPOAQBQPeIcAADVI84BAFA94hwAANUjzgEAUD3iHAAA1SPOAQBQPeIcAADVI84BAFA94hwAANX7P/yZIZIed8UDAAAAAElFTkSuQmCC" width="666" height="256" class="img_CujE"></p>
<p>完整的配置如下</p>
<div class="language-nginx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-nginx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token directive keyword" style="color:#00009f">http</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">server</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">listen</span><span class="token directive"> </span><span class="token directive number" style="color:#36acaa">80</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">server_name</span><span class="token directive"> </span><span class="token directive string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">location</span><span class="token directive"> /</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token directive keyword" style="color:#00009f">root</span><span class="token directive"> html</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token directive keyword" style="color:#00009f">index</span><span class="token directive"> index.html</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="static-content">静态资源服务<a href="https://blog.lintech1024.com/nginx-conf#static-content" class="hash-link" aria-label="静态资源服务的直接链接" title="静态资源服务的直接链接">​</a></h4>
<p>nginx 可作为静态资源服务, 配置如下</p>
<div class="language-nginx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-nginx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token directive keyword" style="color:#00009f">server</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">location</span><span class="token directive"> /</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">root</span><span class="token directive"> /data/www</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">location</span><span class="token directive"> /images/</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">root</span><span class="token directive"> /data</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 匹配 .gif .jpg .png 扩展名结尾</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">location</span><span class="token directive"> ~ \.(gif|jpg|png)$</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">root</span><span class="token directive"> /data/images</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="spa">单页面应用<a href="https://blog.lintech1024.com/nginx-conf#spa" class="hash-link" aria-label="单页面应用的直接链接" title="单页面应用的直接链接">​</a></h4>
<p>单页面应用(如 React/Vue/Angular)使用前端路由, 所有路径(如 /about、/dashboard)均由前端 JavaScript 处理, 服务器上不存在对应物理路径</p>
<p>默认配置下, 当用户直接访问 /about 时, nginx 会尝试查找服务器上的 /about 目录或文件, 由于文件不存在会返回 404 错误</p>
<p>配置单页面应用需要使用 <code>try_files</code> 指令, 该指令会在找不到文件时返回一个兜底路径, 这样就能将找不到静态资源的请求都返回到 <code>index.html</code> 页面, 交由前端路由处理</p>
<div class="language-nginx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-nginx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token directive keyword" style="color:#00009f">server</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">location</span><span class="token directive"> /</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">root</span><span class="token directive"> html</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">index</span><span class="token directive"> index.html</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">try_files</span><span class="token directive"> </span><span class="token directive variable" style="color:#36acaa">$uri</span><span class="token directive"> </span><span class="token directive variable" style="color:#36acaa">$uri</span><span class="token directive">/ /index.html</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="proxy-pass">反向代理<a href="https://blog.lintech1024.com/nginx-conf#proxy-pass" class="hash-link" aria-label="反向代理的直接链接" title="反向代理的直接链接">​</a></h4>
<p>可以使用 <code>proxy_pass</code> 指令将请求代理到后端服务</p>
<div class="language-nginx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-nginx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token directive keyword" style="color:#00009f">server</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">location</span><span class="token directive"> /api</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">proxy_pass</span><span class="token directive"> http://localhost:8080/</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="http-uptream">负载均衡<a href="https://blog.lintech1024.com/nginx-conf#http-uptream" class="hash-link" aria-label="负载均衡的直接链接" title="负载均衡的直接链接">​</a></h4>
<p>负载均衡可以将请求代理到多个后端实例, 实现服务的高可用</p>
<p>nginx 使用 <code>uptream</code> 指令实现负载均衡, 通过定义后端服务器组实现流量分发</p>
<div class="language-nginx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-nginx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token directive keyword" style="color:#00009f">http</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">upstream</span><span class="token directive"> myapp1</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">server</span><span class="token directive"> srv1.example.com</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">server</span><span class="token directive"> srv2.example.com</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">server</span><span class="token directive"> srv3.example.com</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">server</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">listen</span><span class="token directive"> </span><span class="token directive number" style="color:#36acaa">80</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">location</span><span class="token directive"> /</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">            </span><span class="token directive keyword" style="color:#00009f">proxy_pass</span><span class="token directive"> http://myapp1</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># 指向upstream定义的组</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>nginx 负载均衡支持三种算法, 包括</p>
<ul>
<li>轮询(round-robin): 依次将请求分配给每个服务器, 支持按权重比例分配请求, nginx 默认使用轮询算法</li>
<li>最少连接(least-connected): 将请求分配给当前活跃连接数最少的服务器</li>
<li>IP哈希(ip-hash): 根据客户端 IP 计算哈希值, 固定分配到特定服务器</li>
</ul>
<h5 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="least-conn">配置最少连接算法<a href="https://blog.lintech1024.com/nginx-conf#least-conn" class="hash-link" aria-label="配置最少连接算法的直接链接" title="配置最少连接算法的直接链接">​</a></h5>
<div class="language-nginx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-nginx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">upstream</span><span class="token directive"> myapp1</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">least_conn</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">server</span><span class="token directive"> srv1.example.com</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">server</span><span class="token directive"> srv2.example.com</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">server</span><span class="token directive"> srv3.example.com</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<h5 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="ip-hash">配置 IP 哈希算法<a href="https://blog.lintech1024.com/nginx-conf#ip-hash" class="hash-link" aria-label="配置 IP 哈希算法的直接链接" title="配置 IP 哈希算法的直接链接">​</a></h5>
<div class="language-nginx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-nginx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token directive keyword" style="color:#00009f">upstream</span><span class="token directive"> myapp1</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">ip_hash</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">server</span><span class="token directive"> srv1.example.com</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">server</span><span class="token directive"> srv2.example.com</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">server</span><span class="token directive"> srv3.example.com</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<h5 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="weight-round-robin">配置加权轮询<a href="https://blog.lintech1024.com/nginx-conf#weight-round-robin" class="hash-link" aria-label="配置加权轮询的直接链接" title="配置加权轮询的直接链接">​</a></h5>
<div class="language-nginx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-nginx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">upstream</span><span class="token directive"> myapp1</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">server</span><span class="token directive"> srv1.example.com weight=3</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">server</span><span class="token directive"> srv2.example.com</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token directive keyword" style="color:#00009f">server</span><span class="token directive"> srv3.example.com</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>nginx 自带健康检查机制, 如果某个服务实例不可用, nginx 会将其标记为不可用, 并在一段时间内避免将后续的传入请求分发至该服务器</p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="ssl-module">SSL module<a href="https://blog.lintech1024.com/nginx-conf#ssl-module" class="hash-link" aria-label="SSL module的直接链接" title="SSL module的直接链接">​</a></h3>
<p>nginx 的 SSL 加密由 <a href="https://nginx.org/en/docs/http/ngx_http_ssl_module.html" target="_blank" rel="noopener noreferrer">ngx_http_ssl_module</a> 模块提供</p>
<div class="language-nginx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-nginx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token directive keyword" style="color:#00009f">server</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">listen</span><span class="token directive">              </span><span class="token directive number" style="color:#36acaa">443</span><span class="token directive"> ssl</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">server_name</span><span class="token directive">         www.example.com</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">ssl_certificate</span><span class="token directive">     www.example.com.crt</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">ssl_certificate_key</span><span class="token directive"> www.example.com.key</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">ssl_protocols</span><span class="token directive">       TLSv1.2 TLSv1.3</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">ssl_ciphers</span><span class="token directive">         HIGH:!aNULL:!MD5</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ...</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>为确保所有流量都通过HTTPS, 应设置 HTTP 到 HTTPS 的重定向</p>
<div class="language-nginx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-nginx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token directive keyword" style="color:#00009f">server</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">listen</span><span class="token directive"> </span><span class="token directive number" style="color:#36acaa">80</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">server_name</span><span class="token directive"> default_server</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token directive keyword" style="color:#00009f">return</span><span class="token directive"> </span><span class="token directive number" style="color:#36acaa">301</span><span class="token directive"> https://</span><span class="token directive variable" style="color:#36acaa">$host</span><span class="token directive variable" style="color:#36acaa">$request_uri</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="ref">参考<a href="https://blog.lintech1024.com/nginx-conf#ref" class="hash-link" aria-label="参考的直接链接" title="参考的直接链接">​</a></h2>
<ul>
<li><a href="https://nginx.org/en/docs/" target="_blank" rel="noopener noreferrer">nginx documentation</a></li>
<li><a href="https://nginx.org/en/docs/beginners_guide.html" target="_blank" rel="noopener noreferrer">Beginner’s Guide</a></li>
<li><a href="https://nginx.org/en/docs/dirindex.html" target="_blank" rel="noopener noreferrer">Alphabetical index of directives</a></li>
<li><a href="https://nginx.org/en/docs/varindex.html" target="_blank" rel="noopener noreferrer">Alphabetical index of variables</a></li>
<li><a href="https://nginx.org/en/docs/http/load_balancing.html" target="_blank" rel="noopener noreferrer">Using nginx as HTTP load balancer</a></li>
<li><a href="https://nginx.org/en/docs/http/configuring_https_servers.html" target="_blank" rel="noopener noreferrer">Configuring HTTPS servers</a></li>
</ul>]]></content:encoded>
            <author>lintech1024@hotmail.com (小林)</author>
            <category>快速入门</category>
            <category>nginx</category>
        </item>
        <item>
            <title><![CDATA[直观感受 html lang 作用, 写错这个直接影响网站搜索流量]]></title>
            <link>https://blog.lintech1024.com/html-lang</link>
            <guid>https://blog.lintech1024.com/html-lang</guid>
            <pubDate>Sat, 13 Sep 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[以前学习 HTML 时只知道 html lang 是标记网页语言的, 具体有什么作用不清楚, 对这一块也不太注意, 随便填个值就算了, 没想到这一小小的细节竟会影响网站搜索流量, 注重站点 SEO 的千万别搞错了]]></description>
            <content:encoded><![CDATA[<p>以前学习 HTML 时只知道 html lang 是标记网页语言的, 具体有什么作用不清楚, 对这一块也不太注意, 随便填个值就算了, 没想到这一小小的细节竟会影响网站搜索流量, 注重站点 SEO 的千万别搞错了</p>
<p><img decoding="async" loading="lazy" alt="witch-old.png" src="https://blog.lintech1024.com/assets/images/witch-old-17a970b7817f2c6f5c2166bf402c804a.png" width="597" height="566" class="img_CujE"></p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="故事背景">故事背景<a href="https://blog.lintech1024.com/html-lang#%E6%95%85%E4%BA%8B%E8%83%8C%E6%99%AF" class="hash-link" aria-label="故事背景的直接链接" title="故事背景的直接链接">​</a></h2>
<p>偶然机会想搜索一下自己文章的 Google 排名, 全语言排在第3页的第2行</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-09-14-02-15-49-75d58e22a655a4f77e3e77c50bc73370.png" width="1898" height="1189" class="img_CujE"></p>
<p>切换到中文页面, 翻到第3行了竟不见自己文章踪迹</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-09-14-02-18-38-4e53bac1baf9c33b9398bea4afd87b65.png" width="1902" height="1185" class="img_CujE"></p>
<p>排查了一下原因, 没想到 Google 竟很死板的根据页面的 html lang 值过滤搜索结果</p>
<p>简体中文的值为 <code>zh-CN</code>, 而我的博客错误设置成了 <code>zh</code>, 这就导致用中文过滤永远无法搜索到我的文章</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-09-14-02-24-12-fa96debc2066c98ef72047caf0b72e70.png" width="1890" height="1125" class="img_CujE"></p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="解决">解决<a href="https://blog.lintech1024.com/html-lang#%E8%A7%A3%E5%86%B3" class="hash-link" aria-label="解决的直接链接" title="解决的直接链接">​</a></h2>
<p>知道问题就好办了, 我的博客使用 docusaurus 搭建, 直接修改配置即可</p>
<p>找到 htmllang 改成 <code>zh-CN</code></p>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-09-14-02-29-14-0257221936d1b7d363e26e1dfcf9adf5.png" width="845" height="536" class="img_CujE"></p>
<p>查看页面, 发现已经改过来了</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-09-14-02-32-37-2dc281b5fad076c3469841b12e7f43df.png" width="1348" height="1105" class="img_CujE"></p>
<p>重新部署上去, 静待搜索爬虫重新抓取, 后续看能不能搜索到吧</p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="总结">总结<a href="https://blog.lintech1024.com/html-lang#%E6%80%BB%E7%BB%93" class="hash-link" aria-label="总结的直接链接" title="总结的直接链接">​</a></h2>
<p>html lang 主要影响的是搜索引擎, 搜索引擎会根据这个值判断页面语言, 作为语言过滤条件.</p>
<p>简体中文的 html lang 值是 <code>zh-CN</code>, 千万不要搞错了</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>提示</div><div class="admonitionContent_BuS1"><p>其实简体中文的更标准写法是 <code>zh-Hans</code> 或 <code>zh-Hans-CN</code>, 由于历史原因 <code>zh-CN</code> 使用得更多</p></div></div>]]></content:encoded>
            <author>lintech1024@hotmail.com (小林)</author>
            <category>博客技术</category>
            <category>前端</category>
        </item>
        <item>
            <title><![CDATA[使用 MarkItDown 实现一个文档转 Markdown 服务]]></title>
            <link>https://blog.lintech1024.com/markitdown-project</link>
            <guid>https://blog.lintech1024.com/markitdown-project</guid>
            <pubDate>Wed, 10 Sep 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[MarkItDown 是一个微软开源的 Python 工具, 用于将多种文件格式转换为 Markdown 格式. 本文将介绍如何使用 MarkItDown, 最后用 MarkItDown 实现一个文档转 Markdown 服务]]></description>
            <content:encoded><![CDATA[<p>MarkItDown 是一个微软开源的 Python 工具, 用于将多种文件格式转换为 Markdown 格式. 本文将介绍如何使用 MarkItDown, 最后用 MarkItDown 实现一个文档转 Markdown 服务</p>
<p><img decoding="async" loading="lazy" alt="witch-broom.png" src="https://blog.lintech1024.com/assets/images/witch-broom-a1b71fd6777e8a112fc2d269477d9b50.png" width="538" height="668" class="img_CujE"></p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="pre">前置条件<a href="https://blog.lintech1024.com/markitdown-project#pre" class="hash-link" aria-label="前置条件的直接链接" title="前置条件的直接链接">​</a></h2>
<ul>
<li>Dev Container 开发环境, 可以参考我之前的文章 <a href="https://blog.lintech1024.com/devcontainer-tutorial">Dev Container 教程 (一) 基本使用</a></li>
<li>安装 Docker, Windows 系统推荐安装 <a href="https://www.docker.com/products/docker-desktop/" target="_blank" rel="noopener noreferrer">Docker Desktop</a></li>
</ul>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="install">安装<a href="https://blog.lintech1024.com/markitdown-project#install" class="hash-link" aria-label="安装的直接链接" title="安装的直接链接">​</a></h2>
<p>全量依赖安装</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">pip </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> 'markitdown</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">all</span><span class="token punctuation" style="color:#393A34">]</span><br></span></code></pre></div></div>
<p>可选依赖安装</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">pip </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'markitdown[pdf, docx, pptx]'</span><br></span></code></pre></div></div>
<p>可选的依赖列表如下:</p>
<ul>
<li><code>[all]</code> 安装所有可选依赖项</li>
<li><code>[pptx]</code> 安装 PPT 文件的依赖项</li>
<li><code>[docx]</code> 安装 Word 文件的依赖项</li>
<li><code>[xlsx]</code> 安装 Excel 文件的依赖项</li>
<li><code>[xls]</code> 安装旧版 Excel 文件的依赖项</li>
<li><code>[pdf]</code> 安装 PDF 文件的依赖项</li>
<li><code>[outlook]</code> 安装 Outlook 邮件的依赖项</li>
<li><code>[az-doc-intel]</code> 安装 Azure Document Intelligence 的依赖项</li>
<li><code>[audio-transcription]</code> 安装 wav 和 mp3 文件音频转录的依赖项</li>
<li><code>[youtube-transcription]</code> 安装获取 YouTube 视频转录的依赖项</li>
</ul>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="deps">依赖<a href="https://blog.lintech1024.com/markitdown-project#deps" class="hash-link" aria-label="依赖的直接链接" title="依赖的直接链接">​</a></h2>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="ffmpeg">ffmpeg<a href="https://blog.lintech1024.com/markitdown-project#ffmpeg" class="hash-link" aria-label="ffmpeg的直接链接" title="ffmpeg的直接链接">​</a></h3>
<p>转换 PDF 时需要依赖 <a href="https://ffmpeg.org/" target="_blank" rel="noopener noreferrer">ffmpeg</a>, 这在官方文档并没有提到</p>
<p>Ubuntu 安装 ffmpeg</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">apt</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-y</span><span class="token plain"> ffmpeg</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="exiftool">exiftool<a href="https://blog.lintech1024.com/markitdown-project#exiftool" class="hash-link" aria-label="exiftool的直接链接" title="exiftool的直接链接">​</a></h3>
<p>转换图片需要依赖 <a href="https://www.exiftool.org/" target="_blank" rel="noopener noreferrer">exiftool</a></p>
<p>直接用 Ubuntu 包管理安装的 exiftool 可能有版本太低存在安全漏洞的问题, 需要源码编译高版本</p>
<p>使用 MarkItDown 图片转 Makrdown 的效果并不好, 只能得到类似 "ImageSize: 931x895" 这样的文本, 需要借助大模型的能力才能识别图片内容, 我尝试了用阿里千问的 <code>qwen-vl-max</code> 模型集成, 确实能够正确转换为 Markdown, 但是这样的话还不如直接就用大模型识别, 感觉有点鸡肋</p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="plugins">插件<a href="https://blog.lintech1024.com/markitdown-project#plugins" class="hash-link" aria-label="插件的直接链接" title="插件的直接链接">​</a></h2>
<p>MarkItDown 支持第三方插件, 感觉功能不太成熟就不多介绍了, 想了解的可以参考官方文档: <a href="https://github.com/microsoft/markitdown?tab=readme-ov-file#plugins" target="_blank" rel="noopener noreferrer">MarkItDown Plugins</a></p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="usage">使用<a href="https://blog.lintech1024.com/markitdown-project#usage" class="hash-link" aria-label="使用的直接链接" title="使用的直接链接">​</a></h2>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="li">命令行<a href="https://blog.lintech1024.com/markitdown-project#li" class="hash-link" aria-label="命令行的直接链接" title="命令行的直接链接">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">markitdown path-to-file.pdf </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> document.md</span><br></span></code></pre></div></div>
<p>可以使用 <code>-o</code> 参数指定输出文件</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">markitdown path-to-file.pdf </span><span class="token parameter variable" style="color:#36acaa">-o</span><span class="token plain"> document.md</span><br></span></code></pre></div></div>
<p>或者使用 Linux 管道符</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">cat</span><span class="token plain"> path-to-file.pdf </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> markitdown</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="python-api">Python API<a href="https://blog.lintech1024.com/markitdown-project#python-api" class="hash-link" aria-label="Python API的直接链接" title="Python API的直接链接">​</a></h3>
<h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="basic-usage">基本使用<a href="https://blog.lintech1024.com/markitdown-project#basic-usage" class="hash-link" aria-label="基本使用的直接链接" title="基本使用的直接链接">​</a></h4>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> markitdown </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> MarkItDown</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">md </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> MarkItDown</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">enable_plugins</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">False</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Set to True to enable plugins</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> md</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">convert</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"test.xlsx"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">result</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">text_content</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="use-llm">集成大模型<a href="https://blog.lintech1024.com/markitdown-project#use-llm" class="hash-link" aria-label="集成大模型的直接链接" title="集成大模型的直接链接">​</a></h4>
<p>我使用阿里千问的 <code>qwen-vl-max</code> 模型</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> markitdown </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> MarkItDown</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> openai </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> OpenAI</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">client </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> OpenAI</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">base_url</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"https://dashscope.aliyuncs.com/compatible-mode/v1"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> api_key</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"sk-xxx"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">md </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> MarkItDown</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">llm_client</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">client</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> llm_model</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"qwen-vl-max"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> llm_prompt</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"识别图片中的内容, 直接返回原始内容, 不要任何多余处理, 不要返回任何多余的文本"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> md</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">convert</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"a.jpg"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">result</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">text_content</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="use-docker">Docker<a href="https://blog.lintech1024.com/markitdown-project#use-docker" class="hash-link" aria-label="Docker的直接链接" title="Docker的直接链接">​</a></h3>
<p>MarkItDown 官方并没有提供构建好的 Docker 镜像, 需要自己下载源码构建</p>
<p>博主自己构建了一个镜像, 可以直接使用</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> run </span><span class="token parameter variable" style="color:#36acaa">--rm</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-i</span><span class="token plain"> linrepo/markitdown </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain"> ~/your-file.pdf </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> output.md</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="markitdown-server">封装成服务<a href="https://blog.lintech1024.com/markitdown-project#markitdown-server" class="hash-link" aria-label="封装成服务的直接链接" title="封装成服务的直接链接">​</a></h2>
<p>使用 FastAPI 框架将 MarkItDown 封装成一个服务</p>
<p>源码已开源到 GitHub: <a href="https://github.com/lintech1024/markitdown-server" target="_blank" rel="noopener noreferrer">lintech1024/markitdown-server</a></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="dev">开发环境准备<a href="https://blog.lintech1024.com/markitdown-project#dev" class="hash-link" aria-label="开发环境准备的直接链接" title="开发环境准备的直接链接">​</a></h3>
<p>用 Dev Container 启动开发环境</p>
<details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary><p>配置文件</p></summary><div><div class="collapsibleContent_i85q"><div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">.devcontainer/devcontainer.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"markitdown-server"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"image"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"mcr.microsoft.com/devcontainers/python:1-3.12-bullseye"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"customizations"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token property" style="color:#36acaa">"vscode"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"settings"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"window.newWindowProfile"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Default"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 新窗口使用的配置文件, 只能在"默认配置文件"中设置</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"files.autoSave"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"afterDelay"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 自动保存文件</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"editor.formatOnSave"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 保存时自动格式化</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"files.simpleDialog.enable"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 简单对话框, ctrl+n 新建文件时不再弹出对话框</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"editor.insertSpaces"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 使用空格代替制表符</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"editor.tabSize"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 缩进空格数</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"editor.autoClosingDelete"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"always"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 删除左括号时总是自动删除右括号</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"editor.minimap.autohide"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"mouseover"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 鼠标不在缩略图上时隐藏缩略图, 鼠标在缩略图上时显示缩略图</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"terminal.integrated.cursorStyle"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"underline"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 终端光标样式使用下划线</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"explorer.confirmDelete"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 删除文件时不再弹出确认对话框</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"explorer.confirmDragAndDrop"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 拖放移动文件或文件夹时不再弹出确认对话框</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"vsicons.dontShowNewVersionMessage"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 不显示新版本消息</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"workbench.iconTheme"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"vscode-icons"</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 使用 vscode-icons 图标主题</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"extensions"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"mhutchie.git-graph"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Git 图形化视图</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"eamodio.gitlens"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Git 增强工具, 主要为了能显示代码作者</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				</span><span class="token string" style="color:#e3116c">"ms-python.vscode-pylance"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Python 语言支持</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"vscode-icons-team.vscode-icons"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// VSCode 图标主题</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"github.copilot"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// GitHub Copilot, AI 代码补全</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"github.copilot-chat"</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// GitHub Copilot Chat, AI 聊天助手</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"containerEnv"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token property" style="color:#36acaa">"PIP_INDEX_URL"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"https://mirrors.aliyun.com/pypi/simple/"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token property" style="color:#36acaa">"PIP_TRUSTED_HOST"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"mirrors.aliyun.com"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"postCreateCommand"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"bash scripts/install.sh"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"postStartCommand"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"uvicorn main:app --reload"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token property" style="color:#36acaa">"forwardPorts"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">8000</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">scripts/install.sh</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># 使用阿里源</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">sed</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-i</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"s@deb.debian.org@mirrors.aliyun.com@g"</span><span class="token plain"> /etc/apt/sources.list</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 更新 apt 并安装 ffmpeg</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">apt</span><span class="token plain"> update </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">apt</span><span class="token plain"> upgrade </span><span class="token parameter variable" style="color:#36acaa">-y</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">apt</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-y</span><span class="token plain"> ffmpeg</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># sudo apt install -y exiftool</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 更新 pip 并安装 markitdown</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">pip </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-U</span><span class="token plain"> pip </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> pip </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> markitdown</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">all</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 安装 fastapi</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">pip </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> fastapi uvicorn</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">standard</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> python-multipart</span><br></span></code></pre></div></div></div></div></details>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="api">API 接口<a href="https://blog.lintech1024.com/markitdown-project#api" class="hash-link" aria-label="API 接口的直接链接" title="API 接口的直接链接">​</a></h3>
<p>上传文件并让 MarkItDown 转换</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">main.py</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token decorator annotation punctuation" style="color:#393A34">@app</span><span class="token decorator annotation punctuation" style="color:#393A34">.</span><span class="token decorator annotation punctuation" style="color:#393A34">post</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"/uploadfile/"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">convert_file</span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">file</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> UploadFile</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">try</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        contents </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> </span><span class="token builtin">file</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">read</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        converter </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> MarkItDown</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> converter</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">convert</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">io</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">BytesIO</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">contents</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> Response</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">content</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">result</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">text_content</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">except</span><span class="token plain"> Exception </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> e</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-interpolation string" style="color:#e3116c">f"Error during conversion: </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation builtin">str</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">(</span><span class="token string-interpolation interpolation">e</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">)</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">raise</span><span class="token plain"> HTTPException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">status_code</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">500</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> detail</span><span class="token operator" style="color:#393A34">=</span><span class="token string-interpolation string" style="color:#e3116c">f"转换失败: </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation builtin">str</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">(</span><span class="token string-interpolation interpolation">e</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">)</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>MarkItDown 支持直接转换网页, 再编写一个转网页接口</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">main.py</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token decorator annotation punctuation" style="color:#393A34">@app</span><span class="token decorator annotation punctuation" style="color:#393A34">.</span><span class="token decorator annotation punctuation" style="color:#393A34">post</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"/convertPage/"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">convert_page</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">url</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">try</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        converter </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> MarkItDown</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> converter</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">convert</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">url</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> Response</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">content</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">result</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">text_content</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">except</span><span class="token plain"> Exception </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> e</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-interpolation string" style="color:#e3116c">f"Error during conversion: </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation builtin">str</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">(</span><span class="token string-interpolation interpolation">e</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">)</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">raise</span><span class="token plain"> HTTPException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">status_code</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">500</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> detail</span><span class="token operator" style="color:#393A34">=</span><span class="token string-interpolation string" style="color:#e3116c">f"转换失败: </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation builtin">str</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">(</span><span class="token string-interpolation interpolation">e</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">)</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="page">前端页面<a href="https://blog.lintech1024.com/markitdown-project#page" class="hash-link" aria-label="前端页面的直接链接" title="前端页面的直接链接">​</a></h3>
<p>直接让AI帮我们写前端页面, 效果是真不错 👍</p>
<details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary><p>页面代码</p></summary><div><div class="collapsibleContent_i85q"><div class="language-html codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">static/index.html</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-html codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token doctype punctuation" style="color:#393A34;font-style:italic">&lt;!</span><span class="token doctype doctype-tag" style="color:#999988;font-style:italic">DOCTYPE</span><span class="token doctype" style="color:#999988;font-style:italic"> </span><span class="token doctype name" style="color:#999988;font-style:italic">html</span><span class="token doctype punctuation" style="color:#393A34;font-style:italic">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">html</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">lang</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">zh-CN</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">head</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">meta</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">charset</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">UTF-8</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">meta</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">name</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">viewport</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">content</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">width=device-width, initial-scale=1.0</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">title</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">文档转Markdown</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">title</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">style</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector" style="color:#00009f">*</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">box-sizing</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> border-box</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">margin</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">0</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">padding</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">0</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">font-family</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css string" style="color:#e3116c">'Segoe UI'</span><span class="token style language-css punctuation" style="color:#393A34">,</span><span class="token style language-css"> Tahoma</span><span class="token style language-css punctuation" style="color:#393A34">,</span><span class="token style language-css"> Geneva</span><span class="token style language-css punctuation" style="color:#393A34">,</span><span class="token style language-css"> Verdana</span><span class="token style language-css punctuation" style="color:#393A34">,</span><span class="token style language-css"> sans-serif</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector" style="color:#00009f">body</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">background-color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#f5f7fa</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#333</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">line-height</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">1.6</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">padding</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">20</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">max-width</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">800</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">margin</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">0</span><span class="token style language-css"> auto</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.container</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">background</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css color">white</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">border-radius</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">10</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">box-shadow</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">0</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">2</span><span class="token style language-css unit">px</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">10</span><span class="token style language-css unit">px</span><span class="token style language-css"> </span><span class="token style language-css color function" style="color:#d73a49">rgba</span><span class="token style language-css color punctuation" style="color:#393A34">(</span><span class="token style language-css color number" style="color:#36acaa">0</span><span class="token style language-css color punctuation" style="color:#393A34">,</span><span class="token style language-css color"> </span><span class="token style language-css color number" style="color:#36acaa">0</span><span class="token style language-css color punctuation" style="color:#393A34">,</span><span class="token style language-css color"> </span><span class="token style language-css color number" style="color:#36acaa">0</span><span class="token style language-css color punctuation" style="color:#393A34">,</span><span class="token style language-css color"> </span><span class="token style language-css color number" style="color:#36acaa">0.1</span><span class="token style language-css color punctuation" style="color:#393A34">)</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">padding</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">30</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">margin-top</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">20</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector" style="color:#00009f">header</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">text-align</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> center</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">margin-bottom</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">30</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector" style="color:#00009f">h1</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#2c3e50</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">margin-bottom</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">10</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.description</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#7f8c8d</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">max-width</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">600</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">margin</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">0</span><span class="token style language-css"> auto</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.url-converter</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">margin-top</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">30</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">padding-top</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">20</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">border-top</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">1</span><span class="token style language-css unit">px</span><span class="token style language-css"> solid </span><span class="token style language-css hexcode color">#eee</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.url-converter</span><span class="token style language-css selector" style="color:#00009f"> h2</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">text-align</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> center</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">margin-bottom</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">10</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#2c3e50</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.url-input-group</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">display</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> flex</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">gap</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">10</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">margin-top</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">10</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.url-input</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">flex</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">1</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">padding</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">12</span><span class="token style language-css unit">px</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">15</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">border</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">1</span><span class="token style language-css unit">px</span><span class="token style language-css"> solid </span><span class="token style language-css hexcode color">#ddd</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">border-radius</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">5</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">font-size</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">16</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">outline</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> none</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">transition</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> border-color </span><span class="token style language-css number" style="color:#36acaa">0.3</span><span class="token style language-css unit">s</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.url-input</span><span class="token style language-css selector pseudo-class" style="color:#00009f">:focus</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">border-color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#3498db</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.upload-area</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">border</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">2</span><span class="token style language-css unit">px</span><span class="token style language-css"> dashed </span><span class="token style language-css hexcode color">#3498db</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">border-radius</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">8</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">padding</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">40</span><span class="token style language-css unit">px</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">20</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">text-align</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> center</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">background-color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#f8f9fa</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">transition</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> all </span><span class="token style language-css number" style="color:#36acaa">0.3</span><span class="token style language-css unit">s</span><span class="token style language-css"> ease</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">cursor</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> pointer</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">margin-bottom</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">20</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.upload-area</span><span class="token style language-css selector pseudo-class" style="color:#00009f">:hover</span><span class="token style language-css selector punctuation" style="color:#393A34">,</span><span class="token style language-css selector" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css selector" style="color:#00009f">        </span><span class="token style language-css selector class" style="color:#00009f">.upload-area</span><span class="token style language-css selector class" style="color:#00009f">.dragover</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">background-color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#e3f2fd</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">border-color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#2980b9</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.upload-icon</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">font-size</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">48</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#3498db</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">margin-bottom</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">15</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.upload-text</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">font-size</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">18</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#2c3e50</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">margin-bottom</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">10</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.upload-hint</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#7f8c8d</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">font-size</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">14</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.formats-list</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">display</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> flex</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">flex-wrap</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> wrap</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">justify-content</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> center</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">gap</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">10</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">margin-top</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">15</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.format-tag</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">background-color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#e1f5fe</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#0288d1</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">padding</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">3</span><span class="token style language-css unit">px</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">10</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">border-radius</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">15</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">font-size</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">12</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">font-weight</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> bold</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.file-input</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">display</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> none</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.btn</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">background-color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#3498db</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css color">white</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">border</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> none</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">padding</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">12</span><span class="token style language-css unit">px</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">24</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">border-radius</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">5</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">font-size</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">16</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">cursor</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> pointer</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">transition</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> background-color </span><span class="token style language-css number" style="color:#36acaa">0.3</span><span class="token style language-css unit">s</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">display</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> inline-block</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">margin-top</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">10</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.btn</span><span class="token style language-css selector pseudo-class" style="color:#00009f">:hover</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">background-color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#2980b9</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.btn</span><span class="token style language-css selector pseudo-class" style="color:#00009f">:disabled</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">background-color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#95a5a6</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">cursor</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> not-allowed</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.status</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">margin-top</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">20</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">padding</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">15</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">border-radius</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">5</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">display</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> none</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.status</span><span class="token style language-css selector class" style="color:#00009f">.processing</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">background-color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#e3f2fd</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#1976d2</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">display</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> block</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.status</span><span class="token style language-css selector class" style="color:#00009f">.success</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">background-color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#d4edda</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#155724</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">display</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> block</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.status</span><span class="token style language-css selector class" style="color:#00009f">.error</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">background-color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#f8d7da</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#721c24</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">display</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> none</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.progress-container</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">width</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">100</span><span class="token style language-css unit">%</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">height</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">10</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">background-color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#e9ecef</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">border-radius</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">5</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">margin-top</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">15</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">overflow</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> hidden</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">display</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> none</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.progress-bar</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">height</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">100</span><span class="token style language-css unit">%</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">background-color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#28a745</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">width</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">0</span><span class="token style language-css unit">%</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">transition</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> width </span><span class="token style language-css number" style="color:#36acaa">0.3</span><span class="token style language-css unit">s</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.file-info</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">margin-top</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">15</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">padding</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">10</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">background-color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#f8f9fa</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">border-radius</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">5</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">display</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> none</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.features</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">display</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> flex</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">justify-content</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> space-between</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">margin-top</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">30</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">flex-wrap</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> wrap</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">gap</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">20</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.feature</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">flex</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">1</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">min-width</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">250</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">padding</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">15</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">text-align</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> center</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">background</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#f8f9fa</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">border-radius</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">8</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">box-shadow</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">0</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">1</span><span class="token style language-css unit">px</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">3</span><span class="token style language-css unit">px</span><span class="token style language-css"> </span><span class="token style language-css color function" style="color:#d73a49">rgba</span><span class="token style language-css color punctuation" style="color:#393A34">(</span><span class="token style language-css color number" style="color:#36acaa">0</span><span class="token style language-css color punctuation" style="color:#393A34">,</span><span class="token style language-css color"> </span><span class="token style language-css color number" style="color:#36acaa">0</span><span class="token style language-css color punctuation" style="color:#393A34">,</span><span class="token style language-css color"> </span><span class="token style language-css color number" style="color:#36acaa">0</span><span class="token style language-css color punctuation" style="color:#393A34">,</span><span class="token style language-css color"> </span><span class="token style language-css color number" style="color:#36acaa">0.05</span><span class="token style language-css color punctuation" style="color:#393A34">)</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.feature-icon</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">font-size</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">28</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#3498db</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">margin-bottom</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">10</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css selector class" style="color:#00009f">.spinner</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">display</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> inline-block</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">width</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">20</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">height</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">20</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">border</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">3</span><span class="token style language-css unit">px</span><span class="token style language-css"> solid </span><span class="token style language-css color function" style="color:#d73a49">rgba</span><span class="token style language-css color punctuation" style="color:#393A34">(</span><span class="token style language-css color number" style="color:#36acaa">0</span><span class="token style language-css color punctuation" style="color:#393A34">,</span><span class="token style language-css color"> </span><span class="token style language-css color number" style="color:#36acaa">0</span><span class="token style language-css color punctuation" style="color:#393A34">,</span><span class="token style language-css color"> </span><span class="token style language-css color number" style="color:#36acaa">0</span><span class="token style language-css color punctuation" style="color:#393A34">,</span><span class="token style language-css color"> </span><span class="token style language-css color number" style="color:#36acaa">0.1</span><span class="token style language-css color punctuation" style="color:#393A34">)</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">border-radius</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">50</span><span class="token style language-css unit">%</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">border-top-color</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css hexcode color">#3498db</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">animation</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> spin </span><span class="token style language-css number" style="color:#36acaa">1</span><span class="token style language-css unit">s</span><span class="token style language-css"> ease-in-out infinite</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">margin-right</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">8</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css property" style="color:#36acaa">vertical-align</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> middle</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css atrule rule" style="color:#00a4db">@keyframes</span><span class="token style language-css atrule" style="color:#00a4db"> spin</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css selector" style="color:#00009f">to</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">                </span><span class="token style language-css property" style="color:#36acaa">transform</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css function" style="color:#d73a49">rotate</span><span class="token style language-css punctuation" style="color:#393A34">(</span><span class="token style language-css number" style="color:#36acaa">360</span><span class="token style language-css unit">deg</span><span class="token style language-css punctuation" style="color:#393A34">)</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css atrule rule" style="color:#00a4db">@media</span><span class="token style language-css atrule" style="color:#00a4db"> </span><span class="token style language-css atrule punctuation" style="color:#393A34">(</span><span class="token style language-css atrule property" style="color:#36acaa">max-width</span><span class="token style language-css atrule punctuation" style="color:#393A34">:</span><span class="token style language-css atrule" style="color:#00a4db"> </span><span class="token style language-css atrule number" style="color:#36acaa">600</span><span class="token style language-css atrule unit" style="color:#00a4db">px</span><span class="token style language-css atrule punctuation" style="color:#393A34">)</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css selector class" style="color:#00009f">.container</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">                </span><span class="token style language-css property" style="color:#36acaa">padding</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">20</span><span class="token style language-css unit">px</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">15</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css selector class" style="color:#00009f">.upload-area</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">                </span><span class="token style language-css property" style="color:#36acaa">padding</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">30</span><span class="token style language-css unit">px</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">15</span><span class="token style language-css unit">px</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css selector class" style="color:#00009f">.features</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">                </span><span class="token style language-css property" style="color:#36acaa">flex-direction</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> column</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css selector class" style="color:#00009f">.feature</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">                </span><span class="token style language-css property" style="color:#36acaa">min-width</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> </span><span class="token style language-css number" style="color:#36acaa">100</span><span class="token style language-css unit">%</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css selector class" style="color:#00009f">.url-input-group</span><span class="token style language-css"> </span><span class="token style language-css punctuation" style="color:#393A34">{</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">                </span><span class="token style language-css property" style="color:#36acaa">flex-direction</span><span class="token style language-css punctuation" style="color:#393A34">:</span><span class="token style language-css"> column</span><span class="token style language-css punctuation" style="color:#393A34">;</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">            </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">        </span><span class="token style language-css punctuation" style="color:#393A34">}</span><span class="token style language-css"></span><br></span><span class="token-line" style="color:#393A34"><span class="token style language-css">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">style</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">head</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">body</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">container</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">header</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">h1</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">文档转Markdown</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">h1</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">p</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">description</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">上传PDF、Office文档等文件, 一键转换为结构完整的Markdown格式, 支持拖拽上传</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">p</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">header</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">upload-area</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">id</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">dropZone</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">upload-icon</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">📄</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">upload-text</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">点击选择文件或拖拽到此处</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">upload-hint</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">支持多种格式, 最大支持50MB</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">formats-list</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">span</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">format-tag</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">PDF</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">span</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">span</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">format-tag</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">Word</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">span</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">span</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">format-tag</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">Excel</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">span</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">span</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">format-tag</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">PPT</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">span</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">span</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">format-tag</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">TXT</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">span</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">span</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">format-tag</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">HTML</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">span</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">input</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">type</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">file</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">file-input</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">id</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">fileInput</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">accept</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">.pdf,.docx,.xlsx,.xls,.pptx,.txt,.html</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">button</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">btn</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">id</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">browseBtn</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">选择文件</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">file-info</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">id</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">fileInfo</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">已选择: </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">span</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">id</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">fileName</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">span</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">大小: </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">span</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">id</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">fileSize</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">span</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">&lt;!-- URL转换区域移动到文档转换区域下方 --&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">url-converter</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">h2</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">网页转Markdown</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">h2</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">p</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">description</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">输入网页URL地址, 一键转换为Markdown格式</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">p</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">url-input-group</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">input</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">type</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">url</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">url-input</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">id</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">urlInput</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">placeholder</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">https://example.com</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">required</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">button</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">btn</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">id</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">convertUrlBtn</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">转换网页</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">status processing</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">id</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">processingStatus</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag special-attr attr-name" style="color:#00a4db">style</span><span class="token tag special-attr attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag special-attr attr-value punctuation" style="color:#393A34">"</span><span class="token tag special-attr attr-value value css language-css property" style="color:#36acaa">display</span><span class="token tag special-attr attr-value value css language-css punctuation" style="color:#393A34">:</span><span class="token tag special-attr attr-value value css language-css" style="color:#e3116c"> none</span><span class="token tag special-attr attr-value value css language-css punctuation" style="color:#393A34">;</span><span class="token tag special-attr attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">span</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">spinner</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">span</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">正在转换文档，请稍候...</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">progress-container</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">id</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">progressContainer</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">progress-bar</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">id</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">progressBar</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">status success</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">id</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">successStatus</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag special-attr attr-name" style="color:#00a4db">style</span><span class="token tag special-attr attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag special-attr attr-value punctuation" style="color:#393A34">"</span><span class="token tag special-attr attr-value value css language-css property" style="color:#36acaa">display</span><span class="token tag special-attr attr-value value css language-css punctuation" style="color:#393A34">:</span><span class="token tag special-attr attr-value value css language-css" style="color:#e3116c"> none</span><span class="token tag special-attr attr-value value css language-css punctuation" style="color:#393A34">;</span><span class="token tag special-attr attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            转换成功！正在下载...</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">status error</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">id</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">errorStatus</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag special-attr attr-name" style="color:#00a4db">style</span><span class="token tag special-attr attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag special-attr attr-value punctuation" style="color:#393A34">"</span><span class="token tag special-attr attr-value value css language-css property" style="color:#36acaa">display</span><span class="token tag special-attr attr-value value css language-css punctuation" style="color:#393A34">:</span><span class="token tag special-attr attr-value value css language-css" style="color:#e3116c"> none</span><span class="token tag special-attr attr-value value css language-css punctuation" style="color:#393A34">;</span><span class="token tag special-attr attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            转换失败：未知错误</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">&lt;!-- 更新features区域，强调支持转换页面 --&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">features</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">feature</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">feature-icon</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">🔄</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">h3</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">智能转换</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">h3</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">p</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">保留文档结构, 准确转换为Markdown格式</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">p</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">feature</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">feature-icon</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">🌐</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">h3</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">网页与文档转换</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">h3</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">p</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">支持网页和文档一键转换为Markdown格式</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">p</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">script</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">        </span><span class="token script language-javascript dom variable" style="color:#36acaa">document</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">addEventListener</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'DOMContentLoaded'</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword" style="color:#00009f">function</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 原有文件上传相关元素</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> dropZone </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript dom variable" style="color:#36acaa">document</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">getElementById</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'dropZone'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> fileInput </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript dom variable" style="color:#36acaa">document</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">getElementById</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'fileInput'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> browseBtn </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript dom variable" style="color:#36acaa">document</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">getElementById</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'browseBtn'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> fileInfo </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript dom variable" style="color:#36acaa">document</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">getElementById</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'fileInfo'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> fileName </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript dom variable" style="color:#36acaa">document</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">getElementById</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'fileName'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> fileSize </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript dom variable" style="color:#36acaa">document</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">getElementById</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'fileSize'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 新增URL转换相关元素</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> urlInput </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript dom variable" style="color:#36acaa">document</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">getElementById</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'urlInput'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> convertUrlBtn </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript dom variable" style="color:#36acaa">document</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">getElementById</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'convertUrlBtn'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 状态元素（共用）</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> progressContainer </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript dom variable" style="color:#36acaa">document</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">getElementById</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'progressContainer'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> progressBar </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript dom variable" style="color:#36acaa">document</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">getElementById</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'progressBar'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> processingStatus </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript dom variable" style="color:#36acaa">document</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">getElementById</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'processingStatus'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> successStatus </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript dom variable" style="color:#36acaa">document</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">getElementById</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'successStatus'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> errorStatus </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript dom variable" style="color:#36acaa">document</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">getElementById</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'errorStatus'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 确保所有状态默认隐藏</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            processingStatus</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">style</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">display</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'none'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            successStatus</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">style</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">display</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'none'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            errorStatus</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">style</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">display</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'none'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            progressContainer</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">style</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">display</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'none'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            fileInfo</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">style</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">display</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'none'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// ============= URL转换功能 =============</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// URL转换按钮点击事件</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            convertUrlBtn</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">addEventListener</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'click'</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword" style="color:#00009f">function</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> url </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> urlInput</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">value</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">trim</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">if</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript operator" style="color:#393A34">!</span><span class="token script language-javascript">url</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                    </span><span class="token script language-javascript function" style="color:#d73a49">showError</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'请输入有效的URL地址'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                    </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">return</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 验证URL格式</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">try</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                    </span><span class="token script language-javascript keyword" style="color:#00009f">new</span><span class="token script language-javascript"> </span><span class="token script language-javascript class-name">URL</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">url</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">catch</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">e</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                    </span><span class="token script language-javascript function" style="color:#d73a49">showError</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'请输入有效的URL地址'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                    </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">return</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript function" style="color:#d73a49">convertUrl</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">url</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 处理URL转换请求</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript keyword" style="color:#00009f">function</span><span class="token script language-javascript"> </span><span class="token script language-javascript function" style="color:#d73a49">convertUrl</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript parameter">url</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 重置状态</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                processingStatus</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">style</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">display</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'block'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                successStatus</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">style</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">display</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'none'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                errorStatus</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">style</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">display</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'none'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                progressContainer</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">style</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">display</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'none'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"> </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// URL转换不需要进度条</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> xhr </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword" style="color:#00009f">new</span><span class="token script language-javascript"> </span><span class="token script language-javascript class-name">XMLHttpRequest</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                xhr</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method-variable function-variable method function property-access" style="color:#d73a49">onload</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword" style="color:#00009f">function</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                    </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">if</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">xhr</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">status</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">===</span><span class="token script language-javascript"> </span><span class="token script language-javascript number" style="color:#36acaa">200</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 处理成功响应</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        processingStatus</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">style</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">display</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'none'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        successStatus</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">style</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">display</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'block'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 创建下载链接</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> blob </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword" style="color:#00009f">new</span><span class="token script language-javascript"> </span><span class="token script language-javascript class-name">Blob</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">[</span><span class="token script language-javascript">xhr</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">response</span><span class="token script language-javascript punctuation" style="color:#393A34">]</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"> </span><span class="token script language-javascript literal-property property" style="color:#36acaa">type</span><span class="token script language-javascript operator" style="color:#393A34">:</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'text/markdown'</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> blobUrl </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript dom variable" style="color:#36acaa">window</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript constant" style="color:#36acaa">URL</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">createObjectURL</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">blob</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> a </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript dom variable" style="color:#36acaa">document</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">createElement</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'a'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        a</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">href</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> blobUrl</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 从URL生成文件名</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">try</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                            </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> domain </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword" style="color:#00009f">new</span><span class="token script language-javascript"> </span><span class="token script language-javascript class-name">URL</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">url</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">hostname</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                            </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 清理域名中的特殊字符</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                            </span><span class="token script language-javascript keyword" style="color:#00009f">let</span><span class="token script language-javascript"> safeName </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> domain</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">replace</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript regex regex-delimiter" style="color:#36acaa">/</span><span class="token script language-javascript regex regex-source language-regex char-class char-class-punctuation punctuation" style="color:#393A34">[</span><span class="token script language-javascript regex regex-source language-regex char-class char-class-negation operator" style="color:#393A34">^</span><span class="token script language-javascript regex regex-source language-regex char-class range" style="color:#36acaa">a</span><span class="token script language-javascript regex regex-source language-regex char-class range range-punctuation operator" style="color:#393A34">-</span><span class="token script language-javascript regex regex-source language-regex char-class range" style="color:#36acaa">z</span><span class="token script language-javascript regex regex-source language-regex char-class range" style="color:#36acaa">A</span><span class="token script language-javascript regex regex-source language-regex char-class range range-punctuation operator" style="color:#393A34">-</span><span class="token script language-javascript regex regex-source language-regex char-class range" style="color:#36acaa">Z</span><span class="token script language-javascript regex regex-source language-regex char-class range" style="color:#36acaa">0</span><span class="token script language-javascript regex regex-source language-regex char-class range range-punctuation operator" style="color:#393A34">-</span><span class="token script language-javascript regex regex-source language-regex char-class range" style="color:#36acaa">9</span><span class="token script language-javascript regex regex-source language-regex char-class range escape" style="color:#36acaa">\u4e00</span><span class="token script language-javascript regex regex-source language-regex char-class range range-punctuation operator" style="color:#393A34">-</span><span class="token script language-javascript regex regex-source language-regex char-class range escape" style="color:#36acaa">\u9fa5</span><span class="token script language-javascript regex regex-source language-regex char-class char-class-punctuation punctuation" style="color:#393A34">]</span><span class="token script language-javascript regex regex-delimiter" style="color:#36acaa">/</span><span class="token script language-javascript regex regex-flags" style="color:#36acaa">g</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">''</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                            </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 如果清理后为空，使用默认名称</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                            </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">if</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript operator" style="color:#393A34">!</span><span class="token script language-javascript">safeName</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> safeName </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'webpage'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                            a</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">download</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> safeName </span><span class="token script language-javascript operator" style="color:#393A34">+</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'.md'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">catch</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">e</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                            a</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">download</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'webpage.md'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript dom variable" style="color:#36acaa">document</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">body</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">appendChild</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">a</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        a</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">click</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 清理</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript dom variable" style="color:#36acaa">window</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript constant" style="color:#36acaa">URL</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">revokeObjectURL</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">blobUrl</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript dom variable" style="color:#36acaa">document</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">body</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">removeChild</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">a</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                    </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">else</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        processingStatus</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">style</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">display</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'none'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">try</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                            </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> error </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript known-class-name class-name">JSON</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">parse</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">xhr</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">responseText</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                            </span><span class="token script language-javascript function" style="color:#d73a49">showError</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">error</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">detail</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">||</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'转换失败'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">catch</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">e</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                            </span><span class="token script language-javascript function" style="color:#d73a49">showError</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'服务器错误'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                    </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                xhr</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method-variable function-variable method function property-access" style="color:#d73a49">onerror</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword" style="color:#00009f">function</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                    processingStatus</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">style</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">display</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'none'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                    </span><span class="token script language-javascript function" style="color:#d73a49">showError</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'网络错误'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 发送请求到 /convertPage/ 接口</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                xhr</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">open</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'POST'</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> </span><span class="token script language-javascript template-string template-punctuation string" style="color:#e3116c">`</span><span class="token script language-javascript template-string string" style="color:#e3116c">/convertPage/?url=</span><span class="token script language-javascript template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token script language-javascript template-string interpolation function" style="color:#d73a49">encodeURIComponent</span><span class="token script language-javascript template-string interpolation punctuation" style="color:#393A34">(</span><span class="token script language-javascript template-string interpolation">url</span><span class="token script language-javascript template-string interpolation punctuation" style="color:#393A34">)</span><span class="token script language-javascript template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token script language-javascript template-string template-punctuation string" style="color:#e3116c">`</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                xhr</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">responseType</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'blob'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"> </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 重要：接收二进制响应</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                xhr</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">send</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// ============= 原有文件上传功能 =============</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 点击区域选择文件</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            dropZone</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">addEventListener</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'click'</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword" style="color:#00009f">function</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript parameter">e</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">if</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">e</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">target</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">!==</span><span class="token script language-javascript"> fileInput</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                    fileInput</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">click</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 选择文件按钮</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            browseBtn</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">addEventListener</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'click'</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword" style="color:#00009f">function</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript parameter">e</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                e</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">stopPropagation</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                fileInput</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">click</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 文件选择事件</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            fileInput</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">addEventListener</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'change'</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword" style="color:#00009f">function</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">if</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">fileInput</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">files</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">length</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                    </span><span class="token script language-javascript function" style="color:#d73a49">handleFile</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">fileInput</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">files</span><span class="token script language-javascript punctuation" style="color:#393A34">[</span><span class="token script language-javascript number" style="color:#36acaa">0</span><span class="token script language-javascript punctuation" style="color:#393A34">]</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 拖拽事件</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript punctuation" style="color:#393A34">[</span><span class="token script language-javascript string" style="color:#e3116c">'dragenter'</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'dragover'</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'dragleave'</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'drop'</span><span class="token script language-javascript punctuation" style="color:#393A34">]</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">forEach</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript parameter">eventName</span><span class="token script language-javascript"> </span><span class="token script language-javascript arrow operator" style="color:#393A34">=&gt;</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                dropZone</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">addEventListener</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">eventName</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> preventDefaults</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> </span><span class="token script language-javascript boolean" style="color:#36acaa">false</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript keyword" style="color:#00009f">function</span><span class="token script language-javascript"> </span><span class="token script language-javascript function" style="color:#d73a49">preventDefaults</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript parameter">e</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                e</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">preventDefault</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                e</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">stopPropagation</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript punctuation" style="color:#393A34">[</span><span class="token script language-javascript string" style="color:#e3116c">'dragenter'</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'dragover'</span><span class="token script language-javascript punctuation" style="color:#393A34">]</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">forEach</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript parameter">eventName</span><span class="token script language-javascript"> </span><span class="token script language-javascript arrow operator" style="color:#393A34">=&gt;</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                dropZone</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">addEventListener</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">eventName</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> highlight</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> </span><span class="token script language-javascript boolean" style="color:#36acaa">false</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript punctuation" style="color:#393A34">[</span><span class="token script language-javascript string" style="color:#e3116c">'dragleave'</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'drop'</span><span class="token script language-javascript punctuation" style="color:#393A34">]</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">forEach</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript parameter">eventName</span><span class="token script language-javascript"> </span><span class="token script language-javascript arrow operator" style="color:#393A34">=&gt;</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                dropZone</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">addEventListener</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">eventName</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> unhighlight</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> </span><span class="token script language-javascript boolean" style="color:#36acaa">false</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript keyword" style="color:#00009f">function</span><span class="token script language-javascript"> </span><span class="token script language-javascript function" style="color:#d73a49">highlight</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                dropZone</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">classList</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">add</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'dragover'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript keyword" style="color:#00009f">function</span><span class="token script language-javascript"> </span><span class="token script language-javascript function" style="color:#d73a49">unhighlight</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                dropZone</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">classList</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">remove</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'dragover'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 处理拖放的文件</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            dropZone</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">addEventListener</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'drop'</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword" style="color:#00009f">function</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript parameter">e</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> dt </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> e</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">dataTransfer</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> files </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> dt</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">files</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">if</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">files</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">length</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                    </span><span class="token script language-javascript function" style="color:#d73a49">handleFile</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">files</span><span class="token script language-javascript punctuation" style="color:#393A34">[</span><span class="token script language-javascript number" style="color:#36acaa">0</span><span class="token script language-javascript punctuation" style="color:#393A34">]</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 处理文件</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript keyword" style="color:#00009f">function</span><span class="token script language-javascript"> </span><span class="token script language-javascript function" style="color:#d73a49">handleFile</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript parameter">file</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 验证文件大小 (50MB)</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> maxSize </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript number" style="color:#36acaa">50</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">*</span><span class="token script language-javascript"> </span><span class="token script language-javascript number" style="color:#36acaa">1024</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">*</span><span class="token script language-javascript"> </span><span class="token script language-javascript number" style="color:#36acaa">1024</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"> </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 50MB</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">if</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">file</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">size</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">&gt;</span><span class="token script language-javascript"> maxSize</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                    </span><span class="token script language-javascript function" style="color:#d73a49">showError</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript template-string template-punctuation string" style="color:#e3116c">`</span><span class="token script language-javascript template-string string" style="color:#e3116c">文件过大（最大支持</span><span class="token script language-javascript template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token script language-javascript template-string interpolation">maxSize </span><span class="token script language-javascript template-string interpolation operator" style="color:#393A34">/</span><span class="token script language-javascript template-string interpolation"> </span><span class="token script language-javascript template-string interpolation number" style="color:#36acaa">1024</span><span class="token script language-javascript template-string interpolation"> </span><span class="token script language-javascript template-string interpolation operator" style="color:#393A34">/</span><span class="token script language-javascript template-string interpolation"> </span><span class="token script language-javascript template-string interpolation number" style="color:#36acaa">1024</span><span class="token script language-javascript template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token script language-javascript template-string string" style="color:#e3116c">MB）</span><span class="token script language-javascript template-string template-punctuation string" style="color:#e3116c">`</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                    </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">return</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 显示文件信息</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                fileName</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">textContent</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> file</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">name</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                fileSize</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">textContent</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript function" style="color:#d73a49">formatFileSize</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">file</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">size</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                fileInfo</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">style</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">display</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'block'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 上传文件</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript function" style="color:#d73a49">uploadFile</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">file</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 上传文件</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript keyword" style="color:#00009f">function</span><span class="token script language-javascript"> </span><span class="token script language-javascript function" style="color:#d73a49">uploadFile</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript parameter">file</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> formData </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword" style="color:#00009f">new</span><span class="token script language-javascript"> </span><span class="token script language-javascript class-name">FormData</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                formData</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">append</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'file'</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> file</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 重置状态</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                processingStatus</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">style</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">display</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'block'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                successStatus</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">style</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">display</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'none'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                errorStatus</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">style</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">display</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'none'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                progressContainer</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">style</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">display</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'block'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                progressBar</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">style</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">width</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'0%'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> xhr </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword" style="color:#00009f">new</span><span class="token script language-javascript"> </span><span class="token script language-javascript class-name">XMLHttpRequest</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 进度事件</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                xhr</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">upload</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method-variable function-variable method function property-access" style="color:#d73a49">onprogress</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword" style="color:#00009f">function</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript parameter">e</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                    </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">if</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">e</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">lengthComputable</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> percentComplete </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">e</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">loaded</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">/</span><span class="token script language-javascript"> e</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">total</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">*</span><span class="token script language-javascript"> </span><span class="token script language-javascript number" style="color:#36acaa">100</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        progressBar</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">style</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">width</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> percentComplete </span><span class="token script language-javascript operator" style="color:#393A34">+</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'%'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                    </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                xhr</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method-variable function-variable method function property-access" style="color:#d73a49">onload</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword" style="color:#00009f">function</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                    progressContainer</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">style</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">display</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'none'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                    </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">if</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">xhr</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">status</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">===</span><span class="token script language-javascript"> </span><span class="token script language-javascript number" style="color:#36acaa">200</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 处理成功响应</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        processingStatus</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">style</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">display</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'none'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        successStatus</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">style</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">display</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'block'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 创建下载链接</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> blob </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword" style="color:#00009f">new</span><span class="token script language-javascript"> </span><span class="token script language-javascript class-name">Blob</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">[</span><span class="token script language-javascript">xhr</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">response</span><span class="token script language-javascript punctuation" style="color:#393A34">]</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"> </span><span class="token script language-javascript literal-property property" style="color:#36acaa">type</span><span class="token script language-javascript operator" style="color:#393A34">:</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'text/markdown'</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> url </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript dom variable" style="color:#36acaa">window</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript constant" style="color:#36acaa">URL</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">createObjectURL</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">blob</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> a </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript dom variable" style="color:#36acaa">document</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">createElement</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'a'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        a</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">href</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> url</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 关键修复：确保所有文档类型都以.md结尾</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 1. 移除原始文件名中的特殊字符</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript keyword" style="color:#00009f">let</span><span class="token script language-javascript"> safeName </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> file</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">name</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">replace</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript regex regex-delimiter" style="color:#36acaa">/</span><span class="token script language-javascript regex regex-source language-regex char-class char-class-punctuation punctuation" style="color:#393A34">[</span><span class="token script language-javascript regex regex-source language-regex char-class char-class-negation operator" style="color:#393A34">^</span><span class="token script language-javascript regex regex-source language-regex char-class range" style="color:#36acaa">a</span><span class="token script language-javascript regex regex-source language-regex char-class range range-punctuation operator" style="color:#393A34">-</span><span class="token script language-javascript regex regex-source language-regex char-class range" style="color:#36acaa">z</span><span class="token script language-javascript regex regex-source language-regex char-class range" style="color:#36acaa">A</span><span class="token script language-javascript regex regex-source language-regex char-class range range-punctuation operator" style="color:#393A34">-</span><span class="token script language-javascript regex regex-source language-regex char-class range" style="color:#36acaa">Z</span><span class="token script language-javascript regex regex-source language-regex char-class range" style="color:#36acaa">0</span><span class="token script language-javascript regex regex-source language-regex char-class range range-punctuation operator" style="color:#393A34">-</span><span class="token script language-javascript regex regex-source language-regex char-class range" style="color:#36acaa">9</span><span class="token script language-javascript regex regex-source language-regex char-class range escape" style="color:#36acaa">\u4e00</span><span class="token script language-javascript regex regex-source language-regex char-class range range-punctuation operator" style="color:#393A34">-</span><span class="token script language-javascript regex regex-source language-regex char-class range escape" style="color:#36acaa">\u9fa5</span><span class="token script language-javascript regex regex-source language-regex char-class" style="color:#36acaa">._-</span><span class="token script language-javascript regex regex-source language-regex char-class char-class-punctuation punctuation" style="color:#393A34">]</span><span class="token script language-javascript regex regex-delimiter" style="color:#36acaa">/</span><span class="token script language-javascript regex regex-flags" style="color:#36acaa">g</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">''</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 2. 移除任何现有扩展名</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        safeName </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> safeName</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">replace</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript regex regex-delimiter" style="color:#36acaa">/</span><span class="token script language-javascript regex regex-source language-regex special-escape escape" style="color:#36acaa">\.</span><span class="token script language-javascript regex regex-source language-regex char-class char-class-punctuation punctuation" style="color:#393A34">[</span><span class="token script language-javascript regex regex-source language-regex char-class char-class-negation operator" style="color:#393A34">^</span><span class="token script language-javascript regex regex-source language-regex char-class" style="color:#36acaa">/.</span><span class="token script language-javascript regex regex-source language-regex char-class char-class-punctuation punctuation" style="color:#393A34">]</span><span class="token script language-javascript regex regex-source language-regex quantifier number" style="color:#36acaa">+</span><span class="token script language-javascript regex regex-source language-regex anchor function" style="color:#d73a49">$</span><span class="token script language-javascript regex regex-delimiter" style="color:#36acaa">/</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">""</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 3. 添加.md扩展名</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        safeName </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> safeName </span><span class="token script language-javascript operator" style="color:#393A34">+</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'.md'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        a</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">download</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> safeName</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript dom variable" style="color:#36acaa">document</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">body</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">appendChild</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">a</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        a</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">click</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 清理</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript dom variable" style="color:#36acaa">window</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript constant" style="color:#36acaa">URL</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">revokeObjectURL</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">url</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript dom variable" style="color:#36acaa">document</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">body</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">removeChild</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">a</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                    </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">else</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        processingStatus</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">style</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">display</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'none'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">try</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                            </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> error </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript known-class-name class-name">JSON</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">parse</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">xhr</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">responseText</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                            </span><span class="token script language-javascript function" style="color:#d73a49">showError</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">error</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">detail</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">||</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'转换失败'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">catch</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">e</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                            </span><span class="token script language-javascript function" style="color:#d73a49">showError</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'服务器错误'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                        </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                    </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                xhr</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method-variable function-variable method function property-access" style="color:#d73a49">onerror</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword" style="color:#00009f">function</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                    progressContainer</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">style</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">display</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'none'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                    processingStatus</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">style</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">display</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'none'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                    </span><span class="token script language-javascript function" style="color:#d73a49">showError</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'网络错误'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 发送请求</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                xhr</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">open</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'POST'</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'/uploadfile/'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                xhr</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">responseType</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'blob'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"> </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 重要：接收二进制响应</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                xhr</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">send</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">formData</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 显示错误</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript keyword" style="color:#00009f">function</span><span class="token script language-javascript"> </span><span class="token script language-javascript function" style="color:#d73a49">showError</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript parameter">message</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                errorStatus</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">textContent</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'转换失败: '</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">+</span><span class="token script language-javascript"> message</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                errorStatus</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">style</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">display</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'block'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 5秒后自动隐藏错误</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript function" style="color:#d73a49">setTimeout</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript arrow operator" style="color:#393A34">=&gt;</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                    errorStatus</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">style</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">display</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'none'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> </span><span class="token script language-javascript number" style="color:#36acaa">5000</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript comment" style="color:#999988;font-style:italic">// 格式化文件大小</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript keyword" style="color:#00009f">function</span><span class="token script language-javascript"> </span><span class="token script language-javascript function" style="color:#d73a49">formatFileSize</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript parameter">bytes</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">if</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">bytes </span><span class="token script language-javascript operator" style="color:#393A34">===</span><span class="token script language-javascript"> </span><span class="token script language-javascript number" style="color:#36acaa">0</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">return</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'0 Bytes'</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> k </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript number" style="color:#36acaa">1024</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> sizes </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">[</span><span class="token script language-javascript string" style="color:#e3116c">'Bytes'</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'KB'</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'MB'</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'GB'</span><span class="token script language-javascript punctuation" style="color:#393A34">]</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript keyword" style="color:#00009f">const</span><span class="token script language-javascript"> i </span><span class="token script language-javascript operator" style="color:#393A34">=</span><span class="token script language-javascript"> </span><span class="token script language-javascript known-class-name class-name">Math</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">floor</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript known-class-name class-name">Math</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">log</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">bytes</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">/</span><span class="token script language-javascript"> </span><span class="token script language-javascript known-class-name class-name">Math</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">log</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">k</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">                </span><span class="token script language-javascript keyword control-flow" style="color:#00009f">return</span><span class="token script language-javascript"> </span><span class="token script language-javascript function" style="color:#d73a49">parseFloat</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">bytes </span><span class="token script language-javascript operator" style="color:#393A34">/</span><span class="token script language-javascript"> </span><span class="token script language-javascript known-class-name class-name">Math</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">pow</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript">k</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"> i</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">toFixed</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript number" style="color:#36acaa">2</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">+</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">' '</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">+</span><span class="token script language-javascript"> sizes</span><span class="token script language-javascript punctuation" style="color:#393A34">[</span><span class="token script language-javascript">i</span><span class="token script language-javascript punctuation" style="color:#393A34">]</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">            </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">        </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">script</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">body</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">html</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre></div></div></div></div></details>
<p>简单起见, 就不用 nginx 部署前端页面了, 直接挂到 FastAPI 服务上</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">main.py</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">app</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">mount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"/static"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> StaticFiles</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">directory</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"static"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> name</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"static"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token decorator annotation punctuation" style="color:#393A34">@app</span><span class="token decorator annotation punctuation" style="color:#393A34">.</span><span class="token decorator annotation punctuation" style="color:#393A34">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"/"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">read_root</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">request</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Request</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> Response</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        content</span><span class="token operator" style="color:#393A34">=</span><span class="token builtin">open</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"static/index.html"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"r"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> encoding</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"utf-8"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">read</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        media_type</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"text/html"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>在浏览器访问 <a href="http://localhost:8000/" target="_blank" rel="noopener noreferrer">localhost:8000</a></p>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-09-10-14-55-25-d20184d810db48b26c0227ee0fcb4225.png" width="2548" height="1362" class="img_CujE"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="docker-image">打包成 Docker 镜像<a href="https://blog.lintech1024.com/markitdown-project#docker-image" class="hash-link" aria-label="打包成 Docker 镜像的直接链接" title="打包成 Docker 镜像的直接链接">​</a></h3>
<details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary><p>Dockerfile 配置</p></summary><div><div class="collapsibleContent_i85q"><div class="language-dockerfile codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">Dockerfile</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-dockerfile codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token instruction keyword" style="color:#00009f">FROM</span><span class="token instruction"> python:3.12-slim-bullseye</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token instruction keyword" style="color:#00009f">ENV</span><span class="token instruction"> DEBIAN_FRONTEND=noninteractive</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token instruction keyword" style="color:#00009f">ENV</span><span class="token instruction"> FFMPEG_PATH=/usr/bin/ffmpeg</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token instruction keyword" style="color:#00009f">ENV</span><span class="token instruction"> PIP_INDEX_URL=https://mirrors.aliyun.com/pypi/simple/ </span><span class="token instruction operator" style="color:#393A34">\</span><span class="token instruction"></span><br></span><span class="token-line" style="color:#393A34"><span class="token instruction">    PIP_TRUSTED_HOST=mirrors.aliyun.com </span><span class="token instruction operator" style="color:#393A34">\</span><span class="token instruction"></span><br></span><span class="token-line" style="color:#393A34"><span class="token instruction">    PYTHONUNBUFFERED=1 </span><span class="token instruction operator" style="color:#393A34">\</span><span class="token instruction"></span><br></span><span class="token-line" style="color:#393A34"><span class="token instruction">    PYTHONDONTWRITEBYTECODE=1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token instruction keyword" style="color:#00009f">RUN</span><span class="token instruction"> sed -i </span><span class="token instruction string" style="color:#e3116c">"s@deb.debian.org@mirrors.aliyun.com@g"</span><span class="token instruction"> /etc/apt/sources.list</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Runtime dependency</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token instruction keyword" style="color:#00009f">RUN</span><span class="token instruction"> apt-get update &amp;&amp; apt-get upgrade -y &amp;&amp; apt-get install -y --no-install-recommends ffmpeg</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Cleanup</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token instruction keyword" style="color:#00009f">RUN</span><span class="token instruction"> rm -rf /var/lib/apt/lists/*</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token instruction keyword" style="color:#00009f">WORKDIR</span><span class="token instruction"> /app</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token instruction keyword" style="color:#00009f">COPY</span><span class="token instruction"> . /app</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token instruction keyword" style="color:#00009f">RUN</span><span class="token instruction"> pip --no-cache-dir install markitdown[all] fastapi uvicorn[standard] python-multipart</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Default USERID and GROUPID</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token instruction keyword" style="color:#00009f">ARG</span><span class="token instruction"> USERID=nobody</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token instruction keyword" style="color:#00009f">ARG</span><span class="token instruction"> GROUPID=nogroup</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token instruction keyword" style="color:#00009f">USER</span><span class="token instruction"> </span><span class="token instruction variable" style="color:#36acaa">$USERID</span><span class="token instruction">:</span><span class="token instruction variable" style="color:#36acaa">$GROUPID</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token instruction keyword" style="color:#00009f">ENTRYPOINT</span><span class="token instruction"> [ </span><span class="token instruction string" style="color:#e3116c">"uvicorn"</span><span class="token instruction">, </span><span class="token instruction string" style="color:#e3116c">"main:app"</span><span class="token instruction">, </span><span class="token instruction string" style="color:#e3116c">"--host"</span><span class="token instruction">, </span><span class="token instruction string" style="color:#e3116c">"0.0.0.0"</span><span class="token instruction">, </span><span class="token instruction string" style="color:#e3116c">"--port"</span><span class="token instruction">, </span><span class="token instruction string" style="color:#e3116c">"8000"</span><span class="token instruction">]</span><br></span></code></pre></div></div></div></div></details>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> build </span><span class="token builtin class-name">.</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-t</span><span class="token plain"> linrepo/markitdown-server:latest</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="docker-run">Docker 运行服务<a href="https://blog.lintech1024.com/markitdown-project#docker-run" class="hash-link" aria-label="Docker 运行服务的直接链接" title="Docker 运行服务的直接链接">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> run </span><span class="token parameter variable" style="color:#36acaa">-it</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">--rm</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-p</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8000</span><span class="token plain">:8000 linrepo/markitdown-server</span><br></span></code></pre></div></div>
<p>在浏览器访问 <a href="http://localhost:8000/" target="_blank" rel="noopener noreferrer">localhost:8000</a></p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="evaluate">评价<a href="https://blog.lintech1024.com/markitdown-project#evaluate" class="hash-link" aria-label="评价的直接链接" title="评价的直接链接">​</a></h2>
<p>MarkItDown 现在还处于 <code>v0.1.3</code> 版本, 项目不算成熟, 转换效果只能说属于能用, 期待后续继续迭代吧</p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="ref">参考<a href="https://blog.lintech1024.com/markitdown-project#ref" class="hash-link" aria-label="参考的直接链接" title="参考的直接链接">​</a></h2>
<ul>
<li><a href="https://github.com/microsoft/markitdown" target="_blank" rel="noopener noreferrer">microsoft/markitdown</a></li>
<li><a href="https://fastapi.tiangolo.com/tutorial/request-files/" target="_blank" rel="noopener noreferrer">FastAPI Request Files</a></li>
<li><a href="https://fastapi.tiangolo.com/tutorial/static-files/" target="_blank" rel="noopener noreferrer">FastAPI Static Files</a></li>
</ul>]]></content:encoded>
            <author>lintech1024@hotmail.com (小林)</author>
            <category>开源项目</category>
            <category>Python</category>
        </item>
        <item>
            <title><![CDATA[Git 设置代理]]></title>
            <link>https://blog.lintech1024.com/git-proxy</link>
            <guid>https://blog.lintech1024.com/git-proxy</guid>
            <pubDate>Wed, 10 Sep 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[用了这么久 Git 不知道使用代理需要主动设置, 直到今天死活推不上代码到 GitHub 😂]]></description>
            <content:encoded><![CDATA[<p>用了这么久 Git 不知道使用代理需要主动设置, 直到今天死活推不上代码到 GitHub 😂</p>
<p><img decoding="async" loading="lazy" alt="princess.png" src="https://blog.lintech1024.com/assets/images/princess-fede6fca8037828399e392d80ebca066.png" width="345" height="602" class="img_CujE"></p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="add-global-proxy">添加全局代理<a href="https://blog.lintech1024.com/git-proxy#add-global-proxy" class="hash-link" aria-label="添加全局代理的直接链接" title="添加全局代理的直接链接">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">git</span><span class="token plain"> config </span><span class="token parameter variable" style="color:#36acaa">--global</span><span class="token plain"> http.proxy </span><span class="token string" style="color:#e3116c">"http://127.0.0.1:7890"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">git</span><span class="token plain"> config </span><span class="token parameter variable" style="color:#36acaa">--global</span><span class="token plain"> https.proxy </span><span class="token string" style="color:#e3116c">"http://127.0.0.1:7890"</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>提示</div><div class="admonitionContent_BuS1"><p>Windows 系统用双引号, Linux 系统要用单引号</p></div></div>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="get-global-proxy">验证代理设置<a href="https://blog.lintech1024.com/git-proxy#get-global-proxy" class="hash-link" aria-label="验证代理设置的直接链接" title="验证代理设置的直接链接">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">git</span><span class="token plain"> config </span><span class="token parameter variable" style="color:#36acaa">--global</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">--get</span><span class="token plain"> http.proxy</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">git</span><span class="token plain"> config </span><span class="token parameter variable" style="color:#36acaa">--global</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">--get</span><span class="token plain"> https.proxy</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="unset-global-proxy">取消代理<a href="https://blog.lintech1024.com/git-proxy#unset-global-proxy" class="hash-link" aria-label="取消代理的直接链接" title="取消代理的直接链接">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">git</span><span class="token plain"> config </span><span class="token parameter variable" style="color:#36acaa">--global</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">--unset</span><span class="token plain"> http.proxy</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">git</span><span class="token plain"> config </span><span class="token parameter variable" style="color:#36acaa">--global</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">--unset</span><span class="token plain"> https.proxy</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="unset-proxy">临时绕过代理<a href="https://blog.lintech1024.com/git-proxy#unset-proxy" class="hash-link" aria-label="临时绕过代理的直接链接" title="临时绕过代理的直接链接">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token builtin class-name">unset</span><span class="token plain"> http_proxy</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token builtin class-name">unset</span><span class="token plain"> https_proxy</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="tmp-proxy">在命令中临时指定代理<a href="https://blog.lintech1024.com/git-proxy#tmp-proxy" class="hash-link" aria-label="在命令中临时指定代理的直接链接" title="在命令中临时指定代理的直接链接">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">git</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-c</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">http.proxy</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"http://127.0.0.1:7890"</span><span class="token plain"> clone https://github.com/user/repo.git</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="ref">参考<a href="https://blog.lintech1024.com/git-proxy#ref" class="hash-link" aria-label="参考的直接链接" title="参考的直接链接">​</a></h2>
<ul>
<li><a href="https://blog.csdn.net/qq_51436687/article/details/138586607" target="_blank" rel="noopener noreferrer">Git 设置代理</a></li>
</ul>]]></content:encoded>
            <author>lintech1024@hotmail.com (小林)</author>
            <category>Git</category>
        </item>
        <item>
            <title><![CDATA[Dev Container 教程 (二) 使用预构建加速流程]]></title>
            <link>https://blog.lintech1024.com/devcontainer-prebuild</link>
            <guid>https://blog.lintech1024.com/devcontainer-prebuild</guid>
            <pubDate>Tue, 09 Sep 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[我在之前的文章介绍了 Dev Container 的基本使用, 但容器构建耗时过长的问题还未探讨, 本文将通过预构建(prebuild)技术, 彻底解决这一痛点]]></description>
            <content:encoded><![CDATA[<p>我在之前的文章介绍了 Dev Container 的基本使用, 但容器构建耗时过长的问题还未探讨, 本文将通过<strong>预构建</strong>(<strong>prebuild</strong>)技术, 彻底解决这一痛点</p>
<p><img decoding="async" loading="lazy" alt="messenger-showing.png" src="https://blog.lintech1024.com/assets/images/messenger-showing-557d93b168107729dd4d0ad8046eb87c.png" width="694" height="514" class="img_CujE"></p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="pre">前置条件<a href="https://blog.lintech1024.com/devcontainer-prebuild#pre" class="hash-link" aria-label="前置条件的直接链接" title="前置条件的直接链接">​</a></h2>
<ul>
<li>已阅读 <a href="https://blog.lintech1024.com/devcontainer-tutorial">Dev Container 教程 (一) 基本使用</a></li>
<li>已安装 Node.js 环境(包含 npm 包管理工具), <a href="https://nodejs.org/en/download" target="_blank" rel="noopener noreferrer">官网下载地址</a></li>
</ul>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="simple-demo">简单示例<a href="https://blog.lintech1024.com/devcontainer-prebuild#simple-demo" class="hash-link" aria-label="简单示例的直接链接" title="简单示例的直接链接">​</a></h2>
<p>下面以 Go 开发环境为例, 演示如何配置一个基本的 Dev Container</p>
<p>新建 go-prebuild 目录并添加下面文件</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">.devcontainer/devcontainer.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Go"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"image"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"mcr.microsoft.com/devcontainers/go:1-1.24-bookworm"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"features"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token property" style="color:#36acaa">"ghcr.io/mrsimonemms/devcontainers/cobra-cli:1"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"customizations"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token property" style="color:#36acaa">"vscode"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"settings"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"window.newWindowProfile"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Default"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 新窗口使用的配置文件, 只能在"默认配置文件"中设置</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"files.autoSave"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"afterDelay"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 自动保存文件</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"editor.formatOnSave"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 保存时自动格式化</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"files.simpleDialog.enable"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 简单对话框, ctrl+n 新建文件时不再弹出对话框</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"editor.insertSpaces"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 使用空格代替制表符</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"editor.tabSize"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 缩进空格数</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"editor.autoClosingDelete"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"always"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 删除左括号时总是自动删除右括号</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"editor.minimap.autohide"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"mouseover"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 鼠标不在缩略图上时隐藏缩略图, 鼠标在缩略图上时显示缩略图</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"terminal.integrated.cursorStyle"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"underline"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 终端光标样式使用下划线</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"explorer.confirmDelete"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 删除文件时不再弹出确认对话框</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"explorer.confirmDragAndDrop"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 拖放移动文件或文件夹时不再弹出确认对话框</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"vsicons.dontShowNewVersionMessage"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 不显示新版本消息</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"workbench.iconTheme"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"vscode-icons"</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 使用 vscode-icons 图标主题</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"extensions"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"mhutchie.git-graph"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Git 图形化视图</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"eamodio.gitlens"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Git 增强工具, 主要为了能显示代码作者</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"vscode-icons-team.vscode-icons"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// VSCode 图标主题</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"github.copilot"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// GitHub Copilot, AI 代码补全</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"github.copilot-chat"</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// GitHub Copilot Chat, AI 聊天助手</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"containerEnv"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token property" style="color:#36acaa">"GOPROXY"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"https://goproxy.io,direct"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"runArgs"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"--name"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"go-container"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>关键参数解析</p>
<ul>
<li><code>features</code>: 配置添加功能模块, 这里添加Go命令行工具 <a href="https://cobra.dev/" target="_blank" rel="noopener noreferrer">cobra-cli</a></li>
<li><code>customizations</code>: 个性化配置, 这里提供我的VSCocde设置和插件以供参考</li>
<li><code>containerEnv</code>: 设置环境变量, 这里通过环境变量设置Go镜像源</li>
<li><code>runArgs</code>: 传递给 Docker CLI 的参数, 这里给启动容器命名为 <code>go-container</code>, 方便查找</li>
</ul>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="features">Features<a href="https://blog.lintech1024.com/devcontainer-prebuild#features" class="hash-link" aria-label="Features的直接链接" title="Features的直接链接">​</a></h3>
<p>Features 是 Dev Container 一项比较强大的能力, 它将常见的开发环境配置封装为可复用的模块, 使环境配置变得简单高效</p>
<p>Features 核心优势</p>
<ol>
<li>模块化设计</li>
</ol>
<ul>
<li>将常见开发环境配置拆分为独立的、可组合的功能单元</li>
<li>每个 Feature 专注于单一职责(如安装特定语言运行时、配置工具链等)</li>
<li>可根据项目需求灵活组合不同 Features</li>
</ul>
<ol start="2">
<li>简化配置</li>
</ol>
<ul>
<li>无需再编写复杂的 Dockerfile 或处理繁琐的安装脚本</li>
<li>通过简单的声明式语法在 devcontainer.json 中添加功能</li>
</ul>
<p>在 VSCode 中，可以通过可视化界面轻松添加 Features:</p>
<p><img decoding="async" loading="lazy" alt="feature-devcontainer.gif" src="https://blog.lintech1024.com/assets/images/feature-devcontainer-ec384d9a009ad503fc17c3b30b9f5fb8.gif" width="2560" height="1380" class="img_CujE"></p>
<p>官方和社区提供了很多常用的 Features, 具体可以参考 <a href="https://containers.dev/features" target="_blank" rel="noopener noreferrer">Available Dev Container Features</a></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="add-extensions">添加插件<a href="https://blog.lintech1024.com/devcontainer-prebuild#add-extensions" class="hash-link" aria-label="添加插件的直接链接" title="添加插件的直接链接">​</a></h3>
<p>Dev Container 支持在容器内预装 VS Code 插件, 确保开发环境开箱即用. 配置插件有两种方式:</p>
<ol>
<li>手动添加: 将插件 ID 添加到 devcontainer.json 的 extensions 数组中</li>
<li>可视化操作: 在 VSCode 界面中管理插件</li>
</ol>
<p><img decoding="async" loading="lazy" alt="extensions-devcontainer.gif" src="https://blog.lintech1024.com/assets/images/extensions-devcontainer-f6f270a45a15e7e7e2d3a09b3824126a.gif" width="2558" height="1380" class="img_CujE"></p>
<p>推荐实践: 对于团队项目, 建议在 devcontainer.json 中明确指定必要插件, 确保所有成员使用一致的开发环境</p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="prebuild">预构建<a href="https://blog.lintech1024.com/devcontainer-prebuild#prebuild" class="hash-link" aria-label="预构建的直接链接" title="预构建的直接链接">​</a></h2>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="install-devcontainers-cli">安装 Dev Container CLI<a href="https://blog.lintech1024.com/devcontainer-prebuild#install-devcontainers-cli" class="hash-link" aria-label="安装 Dev Container CLI的直接链接" title="安装 Dev Container CLI的直接链接">​</a></h3>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-g</span><span class="token plain"> @devcontainers/cli</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="build-container">构建自定义镜像<a href="https://blog.lintech1024.com/devcontainer-prebuild#build-container" class="hash-link" aria-label="构建自定义镜像的直接链接" title="构建自定义镜像的直接链接">​</a></h3>
<p>在项目目录执行以下命令构建镜像:</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">devcontainer build --workspace-folder </span><span class="token builtin class-name">.</span><span class="token plain"> --image-name go-devcontainer:0.1</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-09-09-17-44-22-6ac82d6e44e04e89be5e351e67c87f60.png" width="2534" height="684" class="img_CujE"></p>
<p>构建成功后, 您将获得一个包含完整开发环境的 Docker 镜像, 可直接用于创建 Dev Container</p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="use-prebuild-image">使用预构建镜像<a href="https://blog.lintech1024.com/devcontainer-prebuild#use-prebuild-image" class="hash-link" aria-label="使用预构建镜像的直接链接" title="使用预构建镜像的直接链接">​</a></h3>
<p>新建 go-demo 目录, 创建 devcontainer.json 文件</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">.devcontainer/devcontainer.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"go-demo"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"image"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"go-devcontainer:0.1"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>通过 <strong>Ctrl + Shift + P</strong> 打开命令面板, 输入 "Reopen in Container" 并执行, VSCode 将使用预构建镜像启动开发环境</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-09-09-18-18-16-4748b2882e837e1c06cebd88d91528c2.png" width="1859" height="568" class="img_CujE"></p>
<p>静待 VSCode 启动 VSCode Server, 下载并激活必要的插件, 接下来就可以愉快的开发了</p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="docker-push">镜像分发策略<a href="https://blog.lintech1024.com/devcontainer-prebuild#docker-push" class="hash-link" aria-label="镜像分发策略的直接链接" title="镜像分发策略的直接链接">​</a></h3>
<p>预构建的镜像可推送到私有或公共镜像仓库, 解决网络环境限制问题:</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> tag go-devcontainer:0.1 registry.cn-hangzhou.aliyuncs.com/lepo/go-devcontainer:0.1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> push registry.cn-hangzhou.aliyuncs.com/lepo/go-devcontainer:0.1</span><br></span></code></pre></div></div>
<p>也可以直接一步到位, 构建镜像并推送到镜像仓库</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">devcontainer build --workspace-folder </span><span class="token builtin class-name">.</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">--push</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token plain"> --image-name </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">my_image_name</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">:</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">optional_image_version</span><span class="token operator" style="color:#393A34">&gt;</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="offline">离线模式<a href="https://blog.lintech1024.com/devcontainer-prebuild#offline" class="hash-link" aria-label="离线模式的直接链接" title="离线模式的直接链接">​</a></h2>
<p>预构建虽解决了镜像构建问题, 但仍需要网络下载 VSCode Server 和插件. 以下方法可实现真正的离线开发环境</p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="docker-commit">直接将容器打包成镜像<a href="https://blog.lintech1024.com/devcontainer-prebuild#docker-commit" class="hash-link" aria-label="直接将容器打包成镜像的直接链接" title="直接将容器打包成镜像的直接链接">​</a></h3>
<ol>
<li>首先启动一个已配置好的容器</li>
<li>将运行中的容器保存为新镜像</li>
</ol>
<p>我们在先前的示例中已启动一个命名为 go-container 的容器, 可以直接将其保存为镜像</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> commit go-container go-devcontainer-offline:0.1</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="use-offline-image">使用离线镜像<a href="https://blog.lintech1024.com/devcontainer-prebuild#use-offline-image" class="hash-link" aria-label="使用离线镜像的直接链接" title="使用离线镜像的直接链接">​</a></h3>
<p>新建 go-demo-offline 目录, 创建 devcontainer.json 文件</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">.devcontainer/devcontainer.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"go-demo-offline"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"image"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"go-devcontainer-offline:0.1"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>此时, 即使完全断开网络连接, 也能成功启动开发环境, 并且相关的环境配置, 插件安装均已准备就绪, 开箱即用</p>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>重要安全提示</div><div class="admonitionContent_BuS1"><p>此方法会将容器内所有数据(包括可能的敏感信息)打包到镜像中, 请务必:</p><ol>
<li>在提交前清理容器内的敏感数据</li>
<li>避免将包含用户凭证的容器提交到共享仓库</li>
</ol></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="docker-save">保存镜像<a href="https://blog.lintech1024.com/devcontainer-prebuild#docker-save" class="hash-link" aria-label="保存镜像的直接链接" title="保存镜像的直接链接">​</a></h3>
<p>制作的镜像同样可以推送到私有仓库方便随时拉取开发环境, 或者可以保存为 tar 文件, 存到 U 盘随身携带</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> save </span><span class="token parameter variable" style="color:#36acaa">-o</span><span class="token plain"> go-devcontainer-offline.tar go-devcontainer-offline:0.1</span><br></span></code></pre></div></div>
<p>后续可直接恢复镜像</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> load </span><span class="token parameter variable" style="color:#36acaa">-i</span><span class="token plain"> go-devcontainer-offline.tar</span><br></span></code></pre></div></div>
<p>以后你就能火箭般🚀配好自己的开发环境, 从此成为最靓的仔 😎</p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="ref">参考<a href="https://blog.lintech1024.com/devcontainer-prebuild#ref" class="hash-link" aria-label="参考的直接链接" title="参考的直接链接">​</a></h2>
<ul>
<li><a href="https://containers.dev/guide/prebuild" target="_blank" rel="noopener noreferrer">Speed Up Your Workflow with Prebuilds</a></li>
<li><a href="https://www.goproxy.io/zh/" target="_blank" rel="noopener noreferrer">goproxy.io 官网</a></li>
</ul>]]></content:encoded>
            <author>lintech1024@hotmail.com (小林)</author>
            <category>基础教程</category>
            <category>开发环境</category>
            <category>VSCode</category>
            <category>Docker</category>
        </item>
        <item>
            <title><![CDATA[Dev Container 教程 (一) 基本使用]]></title>
            <link>https://blog.lintech1024.com/devcontainer-tutorial</link>
            <guid>https://blog.lintech1024.com/devcontainer-tutorial</guid>
            <pubDate>Mon, 01 Sep 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[作为开发者, 我们常面临这样的挑战：如何让系统环境、编程工具链、依赖库及编辑器配置在不同机器上保持一致？如何避免每次搭建开发环境浪费大量时间? 本文介绍的 Dev Containers 就为了解决这些问题, 让你一键启动一个标准化的开发环境]]></description>
            <content:encoded><![CDATA[<p>作为开发者, 我们常面临这样的挑战：如何让系统环境、编程工具链、依赖库及编辑器配置在不同机器上保持一致？如何避免每次搭建开发环境浪费大量时间? 本文介绍的 Dev Containers 就为了解决这些问题, 让你一键启动一个标准化的开发环境</p>
<p><img decoding="async" loading="lazy" alt="messenger-running.png" src="https://blog.lintech1024.com/assets/images/messenger-running-e359a6391733e907ad9c91124f9c4d09.png" width="588" height="600" class="img_CujE"></p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="what-devcontainer">什么是 Dev Container<a href="https://blog.lintech1024.com/devcontainer-tutorial#what-devcontainer" class="hash-link" aria-label="什么是 Dev Container的直接链接" title="什么是 Dev Container的直接链接">​</a></h2>
<p>Dev Container 是一种将容器用作完整开发环境的技术方案. 简单表示</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mtext>开发环境</mtext><mo>=</mo><mi>d</mi><mi>e</mi><mi>v</mi><mi>c</mi><mi>o</mi><mi>n</mi><mi>t</mi><mi>a</mi><mi>i</mi><mi>n</mi><mi>e</mi><mi>r</mi><mi mathvariant="normal">.</mi><mi>j</mi><mi>s</mi><mi>o</mi><mi>n</mi><mo>+</mo><mi>D</mi><mi>o</mi><mi>c</mi><mi>k</mi><mi>e</mi><mi>r</mi><mo>+</mo><mi>V</mi><mi>S</mi><mi>C</mi><mi>o</mi><mi>d</mi><mi>e</mi></mrow><annotation encoding="application/x-tex">开发环境 = devcontainer.json + Docker + VSCode</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord cjk_fallback">开发环境</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal">d</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.03588em">v</span><span class="mord mathnormal">co</span><span class="mord mathnormal">n</span><span class="mord mathnormal">t</span><span class="mord mathnormal">ain</span><span class="mord mathnormal" style="margin-right:0.02778em">er</span><span class="mord">.</span><span class="mord mathnormal" style="margin-right:0.05724em">j</span><span class="mord mathnormal">so</span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.7778em;vertical-align:-0.0833em"></span><span class="mord mathnormal">Doc</span><span class="mord mathnormal" style="margin-right:0.03148em">k</span><span class="mord mathnormal" style="margin-right:0.02778em">er</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal" style="margin-right:0.22222em">V</span><span class="mord mathnormal" style="margin-right:0.07153em">SC</span><span class="mord mathnormal">o</span><span class="mord mathnormal">d</span><span class="mord mathnormal">e</span></span></span></span></span>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="why-devcontainer">为什么使用 Dev Container<a href="https://blog.lintech1024.com/devcontainer-tutorial#why-devcontainer" class="hash-link" aria-label="为什么使用 Dev Container的直接链接" title="为什么使用 Dev Container的直接链接">​</a></h2>
<ul>
<li><strong>环境一致性</strong>：无论怎样更换设备, 只需拉取容器即可获得完全一致的开发环境, 避免繁琐的环境配置过程</li>
<li><strong>团队协作</strong>：确保所有团队成员使用完全相同的开发环境, 消除"我的机器可以, 你的不行"的常见问题</li>
<li><strong>贴近生产</strong>：提供纯净的 Linux 环境, 更接近实际生产环境, 减少环境差异导致的问题</li>
<li><strong>流程简化</strong>：自动化环境部署, 简化开发流程, 提升开发效率</li>
<li><strong>工具集成</strong>：内置常用开发工具, 自动安装 VSCode 插件, 开箱即用</li>
<li><strong>配置同步</strong>：确保 VSCode 设置和插件在所有环境中保持一致</li>
<li><strong>远程开发</strong>：支持远程开发场景, 使开发环境与本地机器解耦</li>
</ul>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="pre">前置条件<a href="https://blog.lintech1024.com/devcontainer-tutorial#pre" class="hash-link" aria-label="前置条件的直接链接" title="前置条件的直接链接">​</a></h2>
<ul>
<li>安装 Docker, Windows 系统推荐安装 <a href="https://www.docker.com/products/docker-desktop/" target="_blank" rel="noopener noreferrer">Docker Desktop</a></li>
<li>安装 <a href="https://code.visualstudio.com/" target="_blank" rel="noopener noreferrer">VSCode</a> 或 <a href="https://code.visualstudio.com/insiders/" target="_blank" rel="noopener noreferrer">VSCode Insider</a></li>
<li>安装 <a href="vscode:extension/ms-vscode-remote.remote-containers" target="_blank" rel="noopener noreferrer">Dev Containers extension</a> 插件</li>
<li>(可选)安装 <a href="vscode:extension/ms-vscode-remote.vscode-remote-extensionpack" target="_blank" rel="noopener noreferrer">Remote Development extension pack</a> 插件, 如果需要远程开发</li>
<li>网络代理, 没有代理可能会因为网络问题无法拉到Docker镜像,国内环境你懂的</li>
</ul>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="quick-start">快速入门<a href="https://blog.lintech1024.com/devcontainer-tutorial#quick-start" class="hash-link" aria-label="快速入门的直接链接" title="快速入门的直接链接">​</a></h2>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="new-devcontainer">从零开始创建一个 Dev Container<a href="https://blog.lintech1024.com/devcontainer-tutorial#new-devcontainer" class="hash-link" aria-label="从�零开始创建一个 Dev Container的直接链接" title="从零开始创建一个 Dev Container的直接链接">​</a></h3>
<p>下面以创建 Python 开发环境为例, 演示如何从零开始创建 Dev Container</p>
<p><img decoding="async" loading="lazy" alt="new-devcontainers" src="https://blog.lintech1024.com/assets/images/new-devcontainers-52a9c89d056cfbe7cc28b564d94c7baa.gif" width="2559" height="1380" class="img_CujE"></p>
<ul>
<li>点击 VSCode 左下角的 <strong>Open a Remote Window</strong> 按钮</li>
<li>在弹出菜单中选择 <strong>New Dev Container...</strong></li>
<li>在模板列表中输入 <strong>python</strong> 进行过滤</li>
<li>输入容器名称(如 python-demo)</li>
<li>等待 Docker 拉取容器镜像(可点击 <strong>show log</strong> 查看进度)</li>
<li>成功后, VSCode 将打开新窗口, 表示已进入 Dev Container 环境</li>
<li>在终端输入 <code>python --version</code> 确认 Python 环境是否可用</li>
</ul>
<p>此时, 工作目录下会生成 Dev Container 配置文件：</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">.devcontainer/devcontainer.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// For format details, see https://aka.ms/devcontainer.json. For config options, see the</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// README at: https://github.com/devcontainers/templates/tree/main/src/python</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Python 3"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"image"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"mcr.microsoft.com/devcontainers/python:1-3.12-bullseye"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Features to add to the dev container. More info: https://containers.dev/features.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// "features": {},</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Use 'forwardPorts' to make a list of ports inside the container available locally.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// "forwardPorts": [],</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Use 'postCreateCommand' to run commands after the container is created.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// "postCreateCommand": "pip3 install --user -r requirements.txt",</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Configure tool-specific properties.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// "customizations": {},</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// "remoteUser": "root"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>Dev Container 做了什么事?</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-09-01-11-22-31-73129ea05bcc48b52db73cdf64f0fabc.png" width="2499" height="903" class="img_CujE"></p>
<ul>
<li>拉取配置中 <code>image</code> 字段指定的 Docker 镜像</li>
<li>如使用 Dockerfile 或 Features, 会自动构建定制化镜像</li>
</ul>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-09-01-11-31-05-e418dc75b47b253e932ed850ed9db094.png" width="2475" height="769" class="img_CujE"></p>
<ul>
<li>启动 Docker 容器作为开发环境</li>
<li>容器关闭后自动销毁, 确保环境纯净</li>
</ul>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-09-01-11-33-48-8c10a1bbcf901a899926b360b244e2f0.png" width="2509" height="651" class="img_CujE"></p>
<ul>
<li>创建与容器名称相同的持久化卷</li>
<li>将持久化卷挂载到工作目录, 确保数据不丢失</li>
</ul>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-09-01-11-39-48-b7d80e48dd45d6317f3301dfca74acdb.png" width="1013" height="1177" class="img_CujE"></p>
<ul>
<li>自动安装配置模板中指定的 VSCode 插件</li>
<li>开箱即用, 无需手动配置开发环境</li>
</ul>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="existing-devcontainer">用 Dev Container 打开已有项目<a href="https://blog.lintech1024.com/devcontainer-tutorial#existing-devcontainer" class="hash-link" aria-label="用 Dev Container 打开已有项目的直接链接" title="用 Dev Container 打开已有项目的直接链接">​</a></h3>
<p><img decoding="async" loading="lazy" alt="exist-devcontainers" src="https://blog.lintech1024.com/assets/images/exist-devcontainers-184d30851bbbe129367cca2fae2756a7.gif" width="2564" height="1380" class="img_CujE"></p>
<p>以下演示如何为现有项目配置 Dev Container(以 Golang Gin <a href="https://github.com/lintech1024/gin-demo" target="_blank" rel="noopener noreferrer">项目</a>为例):</p>
<ul>
<li>点击左下角的 <strong>Open a Remote Window</strong> 按钮</li>
<li>选择 <strong>Add Dev Container Configuration Files...</strong></li>
<li>选择 <strong>Add Configuration to workspace</strong> 将配置文件存放到工作目录</li>
<li>在模板列表中输入 <strong>go</strong> 进行过滤</li>
<li>选择 Go 版本(如 1.24-bookworm)</li>
<li>按需选择 <strong>Features</strong>(可跳过)</li>
<li>点击 <strong>Reopen in Container</strong> 在新窗口打开容器</li>
</ul>
<p>生成的配置文件如下:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">.devcontainer/devcontainer.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// For format details, see https://aka.ms/devcontainer.json. For config options, see the</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// README at: https://github.com/devcontainers/templates/tree/main/src/go</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Go"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"image"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"mcr.microsoft.com/devcontainers/go:1-1.24-bookworm"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Features to add to the dev container. More info: https://containers.dev/features.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// "features": {},</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Use 'forwardPorts' to make a list of ports inside the container available locally.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// "forwardPorts": [],</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Use 'postCreateCommand' to run commands after the container is created.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// "postCreateCommand": "go version",</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Configure tool-specific properties.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// "customizations": {},</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// "remoteUser": "root"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>同样预装了相关插件, 预装哪些插件根据配置模板会有不同</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-09-01-15-53-24-26723cd2a49ee574bf08e1c1aada0d8e.png" width="399" height="809" class="img_CujE"></p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>注意</div><div class="admonitionContent_BuS1"><p>使用 Dev Container 打开已有项目时, 不会创建持久化卷, 而是直接将本地目录挂载到容器工作目录. 这意味着在容器中对工作目录的修改会直接反映到本地文件系统</p></div></div>
<p>配置完成后, 可运行项目并访问 <a href="http://localhost:8080/ping" target="_blank" rel="noopener noreferrer">http://localhost:8080/ping</a></p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">go run main.go</span><br></span></code></pre></div></div>
<p>Dev Container 会自动将容器内部端口转发到本地, 无需手动配置端口映射</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-09-01-15-02-36-8d24b5ad5e79f2885f321f664dac5e0c.png" width="1823" height="185" class="img_CujE"></p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="devcontainer-json">配置 devcontainer.json<a href="https://blog.lintech1024.com/devcontainer-tutorial#devcontainer-json" class="hash-link" aria-label="配置 devcontainer.json的直接链接" title="配置 devcontainer.json的直接链接">​</a></h2>
<p>下面以 Python FastAPI <a href="https://github.com/lintech1024/fastapi-demo" target="_blank" rel="noopener noreferrer">项目</a>为例, 优化第一个<a href="https://blog.lintech1024.com/devcontainer-tutorial#new-devcontainer">示例</a>的 <code>devcontainer.json</code> 配置, 提升开发体验</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">.devcontainer/devcontainer.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// For format details, see https://aka.ms/devcontainer.json. For config options, see the</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// README at: https://github.com/devcontainers/templates/tree/main/src/python</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Python 3"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"image"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"mcr.microsoft.com/devcontainers/python:1-3.12-bullseye"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Features to add to the dev container. More info: https://containers.dev/features.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// "features": {},</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Use 'forwardPorts' to make a list of ports inside the container available locally.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// "forwardPorts": [],</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Use 'postCreateCommand' to run commands after the container is created.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// "postCreateCommand": "pip3 install --user -r requirements.txt",</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Configure tool-specific properties.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"customizations"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token property" style="color:#36acaa">"vscode"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"settings"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"window.newWindowProfile"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Default"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 新窗口使用的配置文件, 只能在"默认配置文件"中设置</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"files.autoSave"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"afterDelay"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 自动保存文件</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"editor.formatOnSave"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 保存时自动格式化</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"files.simpleDialog.enable"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 简单对话框, ctrl+n 新建文件时不再弹出对话框</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"editor.insertSpaces"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 使用空格代替制表符</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"editor.tabSize"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 缩进空格数</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"editor.autoClosingDelete"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"always"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 删除左括号时总是自动删除右括号</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"editor.minimap.autohide"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"mouseover"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 鼠标不在缩略图上时隐藏缩略图, 鼠标在缩略图上时显示缩略图</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"terminal.integrated.cursorStyle"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"underline"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 终端光标样式使用下划线</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"explorer.confirmDelete"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 删除文件时不再弹出确认对话框</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"explorer.confirmDragAndDrop"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 拖放移动文件或文件夹时不再弹出确认对话框</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"vsicons.dontShowNewVersionMessage"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 不显示新版本消息</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token property" style="color:#36acaa">"workbench.iconTheme"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"vscode-icons"</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 使用 vscode-icons 图标主题</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"extensions"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"piotrpalarz.vscode-gitignore-generator"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// .gitignore 文件生成器</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"mhutchie.git-graph"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Git 图形化视图</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"eamodio.gitlens"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Git 增强工具, 主要为了能显示代码作者</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"ms-python.vscode-pylance"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Python 语言支持</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"vscode-icons-team.vscode-icons"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// VSCode 图标主题</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"github.copilot"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// GitHub Copilot, AI 代码补全</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"github.copilot-chat"</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// GitHub Copilot Chat, AI 聊天助手</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// "remoteUser": "root"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"containerEnv"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token property" style="color:#36acaa">"PIP_INDEX_URL"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"https://mirrors.aliyun.com/pypi/simple/"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token property" style="color:#36acaa">"PIP_TRUSTED_HOST"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"mirrors.aliyun.com"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"postCreateCommand"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"pip install -U pip &amp;&amp; pip install fastapi uvicorn[standard]"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token property" style="color:#36acaa">"postStartCommand"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"uvicorn main:app --reload"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<ul>
<li><code>name</code>: 必填项, 会显示在 VSCode 的 UI 上</li>
<li><code>image</code>: 基础 Docker 镜像地址</li>
<li><code>customizations</code>: 跟编辑器相关的配置, VSCode 的相关设置可以在这里配置, 我这里修改 <code>settings.json</code> 的一些值和预装插件, VSCode 配置可以参考我之前的<a href="https://blog.lintech1024.com/vscode-profile">文章</a></li>
<li><code>containerEnv</code>: 注入容器的环境变量, 这里通过环境变量设置 pip 使用阿里源</li>
<li><code>postCreateCommand</code>: 每次容器构建完成前执行的最后一段命令, 我们在这里配置安装项目所需的依赖库</li>
<li><code>postStartCommand</code>: 每次容器启动前执行的最后一段命令, 可以在这里配置启动服务的命令</li>
<li>更多配置项可以参考<a href="https://containers.dev/implementors/json_reference/" target="_blank" rel="noopener noreferrer">Dev Container 官方文档</a></li>
</ul>
<p>配置完成后, 点击左下角按钮选择 <strong>Rebuild Container</strong> 重建容器:</p>
<p><img decoding="async" loading="lazy" alt="rebuild-devcontainer" src="https://blog.lintech1024.com/assets/images/rebuild-devcontainer-fc605a5cda7fd7f69680f156713fa10a.gif" width="2558" height="1380" class="img_CujE"></p>
<p>容器构建完成后, Dev Container 会自动启动服务并映射端口, 可点击 Open in Browser 直接访问应用</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://blog.lintech1024.com/assets/images/2025-09-01-17-16-33-2965203cc5368c5c98539191f7bfcc8d.png" width="1812" height="221" class="img_CujE"></p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="summary">总结<a href="https://blog.lintech1024.com/devcontainer-tutorial#summary" class="hash-link" aria-label="总结的直接链接" title="总结的直接链接">​</a></h2>
<p>本文通过示例介绍了 Dev Container 的基本使用, 为避免篇幅过长, 我将在后续文章介绍 Dev Container 更多高级用法</p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="more">更多<a href="https://blog.lintech1024.com/devcontainer-tutorial#more" class="hash-link" aria-label="更多的直接链接" title="更多的直接链接">​</a></h2>
<ul>
<li><a href="https://blog.lintech1024.com/devcontainer-prebuild">Dev Container 教程 (二) 使用预构建加速流程</a></li>
</ul>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="ref">参考<a href="https://blog.lintech1024.com/devcontainer-tutorial#ref" class="hash-link" aria-label="参考的直接链接" title="参考的直接链接">​</a></h2>
<ul>
<li><a href="https://containers.dev/" target="_blank" rel="noopener noreferrer">Dev Containers 官网</a></li>
<li><a href="https://code.visualstudio.com/docs/devcontainers/containers" target="_blank" rel="noopener noreferrer">Dev Containers Overview</a></li>
<li><a href="https://code.visualstudio.com/remote/advancedcontainers/overview" target="_blank" rel="noopener noreferrer">Advanced container configuration</a></li>
</ul>]]></content:encoded>
            <author>lintech1024@hotmail.com (小林)</author>
            <category>基础教程</category>
            <category>开发环境</category>
            <category>VSCode</category>
            <category>Docker</category>
        </item>
        <item>
            <title><![CDATA[一份珍藏多年的 VSCode 配置]]></title>
            <link>https://blog.lintech1024.com/vscode-profile</link>
            <guid>https://blog.lintech1024.com/vscode-profile</guid>
            <pubDate>Thu, 28 Aug 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[记录下最符合个人习惯的 VSCode 配置, 只修改非默认项以保持配置文件的简洁. 可能会随着使用不断补充更新.]]></description>
            <content:encoded><![CDATA[<p>记录下最符合个人习惯的 VSCode 配置, 只修改非默认项以保持配置文件的简洁. 可能会随着使用不断补充更新.</p>
<p><img decoding="async" loading="lazy" alt="hero-poster-dark" src="https://blog.lintech1024.com/assets/images/hero-poster-dark-7d1e85898144fa02a9c84bcb0316b7f9.webp" width="2306" height="1440" class="img_CujE"></p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="settings">Settings<a href="https://blog.lintech1024.com/vscode-profile#settings" class="hash-link" aria-label="Settings的直接链接" title="Settings的直接链接">​</a></h2>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">settings.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"window.newWindowProfile"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Default"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 新窗口使用的配置文件, 只能在"默认配置文件"中设置</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"files.autoSave"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"afterDelay"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 自动保存文件</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"editor.formatOnSave"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 保存时自动格式化</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"files.simpleDialog.enable"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 简单对话框, ctrl+n 新建文件时不再弹出对话框</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"editor.insertSpaces"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 使用空格代替制表符</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"editor.tabSize"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 缩进空格数</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"editor.autoClosingDelete"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"always"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 删除左括号时总是自动删除右括号</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"editor.minimap.autohide"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"mouseover"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 鼠标不在缩略图上时隐藏缩略图，鼠标在缩略图上时显示缩略图</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"terminal.integrated.cursorStyle"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"underline"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 终端光标样式使用下划线</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"explorer.confirmDelete"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 删除文件时不再弹出确认对话框</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"explorer.confirmDragAndDrop"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 拖放移动文件或文件夹时不再弹出确认对话框</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"vsicons.dontShowNewVersionMessage"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 不显示新版本消息</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"workbench.iconTheme"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"vscode-icons"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 使用 vscode-icons 图标主题</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="keybindings">键盘快捷键<a href="https://blog.lintech1024.com/vscode-profile#keybindings" class="hash-link" aria-label="键盘快捷键的直接链接" title="键盘快捷键的直接链接">​</a></h2>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">keybindings.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token property" style="color:#36acaa">"key"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"ctrl+k ctrl+u"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token property" style="color:#36acaa">"command"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"editor.action.transformToUppercase"</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 转为大写</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token property" style="color:#36acaa">"key"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"ctrl+k ctrl+l"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token property" style="color:#36acaa">"command"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"editor.action.transformToLowercase"</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 转为小写</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">]</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="extensions">插件<a href="https://blog.lintech1024.com/vscode-profile#extensions" class="hash-link" aria-label="插件的直接链接" title="插件的直接链接">​</a></h2>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">extensions.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"recommendations"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"piotrpalarz.vscode-gitignore-generator"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// .gitignore 文件生成器</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"mhutchie.git-graph"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Git 图形化视图</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"eamodio.gitlens"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Git 增强工具, 主要为了能显示代码作者</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"golang.go"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Go 语言支持</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"ritwickdey.liveserver"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// HTML 实时预览</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"ms-python.vscode-pylance"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Python 语言支持</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"mushan.vscode-paste-image"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 支持粘贴图片到 Markdown</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"vscode-icons-team.vscode-icons"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// VSCode 图标主题</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"github.copilot"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// GitHub Copilot, AI 代码补全</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"github.copilot-chat"</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// GitHub Copilot Chat, AI 聊天助手</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>]]></content:encoded>
            <author>lintech1024@hotmail.com (小林)</author>
            <category>开发环境</category>
            <category>VSCode</category>
        </item>
    </channel>
</rss>