<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Gin Archives - 原立方</title>
	<atom:link href="https://www.atomic-cube.cn/tag/gin/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.atomic-cube.cn/tag/gin/</link>
	<description>技术栈的流动-从零到∞</description>
	<lastBuildDate>Fri, 01 May 2026 08:32:50 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0.2</generator>
	<item>
		<title>Go 语言文件处理</title>
		<link>https://www.atomic-cube.cn/go-%e8%af%ad%e8%a8%80%e6%96%87%e4%bb%b6%e5%a4%84%e7%90%86/</link>
					<comments>https://www.atomic-cube.cn/go-%e8%af%ad%e8%a8%80%e6%96%87%e4%bb%b6%e5%a4%84%e7%90%86/#respond</comments>
		
		<dc:creator><![CDATA[evans]]></dc:creator>
		<pubDate>Tue, 28 Apr 2026 23:37:11 +0000</pubDate>
				<category><![CDATA[Gin]]></category>
		<category><![CDATA[go-zero]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[技术文章]]></category>
		<category><![CDATA[Go-zero]]></category>
		<guid isPermaLink="false">https://www.atomic-cube.cn/?p=1952</guid>

					<description><![CDATA[<p>文件处理是 Go 语言中最常见的操作之一——读取配置、写入日志、数据持久化都离不开它。Go 的标准库提供了一套简洁而强大的文件 I/O 接口，覆盖从单次读写到流式处理的各种场景</p>
<p>The post <a href="https://www.atomic-cube.cn/go-%e8%af%ad%e8%a8%80%e6%96%87%e4%bb%b6%e5%a4%84%e7%90%86/">Go 语言文件处理</a> appeared first on <a href="https://www.atomic-cube.cn">原立方</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">文件处理是 Go 语言中最常见的操作之一——读取配置、写入日志、数据持久化都离不开它。Go 的标准库提供了一套简洁而强大的文件 I/O 接口，覆盖从单次读写到流式处理的各种场景。</p>



<p class="wp-block-paragraph">与文件处理相关的核心包有 5 个，各有分工：</p>



<figure class="wp-block-image"><a href="https://www.atomic-cube.cn/wp-content/uploads/2026/04/os-scaled.png"><img fetchpriority="high" decoding="async" width="2560" height="986" src="https://www.atomic-cube.cn/wp-content/uploads/2026/04/os-scaled.png" alt="" class="wp-image-1954" srcset="https://www.atomic-cube.cn/wp-content/uploads/2026/04/os-scaled.png 2560w, https://www.atomic-cube.cn/wp-content/uploads/2026/04/os-300x116.png 300w, https://www.atomic-cube.cn/wp-content/uploads/2026/04/os-1024x394.png 1024w, https://www.atomic-cube.cn/wp-content/uploads/2026/04/os-768x296.png 768w, https://www.atomic-cube.cn/wp-content/uploads/2026/04/os-1536x592.png 1536w, https://www.atomic-cube.cn/wp-content/uploads/2026/04/os-2048x789.png 2048w" sizes="(max-width: 2560px) 100vw, 2560px" /></a></figure>



<p class="wp-block-paragraph"></p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><strong>版本提示：</strong><code>ioutil</code> 包在 Go 1.16 已弃用，其功能已迁移到 <code>os</code>（如 <code>os.ReadFile</code>、<code>os.WriteFile</code>）和 <code>io</code>（如 <code>io.ReadAll</code>）。新代码应直接使用 <code>os</code> 和 <code>io</code> 包。</p>
</blockquote>



<h2 class="wp-block-heading">1. 文件创建</h2>



<p class="wp-block-paragraph"><code>os.Create</code> 创建一个新文件。如果文件已存在，<span class="marked">会被截断（清空内容）</span>。返回的文件对象必须关闭以释放系统资源：</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>package main

import (
        "log"
        "os"
)

func main() {
        // os.Create 创建文件（已存在则清空）
        file, err := os.Create("test.txt")
        if err != nil {
                log.Fatal(err)
        }
        defer file.Close() // defer 确保函数结束时关闭文件

        // 写入内容验证创建成功
        file.WriteString("文件创建成功\n")
        log.Println("文件创建成功")
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">package</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">main</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">import</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">log</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">os</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">main</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// os.Create 创建文件（已存在则清空）</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Create</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">test.txt</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fatal</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">defer</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Close</span><span style="color: #D8DEE9FF">() </span><span style="color: #616E88">// defer 确保函数结束时关闭文件</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// 写入内容验证创建成功</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">WriteString</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">文件创建成功</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Println</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">文件创建成功</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><strong><code>defer file.Close()</code></strong> 是 Go 文件操作的最佳实践。<code>defer</code> 会确保即使后续代码出现 panic，文件也会被正确关闭，避免文件描述符泄漏。</p>
</blockquote>



<h2 class="wp-block-heading">2. 文件打开与关闭</h2>



<p class="wp-block-paragraph"><code>os</code> 包提供三种打开文件的方式，适用于不同场景：</p>



<figure class="wp-block-table reference"><table class="has-fixed-layout"><tbody><tr><th>函数</th><th>模式</th><th>说明</th></tr><tr><td><code>os.Open(name)</code></td><td>只读</td><td>最简单的方式，只读打开</td></tr><tr><td><code>os.Create(name)</code></td><td>读写 + 创建/截断</td><td>创建新文件或清空已有文件</td></tr><tr><td><code>os.OpenFile(name, flag, perm)</code></td><td>自定义</td><td>可指定读写、追加、创建等标志</td></tr></tbody></table></figure>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>package main

import (
        "fmt"
        "os"
)

func main() {
        // 方式1：只读打开
        file, err := os.Open("example.txt")
        if err != nil {
                fmt.Println("打开失败:", err)
                return
        }
        defer file.Close()
        fmt.Println("文件打开成功")

        // 方式2：OpenFile 自定义模式
        // os.O_WRONLY  只写
        // os.O_CREATE  不存在则创建
        // os.O_APPEND  追加模式
        // os.O_TRUNC   存在则截断
        f, err := os.OpenFile("log.txt",
                os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
        if err != nil {
                fmt.Println("打开失败:", err)
                return
        }
        defer f.Close()
        f.WriteString("追加一行日志\n")
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">package</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">main</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">import</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">fmt</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">os</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">main</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// 方式1：只读打开</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Open</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">example.txt</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Println</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">打开失败:</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">return</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">defer</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Close</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Println</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">文件打开成功</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// 方式2：OpenFile 自定义模式</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// os.O_WRONLY  只写</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// os.O_CREATE  不存在则创建</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// os.O_APPEND  追加模式</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// os.O_TRUNC   存在则截断</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">f</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">OpenFile</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">log.txt</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">O_WRONLY</span><span style="color: #81A1C1">|</span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">O_CREATE</span><span style="color: #81A1C1">|</span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">O_APPEND</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0644</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Println</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">打开失败:</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">return</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">defer</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">f</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Close</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">f</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">WriteString</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">追加一行日志</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<h3 class="wp-block-heading">OpenFile 标志位说明</h3>



<figure class="wp-block-table reference"><table class="has-fixed-layout"><tbody><tr><th>标志</th><th>说明</th></tr><tr><td><code>os.O_RDONLY</code></td><td>只读（默认）</td></tr><tr><td><code>os.O_WRONLY</code></td><td>只写</td></tr><tr><td><code>os.O_RDWR</code></td><td>读写</td></tr><tr><td><code>os.O_APPEND</code></td><td>追加模式，写入内容追加到文件末尾</td></tr><tr><td><code>os.O_CREATE</code></td><td>文件不存在时创建</td></tr><tr><td><code>os.O_TRUNC</code></td><td>打开时清空文件内容</td></tr><tr><td><code>os.O_EXCL</code></td><td>与 CREATE 配合，文件已存在时报错</td></tr></tbody></table></figure>



<h2 class="wp-block-heading">3. 文件读取</h2>



<p class="wp-block-paragraph">Go 提供了三种主要的文件读取方式，根据文件大小和处理需求选择：<a href="https://www.atomic-cube.cn/wp-content/uploads/2026/04/c.png"><img decoding="async" class="alignnone size-full wp-image-1955" src="https://www.atomic-cube.cn/wp-content/uploads/2026/04/c.png" alt="" width="1956" height="484" srcset="https://www.atomic-cube.cn/wp-content/uploads/2026/04/c.png 1956w, https://www.atomic-cube.cn/wp-content/uploads/2026/04/c-300x74.png 300w, https://www.atomic-cube.cn/wp-content/uploads/2026/04/c-1024x253.png 1024w, https://www.atomic-cube.cn/wp-content/uploads/2026/04/c-768x190.png 768w, https://www.atomic-cube.cn/wp-content/uploads/2026/04/c-1536x380.png 1536w" sizes="(max-width: 1956px) 100vw, 1956px" /></a></p>



<h3 class="wp-block-heading">3.1 一次性读取（小文件推荐）</h3>



<p class="wp-block-paragraph"><code>os.ReadFile</code> 是最简洁的方式——自动打开、读取、关闭文件，一步到位：</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>package main

import (
        "fmt"
        "log"
        "os"
)

func main() {
        // 一次性读取整个文件（Go 1.16+）
        data, err := os.ReadFile("config.json")
        if err != nil {
                log.Fatal(err)
        }

        fmt.Println(string(data))
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">package</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">main</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">import</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">fmt</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">log</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">os</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">main</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// 一次性读取整个文件（Go 1.16+）</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">data</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">ReadFile</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">config.json</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fatal</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Println</span><span style="color: #D8DEE9FF">(</span><span style="color: #88C0D0">string</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF">))</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<h3 class="wp-block-heading">3.2 逐行读取（大文件推荐）</h3>



<p class="wp-block-paragraph">对于大文件，使用 <code>bufio.Scanner</code> 逐行处理，避免一次性加载到内存：</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>package main

import (
        "bufio"
        "fmt"
        "log"
        "os"
)

func main() {
        file, err := os.Open("large-file.log")
        if err != nil {
                log.Fatal(err)
        }
        defer file.Close()

        // 逐行扫描
        scanner := bufio.NewScanner(file)
        lineNum := 0
        for scanner.Scan() {
                lineNum++
                line := scanner.Text() // 获取当前行内容
                fmt.Printf("第 %d 行: %s\n", lineNum, line)
        }

        // 检查扫描过程中是否出错
        if err := scanner.Err(); err != nil {
                log.Fatal("读取错误:", err)
        }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">package</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">main</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">import</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">bufio</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">fmt</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">log</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">os</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">main</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Open</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">large-file.log</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fatal</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">defer</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Close</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// 逐行扫描</span></span>
<span class="line"><span style="color: #D8DEE9FF">        scanner </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">bufio</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">NewScanner</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">file</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        lineNum </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">for</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">scanner</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Scan</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">lineNum</span><span style="color: #81A1C1">++</span></span>
<span class="line"><span style="color: #D8DEE9FF">                line </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">scanner</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Text</span><span style="color: #D8DEE9FF">() </span><span style="color: #616E88">// 获取当前行内容</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Printf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">第 %d 行: %s</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">lineNum</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">line</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// 检查扫描过程中是否出错</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">scanner</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Err</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fatal</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">读取错误:</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><strong>注意：</strong><code>bufio.Scanner</code> 默认最大行长度为 64KB。如果文件中有超长行，需要在调用 <code>Scan()</code> 前设置 <code>scanner.Buffer(make([]byte, 0), maxSize)</code> 来增大缓冲区。</p>
</blockquote>



<h3 class="wp-block-heading">3.3 使用 io.ReadAll 读取</h3>



<p class="wp-block-paragraph">当你已经有一个打开的 <code>io.Reader</code>（如网络响应、已打开的文件），可以使用 <code>io.ReadAll</code>：</p>



<pre class="wp-block-code"><code>package main

import (
        "fmt"
        "io"
        "log"
        "os"
)

func main() {
        file, err := os.Open("example.txt")
        if err != nil {
                log.Fatal(err)
        }
        defer file.Close()

        // 从 Reader 读取所有数据
        data, err := io.ReadAll(file)
        if err != nil {
                log.Fatal(err)
        }

        fmt.Println(string(data))
}</code></pre>



<h2 class="wp-block-heading">4. 文件写入</h2>



<p class="wp-block-paragraph">Go 提供了多种写入方式，从简单的一次性写入到高性能的缓冲写入：</p>



<h3 class="wp-block-heading">4.1 一次性写入</h3>



<p class="wp-block-paragraph"><code>os.WriteFile</code> 将数据一次性写入文件（覆盖原有内容）：</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>package main

import (
        "log"
        "os"
)

func main() {
        content := []byte("Hello, Go!\n这是第二行\n")

        // 0644: 所有者读写，其他用户只读
        err := os.WriteFile("output.txt", content, 0644)
        if err != nil {
                log.Fatal(err)
        }
        log.Println("写入成功")
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">package</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">main</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">import</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">log</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">os</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">main</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        content </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> []</span><span style="color: #88C0D0">byte</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Hello, Go!</span><span style="color: #EBCB8B">\n</span><span style="color: #A3BE8C">这是第二行</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// 0644: 所有者读写，其他用户只读</span></span>
<span class="line"><span style="color: #D8DEE9FF">        err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">WriteFile</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">output.txt</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">content</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0644</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fatal</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Println</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">写入成功</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<h3 class="wp-block-heading">4.2 使用 File 对象写入</h3>



<p class="wp-block-paragraph">通过文件对象写入，可以分多次写入内容：</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>package main

import (
        "fmt"
        "log"
        "os"
)

func main() {
        file, err := os.Create("output.txt")
        if err != nil {
                log.Fatal(err)
        }
        defer file.Close()

        // 方式1：写入字符串
        file.WriteString("直接写入字符串\n")

        // 方式2：写入字节切片
        data := []byte("写入字节切片\n")
        file.Write(data)

        // 方式3：格式化写入
        fmt.Fprintf(file, "格式化写入: %d + %d = %d\n", 3, 4, 7)
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">package</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">main</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">import</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">fmt</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">log</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">os</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">main</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Create</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">output.txt</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fatal</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">defer</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Close</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// 方式1：写入字符串</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">WriteString</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">直接写入字符串</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// 方式2：写入字节切片</span></span>
<span class="line"><span style="color: #D8DEE9FF">        data </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> []</span><span style="color: #88C0D0">byte</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">写入字节切片</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Write</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// 方式3：格式化写入</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fprintf</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">格式化写入: %d + %d = %d</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">3</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">4</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">7</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<h3 class="wp-block-heading">4.3 缓冲写入（大量数据推荐）</h3>



<p class="wp-block-paragraph"><code>bufio.Writer</code> 会先将数据写入内存缓冲区，攒够后再批量写入磁盘，显著减少 I/O 次数。<span class="marked">务必在结束前调用 <code>Flush()</code></span>：</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>package main

import (
        "bufio"
        "log"
        "os"
)

func main() {
        file, err := os.Create("buffered-output.txt")
        if err != nil {
                log.Fatal(err)
        }
        defer file.Close()

        // 创建带缓冲的写入器
        writer := bufio.NewWriter(file)

        // 写入多行（先进入缓冲区，不会立即写磁盘）
        for i := 0; i &lt; 1000; i++ {
                writer.WriteString("这是第 " + itoa(i) + " 行\n")
        }

        // Flush 将缓冲区剩余数据写入文件
        if err := writer.Flush(); err != nil {
                log.Fatal("刷新缓冲区失败:", err)
        }
        log.Println("缓冲写入完成")
}

// 简单的 int 转 string 辅助函数
func itoa(n int) string {
        return fmt.Sprintf("%d", n)
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">package</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">main</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">import</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">bufio</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">log</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">os</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">main</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Create</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">buffered-output.txt</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fatal</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">defer</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Close</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// 创建带缓冲的写入器</span></span>
<span class="line"><span style="color: #D8DEE9FF">        writer </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">bufio</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">NewWriter</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">file</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// 写入多行（先进入缓冲区，不会立即写磁盘）</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">for</span><span style="color: #D8DEE9FF"> i </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1000</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #81A1C1">++</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">writer</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">WriteString</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">这是第 </span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">itoa</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C"> 行</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// Flush 将缓冲区剩余数据写入文件</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">writer</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Flush</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fatal</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">刷新缓冲区失败:</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Println</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">缓冲写入完成</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// 简单的 int 转 string 辅助函数</span></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">itoa</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">n</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF">) </span><span style="color: #D8DEE9">string</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Sprintf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">%d</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">n</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<h2 class="wp-block-heading">5. 文件追加写入</h2>



<p class="wp-block-paragraph">使用 <code>os.O_APPEND</code> 标志在文件末尾追加内容，而不覆盖已有数据：</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>package main

import (
        "log"
        "os"
        "time"
)

func main() {
        // O_APPEND: 追加模式
        // O_CREATE: 不存在则创建
        // O_WRONLY: 只写
        file, err := os.OpenFile("app.log",
                os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
        if err != nil {
                log.Fatal(err)
        }
        defer file.Close()

        // 每次运行都会追加，不会覆盖
        timestamp := time.Now().Format("2006-01-02 15:04:05")
        file.WriteString("&#91;" + timestamp + "&#93; 应用启动\n")
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">package</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">main</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">import</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">log</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">os</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">time</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">main</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// O_APPEND: 追加模式</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// O_CREATE: 不存在则创建</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// O_WRONLY: 只写</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">OpenFile</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">app.log</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">O_APPEND</span><span style="color: #81A1C1">|</span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">O_CREATE</span><span style="color: #81A1C1">|</span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">O_WRONLY</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0644</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fatal</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">defer</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Close</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// 每次运行都会追加，不会覆盖</span></span>
<span class="line"><span style="color: #D8DEE9FF">        timestamp </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">time</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Now</span><span style="color: #D8DEE9FF">()</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Format</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">2006-01-02 15:04:05</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">WriteString</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">&#91;</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">timestamp</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">&#93; 应用启动</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><strong>对比：</strong><code>os.Create</code> 会清空已有内容；<code>os.OpenFile</code> 配合 <code>O_APPEND</code> 会追加内容。日志场景务必使用追加模式。</p>
</blockquote>



<h2 class="wp-block-heading">6. 文件删除与重命名</h2>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>package main

import (
        "log"
        "os"
)

func main() {
        // 重命名 / 移动文件
        err := os.Rename("old.txt", "new.txt")
        if err != nil {
                log.Fatal("重命名失败:", err)
        }
        log.Println("重命名成功")

        // 删除文件
        err = os.Remove("temp.txt")
        if err != nil {
                log.Fatal("删除失败:", err)
        }
        log.Println("删除成功")
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">package</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">main</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">import</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">log</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">os</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">main</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// 重命名 / 移动文件</span></span>
<span class="line"><span style="color: #D8DEE9FF">        err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Rename</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">old.txt</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">new.txt</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fatal</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">重命名失败:</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Println</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">重命名成功</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// 删除文件</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Remove</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">temp.txt</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fatal</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">删除失败:</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Println</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">删除成功</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<h2 class="wp-block-heading">7. 文件信息与检查</h2>



<h3 class="wp-block-heading">7.1 获取文件信息</h3>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>package main

import (
        "fmt"
        "log"
        "os"
)

func main() {
        info, err := os.Stat("test.txt")
        if err != nil {
                log.Fatal(err)
        }

        fmt.Println("文件名:    ", info.Name())
        fmt.Println("大小:      ", info.Size(), "字节")
        fmt.Println("权限:      ", info.Mode())
        fmt.Println("修改时间:  ", info.ModTime())
        fmt.Println("是目录吗:  ", info.IsDir())
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">package</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">main</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">import</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">fmt</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">log</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">os</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">main</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">info</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Stat</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">test.txt</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fatal</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Println</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">文件名:    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">info</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Name</span><span style="color: #D8DEE9FF">())</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Println</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">大小:      </span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">info</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Size</span><span style="color: #D8DEE9FF">()</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">字节</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Println</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">权限:      </span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">info</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Mode</span><span style="color: #D8DEE9FF">())</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Println</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">修改时间:  </span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">info</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">ModTime</span><span style="color: #D8DEE9FF">())</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Println</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">是目录吗:  </span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">info</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">IsDir</span><span style="color: #D8DEE9FF">())</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<h3 class="wp-block-heading">7.2 检查文件是否存在</h3>



<p class="wp-block-paragraph">Go 没有专门的&#8221;文件存在&#8221;函数，通过 <code>os.Stat</code> + <code>os.IsNotExist</code> 判断：</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>package main

import (
        "fmt"
        "os"
)

// fileExists 检查文件是否存在
func fileExists(path string) bool {
        _, err := os.Stat(path)
        return !os.IsNotExist(err)
}

func main() {
        if fileExists("test.txt") {
                fmt.Println("文件存在")
        } else {
                fmt.Println("文件不存在")
        }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">package</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">main</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">import</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">fmt</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">os</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// fileExists 检查文件是否存在</span></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">fileExists</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">string</span><span style="color: #D8DEE9FF">) </span><span style="color: #D8DEE9">bool</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">_</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Stat</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!</span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">IsNotExist</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">main</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">fileExists</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">test.txt</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Println</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">文件存在</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">else</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Println</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">文件不存在</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<h2 class="wp-block-heading">8. 目录操作</h2>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>package main

import (
        "fmt"
        "log"
        "os"
)

func main() {
        // 创建单个目录
        err := os.Mkdir("newdir", 0755)
        if err != nil {
                log.Fatal(err)
        }

        // 递归创建多级目录（父目录不存在也会自动创建）
        err = os.MkdirAll("path/to/deep/dir", 0755)
        if err != nil {
                log.Fatal(err)
        }

        // 读取目录内容
        entries, err := os.ReadDir(".")
        if err != nil {
                log.Fatal(err)
        }
        for _, entry := range entries {
                mark := "&#x1f4c4;"
                if entry.IsDir() {
                        mark = "&#x1f4c1;"
                }
                fmt.Printf("%s %s\n", mark, entry.Name())
        }

        // 删除空目录
        os.Remove("newdir")

        // 递归删除目录及其所有内容（&#x26a0; 谨慎使用）
        os.RemoveAll("path")
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">package</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">main</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">import</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">fmt</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">log</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">os</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">main</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// 创建单个目录</span></span>
<span class="line"><span style="color: #D8DEE9FF">        err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Mkdir</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">newdir</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0755</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fatal</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// 递归创建多级目录（父目录不存在也会自动创建）</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">MkdirAll</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">path/to/deep/dir</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0755</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fatal</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// 读取目录内容</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">entries</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">ReadDir</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">.</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fatal</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">for</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">_</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> entry </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">range</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">entries</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                mark </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">📄</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">entry</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">IsDir</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #D8DEE9">mark</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">📁</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Printf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">%s %s</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">mark</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">entry</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Name</span><span style="color: #D8DEE9FF">())</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// 删除空目录</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Remove</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">newdir</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// 递归删除目录及其所有内容（⚠ 谨慎使用）</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">RemoveAll</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">path</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<h2 class="wp-block-heading">9. 高级操作</h2>



<h3 class="wp-block-heading">9.1 文件复制</h3>



<p class="wp-block-paragraph">使用 <code>io.Copy</code> 在两个文件之间高效复制数据：</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>package main

import (
        "io"
        "log"
        "os"
)

// copyFile 复制文件的通用函数
func copyFile(src, dst string) (int64, error) {
        srcFile, err := os.Open(src)
        if err != nil {
                return 0, err
        }
        defer srcFile.Close()

        dstFile, err := os.Create(dst)
        if err != nil {
                return 0, err
        }
        defer dstFile.Close()

        // io.Copy 自动管理缓冲区，高效复制
        return io.Copy(dstFile, srcFile)
}

func main() {
        n, err := copyFile("source.txt", "destination.txt")
        if err != nil {
                log.Fatal(err)
        }
        log.Printf("复制完成，共 %d 字节", n)
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">package</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">main</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">import</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">io</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">log</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">os</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// copyFile 复制文件的通用函数</span></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">copyFile</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">src</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">dst</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">string</span><span style="color: #D8DEE9FF">) (</span><span style="color: #D8DEE9">int64</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">srcFile</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Open</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">src</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">defer</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">srcFile</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Close</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">dstFile</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Create</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">dst</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">defer</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">dstFile</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Close</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// io.Copy 自动管理缓冲区，高效复制</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">io</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Copy</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">dstFile</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">srcFile</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">main</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">n</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">copyFile</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">source.txt</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">destination.txt</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fatal</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Printf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">复制完成，共 %d 字节</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">n</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<h3 class="wp-block-heading">9.2 临时文件与目录</h3>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>package main

import (
        "fmt"
        "log"
        "os"
)

func main() {
        // 创建临时文件（自动命名，前缀为 "app-"）
        tmpFile, err := os.CreateTemp("", "app-*.txt")
        if err != nil {
                log.Fatal(err)
        }
        defer os.Remove(tmpFile.Name()) // 用完后清理
        fmt.Println("临时文件:", tmpFile.Name())

        // 创建临时目录
        tmpDir, err := os.MkdirTemp("", "app-*")
        if err != nil {
                log.Fatal(err)
        }
        defer os.RemoveAll(tmpDir) // 用完后清理
        fmt.Println("临时目录:", tmpDir)
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">package</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">main</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">import</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">fmt</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">log</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">os</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">main</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// 创建临时文件（自动命名，前缀为 &quot;app-&quot;）</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">tmpFile</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">CreateTemp</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">app-*.txt</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fatal</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">defer</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Remove</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">tmpFile</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Name</span><span style="color: #D8DEE9FF">()) </span><span style="color: #616E88">// 用完后清理</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Println</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">临时文件:</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tmpFile</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Name</span><span style="color: #D8DEE9FF">())</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// 创建临时目录</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">tmpDir</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">MkdirTemp</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">app-*</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fatal</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">defer</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">RemoveAll</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">tmpDir</span><span style="color: #D8DEE9FF">) </span><span style="color: #616E88">// 用完后清理</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Println</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">临时目录:</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tmpDir</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<h3 class="wp-block-heading">9.3 递归遍历目录</h3>



<p class="wp-block-paragraph"><code>filepath.Walk</code> 可以递归遍历目录树中的所有文件和子目录：</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>package main

import (
        "fmt"
        "log"
        "os"
        "path/filepath"
)

func main() {
        root := "." // 从当前目录开始遍历

        err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
                if err != nil {
                        return err // 遇到错误时可以选择跳过或返回
                }

                // 打印路径和大小
                size := ""
                if !info.IsDir() {
                        size = fmt.Sprintf(" (%d bytes)", info.Size())
                }
                fmt.Println(path + size)
                return nil
        })

        if err != nil {
                log.Fatal(err)
        }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">package</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">main</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">import</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">fmt</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">log</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">os</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">path/filepath</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">main</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        root </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">.</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// 从当前目录开始遍历</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">filepath</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Walk</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">root</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">func</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">string</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">info</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">FileInfo</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF">) </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                if err != nil </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// 遇到错误时可以选择跳过或返回</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">                </span><span style="color: #616E88">// 打印路径和大小</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">size </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!</span><span style="color: #D8DEE9">info</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">IsDir</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #D8DEE9">size</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Sprintf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C"> (%d bytes)</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">info</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Size</span><span style="color: #D8DEE9FF">())</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Println</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">size</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fatal</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<h3 class="wp-block-heading">9.4 跨平台路径拼接</h3>



<p class="wp-block-paragraph"><span class="marked">永远不要用字符串拼接路径</span>（如 <code>dir + "/" + file</code>），使用 <code>filepath.Join</code> 确保跨平台兼容：</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>// 错误：Windows 上路径分隔符是 \&lt;br>path := "dir" + "/" + "file.txt"&lt;br>&lt;br>// 正确：自动适配当前操作系统&lt;br>path := filepath.Join("dir", "file.txt")&lt;br>&lt;br></textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88">// 错误：Windows 上路径分隔符是 \&lt;br&gt;path := &quot;dir&quot; + &quot;/&quot; + &quot;file.txt&quot;&lt;br&gt;&lt;br&gt;// 正确：自动适配当前操作系统&lt;br&gt;path := filepath.Join(&quot;dir&quot;, &quot;file.txt&quot;)&lt;br&gt;&lt;br&gt;</span></span></code></pre></div>



<h2 class="wp-block-heading">场景速查表</h2>



<figure class="wp-block-table reference"><table class="has-fixed-layout"><tbody><tr><th>场景</th><th>推荐方式</th><th>关键代码</th></tr><tr><td>读取小文件</td><td><code>os.ReadFile</code></td><td><code>data, err := os.ReadFile("file.txt")</code></td></tr><tr><td>逐行读取大文件</td><td><code>bufio.Scanner</code></td><td><code>scanner := bufio.NewScanner(file)</code></td></tr><tr><td>写入小文件</td><td><code>os.WriteFile</code></td><td><code>os.WriteFile("f.txt", data, 0644)</code></td></tr><tr><td>大量数据写入</td><td><code>bufio.Writer</code></td><td><code>writer := bufio.NewWriter(file)</code></td></tr><tr><td>追加日志</td><td><code>os.OpenFile</code> + <code>O_APPEND</code></td><td><code>os.OpenFile("log", os.O_APPEND|os.O_WRONLY, 0644)</code></td></tr><tr><td>文件复制</td><td><code>io.Copy</code></td><td><code>io.Copy(dst, src)</code></td></tr><tr><td>遍历目录</td><td><code>filepath.Walk</code></td><td><code>filepath.Walk(".", callback)</code></td></tr><tr><td>路径拼接</td><td><code>filepath.Join</code></td><td><code>filepath.Join("dir", "file.txt")</code></td></tr><tr><td>检查文件存在</td><td><code>os.Stat</code> + <code>os.IsNotExist</code></td><td><code>_, err := os.Stat(path)</code></td></tr><tr><td>创建临时文件</td><td><code>os.CreateTemp</code></td><td><code>os.CreateTemp("", "prefix-*.txt")</code></td></tr></tbody></table></figure>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><strong>文件权限说明：</strong><code>0644</code> 表示所有者可读写（6=4+2），组和其他用户只读（4）。目录通常使用 <code>0755</code>（多了执行权限，否则无法进入目录）。</p>
</blockquote>
<p>The post <a href="https://www.atomic-cube.cn/go-%e8%af%ad%e8%a8%80%e6%96%87%e4%bb%b6%e5%a4%84%e7%90%86/">Go 语言文件处理</a> appeared first on <a href="https://www.atomic-cube.cn">原立方</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.atomic-cube.cn/go-%e8%af%ad%e8%a8%80%e6%96%87%e4%bb%b6%e5%a4%84%e7%90%86/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title> Go 开发经验总结：从入门到写出生产级代码</title>
		<link>https://www.atomic-cube.cn/go-%e5%bc%80%e5%8f%91%e7%bb%8f%e9%aa%8c%e6%80%bb%e7%bb%93%ef%bc%9a%e4%bb%8e%e5%85%a5%e9%97%a8%e5%88%b0%e5%86%99%e5%87%ba%e7%94%9f%e4%ba%a7%e7%ba%a7%e4%bb%a3%e7%a0%81/</link>
					<comments>https://www.atomic-cube.cn/go-%e5%bc%80%e5%8f%91%e7%bb%8f%e9%aa%8c%e6%80%bb%e7%bb%93%ef%bc%9a%e4%bb%8e%e5%85%a5%e9%97%a8%e5%88%b0%e5%86%99%e5%87%ba%e7%94%9f%e4%ba%a7%e7%ba%a7%e4%bb%a3%e7%a0%81/#respond</comments>
		
		<dc:creator><![CDATA[evans]]></dc:creator>
		<pubDate>Sat, 18 Apr 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[Gin]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[技术文章]]></category>
		<category><![CDATA[Go-zero]]></category>
		<guid isPermaLink="false">https://www.atomic-cube.cn/?p=1455</guid>

					<description><![CDATA[<p>用 Go 语言写后端已经好多年了。从最初被&#160;if err != nil&#160;烦到不行，到现在能熟练应对各种并发场景和代码组织，踩过不少坑，也有一些心得。这篇文章把这些经验整理下来，希望能帮到正在用 Go 的朋友。每个主题都会配上可直接运行的代码示例，你可以边看边跑。 1. 错误处理：不止是&#160;if err != nil 很多刚接触 Go 的人都会被满屏的&#160;if err != nil&#160;吓到。但真正的问题是，很多人只检查了错误，却没做好错误包装和分类。 1.1 用&#160;%w&#160;包装错误，保留上下文 Go 1.13 引入的错误包装功能非常实用。使用&#160;fmt.Errorf&#160;的&#160;%w&#160;动词，可以在每一层添加上下文信息，同时保留原始错误，方便上层用&#160;errors.Is&#160;和&#160;errors.As&#160;判断： 输出：业务判断：用户不存在完整错误：get user 42: not found 这种包装方式让错误信息像堆栈一样层层递进，排查问题时会省很多时间。 1.2 只在不可恢复时使用 panic Go 社区有共识：panic 只应在程序无法继续运行时使用，比如启动时依赖的配置加载失败。业务逻辑中应该用错误返回值，让调用方决定如何处理： 2. 并发编程：goroutine 的正确打开方式 2.1 用 Context 控制 goroutine 生命周期 在 HTTP 服务或长时间运行的任务中，用 context 来控制 goroutine 的启动和停止是最标准的做法： Go 的并发模型很简洁，但用不好容易出 goroutine [&#8230;]</p>
<p>The post <a href="https://www.atomic-cube.cn/go-%e5%bc%80%e5%8f%91%e7%bb%8f%e9%aa%8c%e6%80%bb%e7%bb%93%ef%bc%9a%e4%bb%8e%e5%85%a5%e9%97%a8%e5%88%b0%e5%86%99%e5%87%ba%e7%94%9f%e4%ba%a7%e7%ba%a7%e4%bb%a3%e7%a0%81/"> Go 开发经验总结：从入门到写出生产级代码</a> appeared first on <a href="https://www.atomic-cube.cn">原立方</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">用 Go 语言写后端已经好多年了。从最初被&nbsp;<code>if err != nil</code>&nbsp;烦到不行，到现在能熟练应对各种并发场景和代码组织，踩过不少坑，也有一些心得。这篇文章把这些经验整理下来，希望能帮到正在用 Go 的朋友。每个主题都会配上可直接运行的代码示例，你可以边看边跑。</p>



<h2 class="wp-block-heading">1. 错误处理：不止是&nbsp;<code>if err != nil</code></h2>



<p class="wp-block-paragraph">很多刚接触 Go 的人都会被满屏的&nbsp;<code>if err != nil</code>&nbsp;吓到。但真正的问题是，很多人只检查了错误，却没做好错误包装和分类。</p>



<h3 class="wp-block-heading">1.1 用&nbsp;<code>%w</code>&nbsp;包装错误，保留上下文</h3>



<p class="wp-block-paragraph">Go 1.13 引入的错误包装功能非常实用。使用&nbsp;<code>fmt.Errorf</code>&nbsp;的&nbsp;<code>%w</code>&nbsp;动词，可以在每一层添加上下文信息，同时保留原始错误，方便上层用&nbsp;<code>errors.Is</code>&nbsp;和&nbsp;<code>errors.As</code>&nbsp;判断：</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>package main

import (
    "errors"
    "fmt"
)

// 定义业务错误常量
var ErrNotFound = errors.New("not found")

// 模拟底层操作
func fetchFromDB(id int) (string, error) {
    return "", ErrNotFound
}

// 中间层：包装错误，添加上下文
func getUser(id int) (string, error) {
    name, err := fetchFromDB(id)
    if err != nil {
        return "", fmt.Errorf("get user %d: %w", id, err)
    }
    return name, nil
}

func main() {
    _, err := getUser(42)
    if err != nil {
        // 使用 errors.Is 判断错误类型
        if errors.Is(err, ErrNotFound) {
            fmt.Printf("业务判断：用户不存在\n")
        }
        // 完整错误信息包含所有上下文
        fmt.Printf("完整错误：%v\n", err)
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">package</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">main</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">import</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">errors</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">fmt</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// 定义业务错误常量</span></span>
<span class="line"><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ErrNotFound</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">errors</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">New</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">not found</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// 模拟底层操作</span></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">fetchFromDB</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">id</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF">) (</span><span style="color: #D8DEE9">string</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ErrNotFound</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// 中间层：包装错误，添加上下文</span></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">getUser</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">id</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF">) (</span><span style="color: #D8DEE9">string</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">name</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">fetchFromDB</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">id</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Errorf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">get user %d: %w</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">id</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">name</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">main</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">_</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">getUser</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">42</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// 使用 errors.Is 判断错误类型</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">errors</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Is</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ErrNotFound</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Printf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">业务判断：用户不存在</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// 完整错误信息包含所有上下文</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Printf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">完整错误：%v</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p class="wp-block-paragraph">输出：<br>业务判断：用户不存在<br>完整错误：get user 42: not found</p>



<p class="wp-block-paragraph">这种包装方式让错误信息像堆栈一样层层递进，排查问题时会省很多时间<a href="https://cloud.tencent.cn/developer/article/2575113?from=15425&amp;frompage=seopage" target="_blank" rel="noreferrer noopener"></a>。</p>



<h3 class="wp-block-heading">1.2 只在不可恢复时使用 panic</h3>



<p class="wp-block-paragraph">Go 社区有共识：panic 只应在程序无法继续运行时使用，比如启动时依赖的配置加载失败。业务逻辑中应该用错误返回值，让调用方决定如何处理<a href="https://cloud.tencent.cn/developer/article/2506310?from=15425&amp;frompage=seopage" target="_blank" rel="noreferrer noopener"></a>：</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>// ❌ 错误：滥用 panic
func readConfig() string {
    data, err := os.ReadFile("config.yaml")
    if err != nil {
        panic(err)  // 直接崩溃，调用方无法处理
    }
    return string(data)
}

// ✅ 正确：返回错误
func readConfig() (string, error) {
    data, err := os.ReadFile("config.yaml")
    if err != nil {
        return "", fmt.Errorf("read config: %w", err)
    }
    return string(data), nil
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88">// ❌ 错误：滥用 panic</span></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">readConfig</span><span style="color: #D8DEE9FF">() </span><span style="color: #D8DEE9">string</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">data</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">ReadFile</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">config.yaml</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">panic</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)  </span><span style="color: #616E88">// 直接崩溃，调用方无法处理</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">string</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// ✅ 正确：返回错误</span></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">readConfig</span><span style="color: #D8DEE9FF">() (</span><span style="color: #D8DEE9">string</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">data</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">ReadFile</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">config.yaml</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Errorf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">read config: %w</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">string</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<h2 class="wp-block-heading">2. 并发编程：goroutine 的正确打开方式</h2>



<h3 class="wp-block-heading">2.1 用 Context 控制 goroutine 生命周期</h3>



<p class="wp-block-paragraph">在 HTTP 服务或长时间运行的任务中，用 context 来控制 goroutine 的启动和停止是最标准的做法<a href="https://cloud.tencent.com.cn/developer/article/2566540?from=15425&amp;frompage=seopage" target="_blank" rel="noreferrer noopener"></a>：</p>



<p class="wp-block-paragraph">Go 的并发模型很简洁，但用不好容易出 goroutine 泄漏、数据竞争等问题。</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>package main

import (
    "context"
    "fmt"
    "time"
)

func worker(ctx context.Context, id int) {
    for {
        select {
        case &lt;-ctx.Done():
            fmt.Printf("worker %d 收到取消信号，退出\n", id)
            return
        default:
            fmt.Printf("worker %d 工作中...\n", id)
            time.Sleep(1 * time.Second)
        }
    }
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())

    // 启动 3 个 worker
    for i := 1; i &lt;= 3; i++ {
        go worker(ctx, i)
    }

    // 5 秒后取消所有 worker
    time.Sleep(5 * time.Second)
    cancel()

    // 给 worker 一点时间退出
    time.Sleep(500 * time.Millisecond)
    fmt.Println("main 退出")
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">package</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">main</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">import</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">context</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">fmt</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">time</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">worker</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">ctx</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">context</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">Context</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">id</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">for</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">select</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">case</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;-</span><span style="color: #D8DEE9">ctx</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Done</span><span style="color: #D8DEE9FF">():</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Printf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">worker %d 收到取消信号，退出</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">id</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">return</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">default</span><span style="color: #D8DEE9FF">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Printf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">worker %d 工作中...</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">id</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">time</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Sleep</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">1</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">time</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">Second</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">main</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">ctx</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> cancel </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">context</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">WithCancel</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">context</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Background</span><span style="color: #D8DEE9FF">())</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 启动 3 个 worker</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">for</span><span style="color: #D8DEE9FF"> i </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">3</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #81A1C1">++</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">go</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">worker</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">ctx</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 5 秒后取消所有 worker</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">time</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Sleep</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">5</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">time</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">Second</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">cancel</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 给 worker 一点时间退出</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">time</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Sleep</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">500</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">time</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">Millisecond</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Println</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">main 退出</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<h3 class="wp-block-heading">2.2 Channel + Actor 模型：通信共享内存，而非共享内存通信</h3>



<p class="wp-block-paragraph">Go 的哲学是“不要通过共享内存来通信，而要通过通信来共享内存”。把共享状态封装在一个 goroutine 内，通过 channel 传递操作指令，可以彻底避免数据竞争<a href="https://cloud.tencent.cn/developer/article/2562803" target="_blank" rel="noreferrer noopener"></a>：</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>package main

import (
    "fmt"
    "time"
)

// Counter 将计数状态封装在独立的 goroutine 中
type Counter struct {
    ops chan func(*int) // 操作函数队列
}

func NewCounter() *Counter {
    c := &amp;Counter{
        ops: make(chan func(*int)),
    }
    go c.loop()
    return c
}

func (c *Counter) loop() {
    var value int
    for op := range c.ops {
        op(&amp;value)
    }
}

// Increment 增加计数
func (c *Counter) Increment() {
    done := make(chan struct{})
    c.ops &lt;- func(v *int) {
        *v++
        close(done)
    }
    &lt;-done // 等待操作完成
}

// Value 获取当前值
func (c *Counter) Value() int {
    done := make(chan int)
    c.ops &lt;- func(v *int) {
        done &lt;- *v
    }
    return &lt;-done
}

func main() {
    counter := NewCounter()

    // 并发增加计数
    for i := 0; i &lt; 100; i++ {
        go counter.Increment()
    }

    time.Sleep(100 * time.Millisecond)
    fmt.Printf("最终计数: %d\n", counter.Value())
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">package</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">main</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">import</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">fmt</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">time</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// Counter 将计数状态封装在独立的 goroutine 中</span></span>
<span class="line"><span style="color: #81A1C1">type</span><span style="color: #D8DEE9FF"> Counter struct {</span></span>
<span class="line"><span style="color: #D8DEE9FF">    ops chan func(*int) </span><span style="color: #616E88">// 操作函数队列</span></span>
<span class="line"><span style="color: #D8DEE9FF">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">NewCounter</span><span style="color: #D8DEE9FF">() </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">Counter</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    c </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">Counter</span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        ops</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">make</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">chan</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">func</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF">))</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">go</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">c</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">loop</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">c</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">func</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">c</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">Counter</span><span style="color: #D8DEE9FF">) </span><span style="color: #88C0D0">loop</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">value</span><span style="color: #D8DEE9FF"> int</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">for</span><span style="color: #D8DEE9FF"> op </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">range</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">c</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">ops</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">op</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">value</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// Increment 增加计数</span></span>
<span class="line"><span style="color: #88C0D0">func</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">c</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">Counter</span><span style="color: #D8DEE9FF">) </span><span style="color: #88C0D0">Increment</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    done </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">make</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">chan</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">struct</span><span style="color: #ECEFF4">{}</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">c</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">ops</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;-</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">func</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">v</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">v</span><span style="color: #81A1C1">++</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">close</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">done</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;-</span><span style="color: #D8DEE9">done</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// 等待操作完成</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// Value 获取当前值</span></span>
<span class="line"><span style="color: #88C0D0">func</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">c</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">Counter</span><span style="color: #D8DEE9FF">) </span><span style="color: #88C0D0">Value</span><span style="color: #D8DEE9FF">() </span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    done </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">make</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">chan</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">c</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">ops</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;-</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">func</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">v</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">done</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;-</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">v</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;-</span><span style="color: #D8DEE9">done</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">main</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    counter </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">NewCounter</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 并发增加计数</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">for</span><span style="color: #D8DEE9FF"> i </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">100</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #81A1C1">++</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">go</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">counter</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Increment</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">time</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Sleep</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">100</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">time</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">Millisecond</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Printf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">最终计数: %d</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">counter</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Value</span><span style="color: #D8DEE9FF">())</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p class="wp-block-paragraph">这种模式本质上是 Actor 模型的 Go 实现，完全不用锁，代码也清晰易读<a href="https://cloud.tencent.cn/developer/article/2562803" target="_blank" rel="noreferrer noopener"></a>。</p>



<h3 class="wp-block-heading">2.3 Worker Pool 模式</h3>



<p class="wp-block-paragraph">当有大量任务需要并发处理时，用 worker pool 控制并发数可以避免资源耗尽：</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>package main

import (
    "fmt"
    "sync"
    "time"
)

func worker(id int, jobs &lt;-chan int, results chan&lt;- int, wg *sync.WaitGroup) {
    defer wg.Done()
    for job := range jobs {
        fmt.Printf("worker %d 处理任务 %d\n", id, job)
        time.Sleep(500 * time.Millisecond) // 模拟耗时操作
        results &lt;- job * 2
    }
}

func main() {
    const numJobs = 10
    const numWorkers = 3

    jobs := make(chan int, numJobs)
    results := make(chan int, numJobs)
    var wg sync.WaitGroup

    // 启动 worker 池
    for w := 1; w &lt;= numWorkers; w++ {
        wg.Add(1)
        go worker(w, jobs, results, &amp;wg)
    }

    // 发送任务
    for j := 1; j &lt;= numJobs; j++ {
        jobs &lt;- j
    }
    close(jobs)

    // 等待所有 worker 完成
    go func() {
        wg.Wait()
        close(results)
    }()

    // 收集结果
    for res := range results {
        fmt.Printf("结果: %d\n", res)
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">package</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">main</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">import</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">fmt</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">sync</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">time</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">worker</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">id</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">int</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">jobs</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;-</span><span style="color: #D8DEE9">chan</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">int</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">results</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">chan</span><span style="color: #81A1C1">&lt;-</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">int</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">wg</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">sync</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">WaitGroup</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">defer</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">wg</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Done</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">for</span><span style="color: #D8DEE9FF"> job </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">range</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">jobs</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Printf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">worker %d 处理任务 %d</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">id</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">job</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">time</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Sleep</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">500</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">time</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">Millisecond</span><span style="color: #D8DEE9FF">) </span><span style="color: #616E88">// 模拟耗时操作</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">results</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;-</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">job</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">2</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">main</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">numJobs</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">10</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">numWorkers</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">3</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    jobs </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">make</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">chan</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">int</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">numJobs</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    results </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">make</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">chan</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">int</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">numJobs</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">wg</span><span style="color: #D8DEE9FF"> sync.WaitGroup</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 启动 worker 池</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">for</span><span style="color: #D8DEE9FF"> w </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">w</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">numWorkers</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">w</span><span style="color: #81A1C1">++</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">wg</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Add</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">1</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">go</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">worker</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">w</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">jobs</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">results</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">wg</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 发送任务</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">for</span><span style="color: #D8DEE9FF"> j </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">j</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">numJobs</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">j</span><span style="color: #81A1C1">++</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">jobs</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;-</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">j</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">close</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">jobs</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 等待所有 worker 完成</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">go</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">func</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">wg</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Wait</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">close</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">results</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 收集结果</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">for</span><span style="color: #D8DEE9FF"> res </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">range</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">results</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Printf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">结果: %d</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">res</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<h2 class="wp-block-heading">3. 项目结构：告别代码混乱</h2>



<p class="wp-block-paragraph">随着项目变大，代码组织会成为头痛的问题。业界普遍参考 Standard Go Project Layout，但不必全盘照搬，可以按需取用<a href="https://blog.csdn.net/gitblog_00081/article/details/151199502" target="_blank" rel="noreferrer noopener"></a>。</p>



<p class="wp-block-paragraph">推荐一个中小型项目的结构：</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>myproject/
├── cmd/
│   └── api/
│       └── main.go          # 应用入口，尽量精简
├── internal/
│   ├── handler/             # HTTP 处理器
│   │   └── user.go
│   ├── service/             # 业务逻辑
│   │   └── user.go
│   ├── repository/          # 数据访问
│   │   └── user.go
│   └── model/               # 数据模型
│       └── user.go
├── pkg/                     # 可被外部引用的公共库
│   └── utils/
├── go.mod
└── go.sum</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">myproject</span><span style="color: #81A1C1">/</span></span>
<span class="line"><span style="color: #D8DEE9FF">├── </span><span style="color: #D8DEE9">cmd</span><span style="color: #81A1C1">/</span></span>
<span class="line"><span style="color: #D8DEE9FF">│   └── </span><span style="color: #D8DEE9">api</span><span style="color: #81A1C1">/</span></span>
<span class="line"><span style="color: #D8DEE9FF">│       └── </span><span style="color: #D8DEE9">main</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">go</span><span style="color: #D8DEE9FF">          # </span><span style="color: #D8DEE9">应用入口</span><span style="color: #D8DEE9FF">，</span><span style="color: #D8DEE9">尽量精简</span></span>
<span class="line"><span style="color: #D8DEE9FF">├── </span><span style="color: #D8DEE9">internal</span><span style="color: #81A1C1">/</span></span>
<span class="line"><span style="color: #D8DEE9FF">│   ├── </span><span style="color: #D8DEE9">handler</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9FF">             # </span><span style="color: #D8DEE9">HTTP</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">处理器</span></span>
<span class="line"><span style="color: #D8DEE9FF">│   │   └── </span><span style="color: #D8DEE9">user</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">go</span></span>
<span class="line"><span style="color: #D8DEE9FF">│   ├── </span><span style="color: #D8DEE9">service</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9FF">             # </span><span style="color: #D8DEE9">业务逻辑</span></span>
<span class="line"><span style="color: #D8DEE9FF">│   │   └── </span><span style="color: #D8DEE9">user</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">go</span></span>
<span class="line"><span style="color: #D8DEE9FF">│   ├── </span><span style="color: #D8DEE9">repository</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9FF">          # </span><span style="color: #D8DEE9">数据访问</span></span>
<span class="line"><span style="color: #D8DEE9FF">│   │   └── </span><span style="color: #D8DEE9">user</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">go</span></span>
<span class="line"><span style="color: #D8DEE9FF">│   └── </span><span style="color: #D8DEE9">model</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9FF">               # </span><span style="color: #D8DEE9">数据模型</span></span>
<span class="line"><span style="color: #D8DEE9FF">│       └── </span><span style="color: #D8DEE9">user</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">go</span></span>
<span class="line"><span style="color: #D8DEE9FF">├── </span><span style="color: #D8DEE9">pkg</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9FF">                     # </span><span style="color: #D8DEE9">可被外部引用的公共库</span></span>
<span class="line"><span style="color: #D8DEE9FF">│   └── </span><span style="color: #D8DEE9">utils</span><span style="color: #81A1C1">/</span></span>
<span class="line"><span style="color: #D8DEE9FF">├── </span><span style="color: #D8DEE9">go</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">mod</span></span>
<span class="line"><span style="color: #D8DEE9FF">└── </span><span style="color: #D8DEE9">go</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">sum</span></span></code></pre></div>



<p class="wp-block-paragraph">要点：</p>



<ul class="wp-block-list">
<li><code>cmd/</code>&nbsp;下放&nbsp;<code>main.go</code>，每个子目录对应一个可执行程序</li>



<li><code>internal/</code>&nbsp;下的代码只有本项目能引用，Go 编译器会强制保证这一点</li>



<li><code>pkg/</code>&nbsp;放可以被其他项目引用的公共代码</li>



<li>main 函数尽量简短，业务逻辑都放在&nbsp;<code>internal</code>&nbsp;中</li>
</ul>



<p class="wp-block-paragraph"><code>internal/</code>&nbsp;内部按功能分层是一个经典做法，但如果你偏好 Clean Architecture，也可以按领域组织：<br></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>internal/
├── user/           # 用户领域的所有代码
│   ├── delivery.go  # HTTP handler
│   ├── service.go   # 业务逻辑
│   └── repository.go # 数据层
└── order/          # 订单领域
    └── ...</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">internal</span><span style="color: #81A1C1">/</span></span>
<span class="line"><span style="color: #D8DEE9FF">├── </span><span style="color: #D8DEE9">user</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9FF">           # </span><span style="color: #D8DEE9">用户领域的所有代码</span></span>
<span class="line"><span style="color: #D8DEE9FF">│   ├── </span><span style="color: #D8DEE9">delivery</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">go</span><span style="color: #D8DEE9FF">  # </span><span style="color: #D8DEE9">HTTP</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">handler</span></span>
<span class="line"><span style="color: #D8DEE9FF">│   ├── </span><span style="color: #D8DEE9">service</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">go</span><span style="color: #D8DEE9FF">   # </span><span style="color: #D8DEE9">业务逻辑</span></span>
<span class="line"><span style="color: #D8DEE9FF">│   └── </span><span style="color: #D8DEE9">repository</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">go</span><span style="color: #D8DEE9FF"> # </span><span style="color: #D8DEE9">数据层</span></span>
<span class="line"><span style="color: #D8DEE9FF">└── </span><span style="color: #D8DEE9">order</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9FF">          # </span><span style="color: #D8DEE9">订单领域</span></span>
<span class="line"><span style="color: #D8DEE9FF">    └── </span><span style="color: #81A1C1">...</span></span></code></pre></div>



<p class="wp-block-paragraph">选择哪种结构取决于团队习惯和项目规模，关键是保持一致。</p>



<h2 class="wp-block-heading">4. Gin 框架：快速构建 Web 服务</h2>



<p class="wp-block-paragraph">Gin 是 Go 生态里使用最广泛的 Web 框架之一，性能好，中间件机制也很方便。</p>



<h3 class="wp-block-heading">4.1 基础用法</h3>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>package main

import (
    "net/http"
    "github.com/gin-gonic/gin"
)

type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

func main() {
    r := gin.Default()

    // 路由分组
    v1 := r.Group("/api/v1")
    {
        v1.GET("/ping", func(c *gin.Context) {
            c.JSON(http.StatusOK, gin.H{"message": "pong"})
        })

        v1.GET("/users/:id", func(c *gin.Context) {
            id := c.Param("id")
            c.JSON(http.StatusOK, User{ID: 1, Name: "张三"})
        })

        v1.POST("/users", func(c *gin.Context) {
            var user User
            if err := c.ShouldBindJSON(&amp;user); err != nil {
                c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
                return
            }
            // 处理业务...
            c.JSON(http.StatusCreated, user)
        })
    }

    r.Run(":8080")
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">package</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">main</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">import</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">net/http</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">github.com/gin-gonic/gin</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">type</span><span style="color: #D8DEE9FF"> User struct {</span></span>
<span class="line"><span style="color: #D8DEE9FF">    ID   int    `json:&quot;id&quot;`</span></span>
<span class="line"><span style="color: #D8DEE9FF">    Name string `json:&quot;name&quot;`</span></span>
<span class="line"><span style="color: #D8DEE9FF">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">main</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    r </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">gin</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Default</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 路由分组</span></span>
<span class="line"><span style="color: #D8DEE9FF">    v1 </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">r</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Group</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">/api/v1</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">v1</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">GET</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">/ping</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">func</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">c</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">gin</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">Context</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            c.JSON(http.</span><span style="color: #D8DEE9">StatusOK</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> gin.H</span><span style="color: #ECEFF4">{</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">message</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">: </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">pong</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">v1</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">GET</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">/users/:id</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">func</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">c</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">gin</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">Context</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">id </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">c</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Param</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">id</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">c</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">JSON</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">http</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">StatusOK</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">User</span><span style="color: #ECEFF4">{</span><span style="color: #88C0D0">ID</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">Name</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">张三</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">v1</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">POST</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">/users</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">func</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">c</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">gin</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">Context</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            var user </span><span style="color: #D8DEE9">User</span></span>
<span class="line"><span style="color: #D8DEE9FF">            if </span><span style="color: #88C0D0">err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">c</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">ShouldBindJSON</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">user</span><span style="color: #D8DEE9FF">); </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                c.JSON(http.</span><span style="color: #D8DEE9">StatusBadRequest</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> gin.H</span><span style="color: #ECEFF4">{</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">error</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">: </span><span style="color: #D8DEE9">err</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Error</span><span style="color: #D8DEE9FF">()</span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">return</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">            </span><span style="color: #616E88">// 处理业务...</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">c</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">JSON</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">http</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">StatusCreated</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">user</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">r</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Run</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">:8080</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<h3 class="wp-block-heading">4.2 自定义中间件</h3>



<p class="wp-block-paragraph">中间件是 Gin 的精华，可以用它来做日志、鉴权、限流等<a href="https://cloud.baidu.com/article/3694199" target="_blank" rel="noreferrer noopener"></a>：</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>package main

import (
    "log"
    "time"
    "github.com/gin-gonic/gin"
)

// 日志中间件：记录请求耗时和状态码
func LoggerMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()
        path := c.Request.URL.Path

        c.Next() // 执行后续处理器

        latency := time.Since(start)
        status := c.Writer.Status()

        log.Printf("&#91;%s&#93; %s %d %v", c.Request.Method, path, status, latency)
    }
}

// 鉴权中间件：简单 token 校验
func AuthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        token := c.GetHeader("Authorization")
        if token != "Bearer secret-token" {
            c.JSON(401, gin.H{"error": "未授权"})
            c.Abort() // 中断请求链
            return
        }
        c.Next()
    }
}

func main() {
    r := gin.Default()

    // 全局中间件
    r.Use(LoggerMiddleware())

    // 需要鉴权的路由组
    authGroup := r.Group("/api")
    authGroup.Use(AuthMiddleware())
    {
        authGroup.GET("/profile", func(c *gin.Context) {
            c.JSON(200, gin.H{"user": "张三"})
        })
    }

    r.Run(":8080")
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">package</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">main</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">import</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">log</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">time</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">github.com/gin-gonic/gin</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// 日志中间件：记录请求耗时和状态码</span></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">LoggerMiddleware</span><span style="color: #D8DEE9FF">() </span><span style="color: #D8DEE9">gin</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">HandlerFunc</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">func</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">c</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">gin</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">Context</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">start </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">time</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Now</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">path</span><span style="color: #D8DEE9FF"> :</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">c</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">Request</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">URL</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">Path</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">c</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Next</span><span style="color: #D8DEE9FF">() </span><span style="color: #616E88">// 执行后续处理器</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">latency</span><span style="color: #D8DEE9FF"> :</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">time</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Since</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">start</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">status</span><span style="color: #D8DEE9FF"> :</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">c</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">Writer</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Status</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Printf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">&#91;%s&#93; %s %d %v</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">c</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">Request</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">Method</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">path</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">status</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">latency</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// 鉴权中间件：简单 token 校验</span></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">AuthMiddleware</span><span style="color: #D8DEE9FF">() </span><span style="color: #D8DEE9">gin</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">HandlerFunc</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">func</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">c</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">gin</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">Context</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">token </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">c</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">GetHeader</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Authorization</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">token</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Bearer secret-token</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            c.JSON(</span><span style="color: #B48EAD">401</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> gin.H</span><span style="color: #ECEFF4">{</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">error</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">: </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">未授权</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            c.Abort() </span><span style="color: #616E88">// 中断请求链</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">return</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">c</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Next</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">main</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    r </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">gin</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Default</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 全局中间件</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">r</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Use</span><span style="color: #D8DEE9FF">(</span><span style="color: #88C0D0">LoggerMiddleware</span><span style="color: #D8DEE9FF">())</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 需要鉴权的路由组</span></span>
<span class="line"><span style="color: #D8DEE9FF">    authGroup </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">r</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Group</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">/api</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">authGroup</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Use</span><span style="color: #D8DEE9FF">(</span><span style="color: #88C0D0">AuthMiddleware</span><span style="color: #D8DEE9FF">())</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">authGroup</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">GET</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">/profile</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">func</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">c</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">gin</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">Context</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            c.JSON(</span><span style="color: #B48EAD">200</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> gin.H</span><span style="color: #ECEFF4">{</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">user</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">: </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">张三</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">r</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Run</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">:8080</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p class="wp-block-paragraph">中间件执行顺序遵循“先进后出”：最后注册的中间件最先执行前置逻辑，最先执行后置逻辑。理解这个机制可以帮助你写出更灵活的中间件组合。</p>



<h2 class="wp-block-heading">5. 常见坑点与最佳实践</h2>



<h3 class="wp-block-heading">5.1 nil 的判断陷阱</h3>



<p class="wp-block-paragraph">Go 中，接口类型的 nil 和具体类型的 nil 并不等价，这是最常见的坑之一<a href="https://developer.aliyun.com/article/1712638" target="_blank" rel="noreferrer noopener"></a>：</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>package main

import "fmt"

type MyError struct{}

func (e *MyError) Error() string {
    return "my error"
}

func returnError() error {
    var err *MyError = nil
    return err // 返回的 error 接口不为 nil！
}

func main() {
    err := returnError()
    if err != nil {
        fmt.Printf("err 不为 nil: %v\n", err) // 这里会执行！
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">package</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">main</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">fmt</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">type</span><span style="color: #D8DEE9FF"> MyError struct{}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">func</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">e</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">MyError</span><span style="color: #D8DEE9FF">) </span><span style="color: #88C0D0">Error</span><span style="color: #D8DEE9FF">() </span><span style="color: #D8DEE9">string</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">my error</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">returnError</span><span style="color: #D8DEE9FF">() </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> *MyError </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// 返回的 error 接口不为 nil！</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">main</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">returnError</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Printf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">err 不为 nil: %v</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">) </span><span style="color: #616E88">// 这里会执行！</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p class="wp-block-paragraph">解决方案：函数返回错误时，直接&nbsp;<code>return nil</code>，而不是返回一个值为 nil 的变量。</p>



<h3 class="wp-block-heading">5.2 循环变量捕获</h3>



<p class="wp-block-paragraph">在 for 循环中启动 goroutine 时，要注意闭包捕获的是同一个循环变量：</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>// ❌ 错误：所有 goroutine 都会打印最后一个值
for i := 0; i &lt; 5; i++ {
    go func() {
        fmt.Println(i)
    }()
}

// ✅ 正确：通过参数传递当前值
for i := 0; i &lt; 5; i++ {
    go func(n int) {
        fmt.Println(n)
    }(i)
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88">// ❌ 错误：所有 goroutine 都会打印最后一个值</span></span>
<span class="line"><span style="color: #D8DEE9">for</span><span style="color: #D8DEE9FF"> i </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">5</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #81A1C1">++</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">go</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">func</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Println</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// ✅ 正确：通过参数传递当前值</span></span>
<span class="line"><span style="color: #D8DEE9">for</span><span style="color: #D8DEE9FF"> i </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">5</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #81A1C1">++</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">go</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">func</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">n</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Println</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">n</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<h3 class="wp-block-heading">5.3 依赖显式化，避免全局变量</h3>



<p class="wp-block-paragraph">日志、配置等依赖应该通过构造函数注入，而不是使用全局变量，这样代码更易于测试和维护<a href="https://developer.aliyun.com/article/1712638" target="_blank" rel="noreferrer noopener"></a>：</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>// ❌ 依赖隐式全局
var logger = log.New(os.Stdout, "", log.LstdFlags)

type Service struct{}

func (s *Service) DoSomething() {
    logger.Println("doing something") // 无法替换 logger
}

// ✅ 依赖显式注入
type Service struct {
    logger *log.Logger
}

func NewService(logger *log.Logger) *Service {
    return &amp;Service{logger: logger}
}

func (s *Service) DoSomething() {
    s.logger.Println("doing something") // 可控、可测
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88">// ❌ 依赖隐式全局</span></span>
<span class="line"><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">logger</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">New</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">Stdout</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">LstdFlags</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">type</span><span style="color: #D8DEE9FF"> Service struct{}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">func</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">s</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">Service</span><span style="color: #D8DEE9FF">) </span><span style="color: #88C0D0">DoSomething</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">logger</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Println</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">doing something</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">) </span><span style="color: #616E88">// 无法替换 logger</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// ✅ 依赖显式注入</span></span>
<span class="line"><span style="color: #81A1C1">type</span><span style="color: #D8DEE9FF"> Service struct {</span></span>
<span class="line"><span style="color: #D8DEE9FF">    logger *log.Logger</span></span>
<span class="line"><span style="color: #D8DEE9FF">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">NewService</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">logger</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">Logger</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">Service</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">Service</span><span style="color: #ECEFF4">{</span><span style="color: #88C0D0">logger</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">logger</span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">func</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">s</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">Service</span><span style="color: #D8DEE9FF">) </span><span style="color: #88C0D0">DoSomething</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">s</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">logger</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Println</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">doing something</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">) </span><span style="color: #616E88">// 可控、可测</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<h2 class="wp-block-heading">6. 开发效率工具</h2>



<h3 class="wp-block-heading">6.1 必备命令</h3>



<p class="wp-block-paragraph">几个几乎每天都会用到的命令<a href="https://datasea.cn/go0326556223.html" target="_blank" rel="noreferrer noopener"></a>：</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly># 格式化代码（必须，团队协作的底线）
gofmt -w .

# 静态检查，发现潜在 bug
go vet ./...

# 竞态检测，并发测试必开
go test -race ./...

# 整理依赖
go mod tidy

# 查看逃逸分析
go build -gcflags="-m"</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF"># </span><span style="color: #D8DEE9">格式化代码</span><span style="color: #D8DEE9FF">（</span><span style="color: #D8DEE9">必须</span><span style="color: #D8DEE9FF">，</span><span style="color: #D8DEE9">团队协作的底线</span><span style="color: #D8DEE9FF">）</span></span>
<span class="line"><span style="color: #D8DEE9">gofmt</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9">w</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">.</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"># </span><span style="color: #D8DEE9">静态检查</span><span style="color: #D8DEE9FF">，</span><span style="color: #D8DEE9">发现潜在</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">bug</span></span>
<span class="line"><span style="color: #D8DEE9">go</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">vet</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">.</span><span style="color: #81A1C1">/...</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"># </span><span style="color: #D8DEE9">竞态检测</span><span style="color: #D8DEE9FF">，</span><span style="color: #D8DEE9">并发测试必开</span></span>
<span class="line"><span style="color: #D8DEE9">go</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">test</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9">race</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">.</span><span style="color: #81A1C1">/...</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"># </span><span style="color: #D8DEE9">整理依赖</span></span>
<span class="line"><span style="color: #D8DEE9">go</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">mod</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tidy</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"># </span><span style="color: #D8DEE9">查看逃逸分析</span></span>
<span class="line"><span style="color: #D8DEE9">go</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">build</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9">gcflags</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">-m</span><span style="color: #ECEFF4">&quot;</span></span></code></pre></div>



<h3 class="wp-block-heading">6.2 用好 pprof 做性能分析</h3>



<p class="wp-block-paragraph">Go 自带的 pprof 是性能调优的利器<a href="https://www.yisu.com/ask/95460261.html" target="_blank" rel="noreferrer noopener"></a>：</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>package main

import (
    "log"
    "net/http"
    _ "net/http/pprof"
)

func main() {
    // 启动 pprof HTTP 服务
    go func() {
        log.Println(http.ListenAndServe("localhost:6060", nil))
    }()

    // 你的业务代码...
    select {}
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">package</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">main</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">import</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">log</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">net/http</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">_</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">net/http/pprof</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">main</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 启动 pprof HTTP 服务</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">go</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">func</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Println</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">http</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">ListenAndServe</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">localhost:6060</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 你的业务代码...</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">select</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p class="wp-block-paragraph">运行后访问&nbsp;<code>http://localhost:6060/debug/pprof/</code>&nbsp;就能看到 CPU、内存、goroutine 等各项指标。</p>



<h2 class="wp-block-heading">总结</h2>



<p class="wp-block-paragraph">Go 语言的魅力在于简洁和务实。写 Go 多年，最大的感悟是：好的 Go 代码不是玩出花活，而是让读代码的人一眼能看懂、改起来不害怕。最后送大家一句话：<strong>Go 爱诚实的人，别藏着掖着，也别假装看不见错误。</strong></p>
<p>The post <a href="https://www.atomic-cube.cn/go-%e5%bc%80%e5%8f%91%e7%bb%8f%e9%aa%8c%e6%80%bb%e7%bb%93%ef%bc%9a%e4%bb%8e%e5%85%a5%e9%97%a8%e5%88%b0%e5%86%99%e5%87%ba%e7%94%9f%e4%ba%a7%e7%ba%a7%e4%bb%a3%e7%a0%81/"> Go 开发经验总结：从入门到写出生产级代码</a> appeared first on <a href="https://www.atomic-cube.cn">原立方</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.atomic-cube.cn/go-%e5%bc%80%e5%8f%91%e7%bb%8f%e9%aa%8c%e6%80%bb%e7%bb%93%ef%bc%9a%e4%bb%8e%e5%85%a5%e9%97%a8%e5%88%b0%e5%86%99%e5%87%ba%e7%94%9f%e4%ba%a7%e7%ba%a7%e4%bb%a3%e7%a0%81/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Go 文件和文件夹工具类：一个开箱即用的 fileutil 包</title>
		<link>https://www.atomic-cube.cn/go-%e6%96%87%e4%bb%b6%e5%92%8c%e6%96%87%e4%bb%b6%e5%a4%b9%e5%b7%a5%e5%85%b7%e7%b1%bb%ef%bc%9a%e4%b8%80%e4%b8%aa%e5%bc%80%e7%ae%b1%e5%8d%b3%e7%94%a8%e7%9a%84-fileutil-%e5%8c%85/</link>
					<comments>https://www.atomic-cube.cn/go-%e6%96%87%e4%bb%b6%e5%92%8c%e6%96%87%e4%bb%b6%e5%a4%b9%e5%b7%a5%e5%85%b7%e7%b1%bb%ef%bc%9a%e4%b8%80%e4%b8%aa%e5%bc%80%e7%ae%b1%e5%8d%b3%e7%94%a8%e7%9a%84-fileutil-%e5%8c%85/#respond</comments>
		
		<dc:creator><![CDATA[evans]]></dc:creator>
		<pubDate>Fri, 20 Mar 2026 07:28:00 +0000</pubDate>
				<category><![CDATA[Gin]]></category>
		<category><![CDATA[go-zero]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[技术文章]]></category>
		<category><![CDATA[Go-zero]]></category>
		<guid isPermaLink="false">https://www.atomic-cube.cn/?p=1510</guid>

					<description><![CDATA[<p>在日常开发中，文件与目录操作是高频需求。虽然 Go 标准库已经提供了&#160;os、path/filepath&#160;等基础能力，但直接使用仍需处理大量边界条件。本文将封装一个实用的&#160;fileutil&#160;工具包，涵盖常用操作：路径存在判断、目录创建、文件复制、目录复制、移动、删除、遍历、大小统计等。每个方法都有详细的注释和错误处理，可直接集成到项目中。 完整代码可在文末获取，或直接复制使用。 功能概览 核心代码：fileutil 包实现 使用示例 要点说明 函数 注意事项 CopyFile 覆盖已存在的目标文件；保留源文件权限 MoveFile 跨文件系统自动降级为复制+删除 CopyDir 递归复制，目标目录存在时会合并内容 SafeWrite 原子写入，先写临时文件再 rename，防止写一半时进程崩溃导致数据损坏 ListFiles 支持按扩展名过滤，返回绝对路径 EnsureDir 已存在但不是目录时报错，避免误用 这个工具包覆盖了日常开发中 90% 的文件目录操作场景。你可以根据项目需要继续扩展，比如增加压缩/解压、权限批量修改等功能。直接拷贝到项目中使用即可。</p>
<p>The post <a href="https://www.atomic-cube.cn/go-%e6%96%87%e4%bb%b6%e5%92%8c%e6%96%87%e4%bb%b6%e5%a4%b9%e5%b7%a5%e5%85%b7%e7%b1%bb%ef%bc%9a%e4%b8%80%e4%b8%aa%e5%bc%80%e7%ae%b1%e5%8d%b3%e7%94%a8%e7%9a%84-fileutil-%e5%8c%85/">Go 文件和文件夹工具类：一个开箱即用的 fileutil 包</a> appeared first on <a href="https://www.atomic-cube.cn">原立方</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">在日常开发中，文件与目录操作是高频需求。虽然 Go 标准库已经提供了&nbsp;<code>os</code>、<code>path/filepath</code>&nbsp;等基础能力，但直接使用仍需处理大量边界条件。本文将封装一个实用的&nbsp;<code>fileutil</code>&nbsp;工具包，涵盖常用操作：路径存在判断、目录创建、文件复制、目录复制、移动、删除、遍历、大小统计等。每个方法都有详细的注释和错误处理，可直接集成到项目中。</p>



<p class="wp-block-paragraph"><strong>完整代码可在文末获取，或直接复制使用。</strong></p>



<h2 class="wp-block-heading">功能概览</h2>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="128" src="https://www.atomic-cube.cn/wp-content/uploads/2026/04/file1-1024x128.png" alt="" class="wp-image-1511" srcset="https://www.atomic-cube.cn/wp-content/uploads/2026/04/file1-1024x128.png 1024w, https://www.atomic-cube.cn/wp-content/uploads/2026/04/file1-300x38.png 300w, https://www.atomic-cube.cn/wp-content/uploads/2026/04/file1-768x96.png 768w, https://www.atomic-cube.cn/wp-content/uploads/2026/04/file1-1536x192.png 1536w, https://www.atomic-cube.cn/wp-content/uploads/2026/04/file1-2048x257.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">核心代码：fileutil 包实现<br></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(3 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>// Package fileutil 提供文件和目录操作的常用工具函数
package fileutil

import (
    "fmt"
    "io"
    "os"
    "path/filepath"
    "strings"
)

// Exists 判断路径是否存在（文件或目录均适用）
func Exists(path string) bool {
    _, err := os.Stat(path)
    return err == nil || !os.IsNotExist(err)
}

// IsDir 判断给定路径是否为目录
func IsDir(path string) (bool, error) {
    info, err := os.Stat(path)
    if err != nil {
        return false, err
    }
    return info.IsDir(), nil
}

// IsFile 判断给定路径是否为普通文件
func IsFile(path string) (bool, error) {
    info, err := os.Stat(path)
    if err != nil {
        return false, err
    }
    return !info.IsDir(), nil
}

// EnsureDir 确保目录存在，如果不存在则递归创建。
// 若已存在但不是目录，返回错误。
func EnsureDir(path string) error {
    if Exists(path) {
        isDir, err := IsDir(path)
        if err != nil {
            return err
        }
        if !isDir {
            return fmt.Errorf("path exists but is not a directory: %s", path)
        }
        return nil
    }
    return os.MkdirAll(path, 0755)
}

// CopyFile 复制单个文件从 src 到 dst。
// 如果 dst 已存在，默认会被覆盖。
// 复制时会保留源文件的权限模式。
func CopyFile(src, dst string) error {
    // 打开源文件
    srcFile, err := os.Open(src)
    if err != nil {
        return fmt.Errorf("open source file: %w", err)
    }
    defer srcFile.Close()

    // 获取源文件信息（用于保留权限）
    srcInfo, err := srcFile.Stat()
    if err != nil {
        return fmt.Errorf("stat source file: %w", err)
    }

    // 创建目标文件
    dstFile, err := os.Create(dst)
    if err != nil {
        return fmt.Errorf("create destination file: %w", err)
    }
    defer dstFile.Close()

    // 复制内容
    if _, err := io.Copy(dstFile, srcFile); err != nil {
        return fmt.Errorf("copy content: %w", err)
    }

    // 同步写入到磁盘
    if err := dstFile.Sync(); err != nil {
        return fmt.Errorf("sync destination file: %w", err)
    }

    // 保留源文件的权限模式
    return os.Chmod(dst, srcInfo.Mode())
}

// MoveFile 移动文件（也可用于重命名）。
// 如果跨文件系统移动失败，会自动降级为复制后删除。
func MoveFile(src, dst string) error {
    // 先尝试直接 rename（同一文件系统内最快）
    err := os.Rename(src, dst)
    if err == nil {
        return nil
    }

    // 跨文件系统时，采用复制+删除
    if err := CopyFile(src, dst); err != nil {
        return fmt.Errorf("copy file during move: %w", err)
    }
    if err := os.Remove(src); err != nil {
        // 复制成功但删除失败，目标文件已存在，记录错误但不回滚（避免数据丢失）
        return fmt.Errorf("remove source file after copy: %w", err)
    }
    return nil
}

// CopyDir 递归复制整个目录从 src 到 dst。
// 如果 dst 已存在且是目录，内容会合并；否则会先创建。
func CopyDir(src, dst string) error {
    // 获取源目录信息
    srcInfo, err := os.Stat(src)
    if err != nil {
        return fmt.Errorf("stat source directory: %w", err)
    }
    if !srcInfo.IsDir() {
        return fmt.Errorf("source is not a directory: %s", src)
    }

    // 创建目标目录（保留源目录权限）
    if err := os.MkdirAll(dst, srcInfo.Mode()); err != nil {
        return fmt.Errorf("create destination directory: %w", err)
    }

    // 遍历源目录
    entries, err := os.ReadDir(src)
    if err != nil {
        return fmt.Errorf("read source directory: %w", err)
    }

    for _, entry := range entries {
        srcPath := filepath.Join(src, entry.Name())
        dstPath := filepath.Join(dst, entry.Name())

        if entry.IsDir() {
            // 递归复制子目录
            if err := CopyDir(srcPath, dstPath); err != nil {
                return err
            }
        } else {
            // 复制文件
            if err := CopyFile(srcPath, dstPath); err != nil {
                return err
            }
        }
    }
    return nil
}

// DeleteDir 删除目录及其所有内容（类似 rm -rf）
func DeleteDir(path string) error {
    if !Exists(path) {
        return nil // 幂等：已不存在即成功
    }
    return os.RemoveAll(path)
}

// ListFiles 递归列出目录下所有文件的绝对路径。
// 如果指定了 extensions（例如 []string{".go", ".txt"}），则只返回匹配的文件。
// 传 nil 或空切片则返回所有文件。
func ListFiles(dir string, extensions []string) ([]string, error) {
    var files []string
    err := filepath.WalkDir(dir, func(path string, d os.DirEntry, err error) error {
        if err != nil {
            return err
        }
        if d.IsDir() {
            return nil
        }

        // 过滤扩展名
        if len(extensions) > 0 {
            ext := strings.ToLower(filepath.Ext(path))
            matched := false
            for _, e := range extensions {
                if strings.ToLower(e) == ext {
                    matched = true
                    break
                }
            }
            if !matched {
                return nil
            }
        }

        absPath, err := filepath.Abs(path)
        if err != nil {
            return err
        }
        files = append(files, absPath)
        return nil
    })
    return files, err
}

// DirSize 计算目录占用磁盘总大小（递归包含子目录）
func DirSize(path string) (int64, error) {
    var size int64
    err := filepath.WalkDir(path, func(_ string, d os.DirEntry, err error) error {
        if err != nil {
            return err
        }
        if !d.IsDir() {
            info, err := d.Info()
            if err != nil {
                return err
            }
            size += info.Size()
        }
        return nil
    })
    return size, err
}

// ReadString 快速读取整个文件为字符串
func ReadString(path string) (string, error) {
    data, err := os.ReadFile(path)
    if err != nil {
        return "", err
    }
    return string(data), nil
}

// WriteString 快速将字符串写入文件（覆盖写入，权限 0644）
func WriteString(path, content string) error {
    return os.WriteFile(path, []byte(content), 0644)
}

// AppendString 追加字符串到文件末尾，如果文件不存在则创建
func AppendString(path, content string) error {
    f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
    if err != nil {
        return err
    }
    defer f.Close()
    _, err = f.WriteString(content)
    return err
}

// SafeWrite 原子性写入：先写临时文件，再 rename 替换，避免写入中途崩溃导致文件损坏。
func SafeWrite(path string, data []byte, perm os.FileMode) error {
    // 在同目录下创建临时文件
    tmpFile, err := os.CreateTemp(filepath.Dir(path), ".tmp-*")
    if err != nil {
        return err
    }
    tmpName := tmpFile.Name()

    // 写入数据
    if _, err := tmpFile.Write(data); err != nil {
        tmpFile.Close()
        os.Remove(tmpName)
        return err
    }
    if err := tmpFile.Sync(); err != nil {
        tmpFile.Close()
        os.Remove(tmpName)
        return err
    }
    if err := tmpFile.Close(); err != nil {
        os.Remove(tmpName)
        return err
    }

    // 设置权限
    if err := os.Chmod(tmpName, perm); err != nil {
        os.Remove(tmpName)
        return err
    }

    // 原子替换
    return os.Rename(tmpName, path)
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88">// Package fileutil 提供文件和目录操作的常用工具函数</span></span>
<span class="line"><span style="color: #81A1C1">package</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fileutil</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">import</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">fmt</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">io</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">os</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">path/filepath</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">strings</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// Exists 判断路径是否存在（文件或目录均适用）</span></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">Exists</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">string</span><span style="color: #D8DEE9FF">) </span><span style="color: #D8DEE9">bool</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">_</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Stat</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">==</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">||</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!</span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">IsNotExist</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// IsDir 判断给定路径是否为目录</span></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">IsDir</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">string</span><span style="color: #D8DEE9FF">) (</span><span style="color: #D8DEE9">bool</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">info</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Stat</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">info</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">IsDir</span><span style="color: #D8DEE9FF">()</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// IsFile 判断给定路径是否为普通文件</span></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">IsFile</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">string</span><span style="color: #D8DEE9FF">) (</span><span style="color: #D8DEE9">bool</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">info</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Stat</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!</span><span style="color: #D8DEE9">info</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">IsDir</span><span style="color: #D8DEE9FF">()</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// EnsureDir 确保目录存在，如果不存在则递归创建。</span></span>
<span class="line"><span style="color: #616E88">// 若已存在但不是目录，返回错误。</span></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">EnsureDir</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">string</span><span style="color: #D8DEE9FF">) </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">Exists</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">isDir</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">IsDir</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!</span><span style="color: #D8DEE9">isDir</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Errorf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">path exists but is not a directory: %s</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">path</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">MkdirAll</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0755</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// CopyFile 复制单个文件从 src 到 dst。</span></span>
<span class="line"><span style="color: #616E88">// 如果 dst 已存在，默认会被覆盖。</span></span>
<span class="line"><span style="color: #616E88">// 复制时会保留源文件的权限模式。</span></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">CopyFile</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">src</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">dst</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">string</span><span style="color: #D8DEE9FF">) </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 打开源文件</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">srcFile</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Open</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">src</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Errorf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">open source file: %w</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">defer</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">srcFile</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Close</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 获取源文件信息（用于保留权限）</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">srcInfo</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">srcFile</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Stat</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Errorf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">stat source file: %w</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 创建目标文件</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">dstFile</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Create</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">dst</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Errorf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">create destination file: %w</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">defer</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">dstFile</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Close</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 复制内容</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">_</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">io</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Copy</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">dstFile</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">srcFile</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Errorf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">copy content: %w</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 同步写入到磁盘</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">dstFile</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Sync</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Errorf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">sync destination file: %w</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 保留源文件的权限模式</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Chmod</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">dst</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">srcInfo</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Mode</span><span style="color: #D8DEE9FF">())</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// MoveFile 移动文件（也可用于重命名）。</span></span>
<span class="line"><span style="color: #616E88">// 如果跨文件系统移动失败，会自动降级为复制后删除。</span></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">MoveFile</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">src</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">dst</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">string</span><span style="color: #D8DEE9FF">) </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 先尝试直接 rename（同一文件系统内最快）</span></span>
<span class="line"><span style="color: #D8DEE9FF">    err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Rename</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">src</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">dst</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">==</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 跨文件系统时，采用复制+删除</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">CopyFile</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">src</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">dst</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Errorf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">copy file during move: %w</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Remove</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">src</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// 复制成功但删除失败，目标文件已存在，记录错误但不回滚（避免数据丢失）</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Errorf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">remove source file after copy: %w</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// CopyDir 递归复制整个目录从 src 到 dst。</span></span>
<span class="line"><span style="color: #616E88">// 如果 dst 已存在且是目录，内容会合并；否则会先创建。</span></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">CopyDir</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">src</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">dst</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">string</span><span style="color: #D8DEE9FF">) </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 获取源目录信息</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">srcInfo</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Stat</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">src</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Errorf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">stat source directory: %w</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!</span><span style="color: #D8DEE9">srcInfo</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">IsDir</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Errorf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">source is not a directory: %s</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">src</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 创建目标目录（保留源目录权限）</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">MkdirAll</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">dst</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">srcInfo</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Mode</span><span style="color: #D8DEE9FF">())</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Errorf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">create destination directory: %w</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 遍历源目录</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">entries</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">ReadDir</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">src</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Errorf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">read source directory: %w</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">for</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">_</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> entry </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">range</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">entries</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        srcPath </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">filepath</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Join</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">src</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">entry</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Name</span><span style="color: #D8DEE9FF">())</span></span>
<span class="line"><span style="color: #D8DEE9FF">        dstPath </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">filepath</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Join</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">dst</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">entry</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Name</span><span style="color: #D8DEE9FF">())</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">entry</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">IsDir</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">            </span><span style="color: #616E88">// 递归复制子目录</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">CopyDir</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">srcPath</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">dstPath</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">else</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">            </span><span style="color: #616E88">// 复制文件</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">CopyFile</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">srcPath</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">dstPath</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// DeleteDir 删除目录及其所有内容（类似 rm -rf）</span></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">DeleteDir</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">string</span><span style="color: #D8DEE9FF">) </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!</span><span style="color: #88C0D0">Exists</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// 幂等：已不存在即成功</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">RemoveAll</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// ListFiles 递归列出目录下所有文件的绝对路径。</span></span>
<span class="line"><span style="color: #616E88">// 如果指定了 extensions（例如 []string{&quot;.go&quot;, &quot;.txt&quot;}），则只返回匹配的文件。</span></span>
<span class="line"><span style="color: #616E88">// 传 nil 或空切片则返回所有文件。</span></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">ListFiles</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">dir</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">string</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">extensions</span><span style="color: #D8DEE9FF"> []</span><span style="color: #D8DEE9">string</span><span style="color: #D8DEE9FF">) ([]</span><span style="color: #D8DEE9">string</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">files</span><span style="color: #D8DEE9FF"> []string</span></span>
<span class="line"><span style="color: #D8DEE9FF">    err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">filepath</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">WalkDir</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">dir</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">func</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">string</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">d</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">DirEntry</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF">) </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        if err != nil </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        if d.IsDir() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// 过滤扩展名</span></span>
<span class="line"><span style="color: #D8DEE9FF">        if </span><span style="color: #88C0D0">len</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">extensions</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> &gt; 0 </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            ext </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">strings</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">ToLower</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">filepath</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Ext</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #D8DEE9FF">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">            matched </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">for</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">_</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> e </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">range</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">extensions</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">strings</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">ToLower</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">e</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">==</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ext</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #D8DEE9">matched</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #81A1C1">break</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!</span><span style="color: #D8DEE9">matched</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">absPath</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">filepath</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Abs</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            return </span><span style="color: #D8DEE9">err</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">files</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">append</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">files</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">absPath</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">files</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// DirSize 计算目录占用磁盘总大小（递归包含子目录）</span></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">DirSize</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">string</span><span style="color: #D8DEE9FF">) (</span><span style="color: #D8DEE9">int64</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">size</span><span style="color: #D8DEE9FF"> int64</span></span>
<span class="line"><span style="color: #D8DEE9FF">    err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">filepath</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">WalkDir</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">func</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">_</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">string</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">d</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">DirEntry</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF">) </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        if err != nil </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        if !d.IsDir() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">info</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">d</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Info</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">size</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">info</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Size</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        return </span><span style="color: #D8DEE9">nil</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">size</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// ReadString 快速读取整个文件为字符串</span></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">ReadString</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">string</span><span style="color: #D8DEE9FF">) (</span><span style="color: #D8DEE9">string</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">data</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">ReadFile</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">string</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// WriteString 快速将字符串写入文件（覆盖写入，权限 0644）</span></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">WriteString</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">content</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">string</span><span style="color: #D8DEE9FF">) </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">WriteFile</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> []</span><span style="color: #88C0D0">byte</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">content</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0644</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// AppendString 追加字符串到文件末尾，如果文件不存在则创建</span></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">AppendString</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">content</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">string</span><span style="color: #D8DEE9FF">) </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">f</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">OpenFile</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">O_APPEND</span><span style="color: #81A1C1">|</span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">O_CREATE</span><span style="color: #81A1C1">|</span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">O_WRONLY</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0644</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">defer</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">f</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Close</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">_</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">f</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">WriteString</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">content</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// SafeWrite 原子性写入：先写临时文件，再 rename 替换，避免写入中途崩溃导致文件损坏。</span></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">SafeWrite</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">string</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF"> []</span><span style="color: #D8DEE9">byte</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">perm</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">FileMode</span><span style="color: #D8DEE9FF">) </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 在同目录下创建临时文件</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">tmpFile</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">CreateTemp</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">filepath</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Dir</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">path</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">.tmp-*</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    tmpName </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tmpFile</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Name</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 写入数据</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">_</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tmpFile</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Write</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">tmpFile</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Close</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Remove</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">tmpName</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tmpFile</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Sync</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">tmpFile</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Close</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Remove</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">tmpName</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tmpFile</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Close</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Remove</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">tmpName</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 设置权限</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Chmod</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">tmpName</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">perm</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Remove</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">tmpName</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 原子替换</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">os</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Rename</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">tmpName</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">path</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p class="wp-block-paragraph">使用示例</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>package main

import (
    "fmt"
    "log"
    "path/to/fileutil" // 替换为你的实际导入路径
)

func main() {
    // 1. 判断路径是否存在
    fmt.Println("存在:", fileutil.Exists("/tmp/test"))

    // 2. 确保目录存在
    if err := fileutil.EnsureDir("./data/logs"); err != nil {
        log.Fatal(err)
    }

    // 3. 写入文件
    if err := fileutil.WriteString("./data/config.txt", "Hello, Gopher!"); err != nil {
        log.Fatal(err)
    }

    // 4. 安全写入（原子操作）
    if err := fileutil.SafeWrite("./data/important.dat", []byte("atomic write"), 0644); err != nil {
        log.Fatal(err)
    }

    // 5. 复制文件
    if err := fileutil.CopyFile("./data/config.txt", "./data/backup/config_copy.txt"); err != nil {
        log.Fatal(err)
    }

    // 6. 复制目录
    if err := fileutil.CopyDir("./data", "./data_backup"); err != nil {
        log.Fatal(err)
    }

    // 7. 列出所有 .go 文件
    goFiles, err := fileutil.ListFiles(".", []string{".go"})
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("找到 %d 个 Go 文件\n", len(goFiles))

    // 8. 计算目录大小
    size, err := fileutil.DirSize("./data")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("目录大小: %.2f MB\n", float64(size)/1024/1024)

    // 9. 移动文件
    if err := fileutil.MoveFile("./data/old.txt", "./data/archive/new.txt"); err != nil {
        log.Fatal(err)
    }

    // 10. 删除目录
    if err := fileutil.DeleteDir("./data_backup"); err != nil {
        log.Fatal(err)
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">package</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">main</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">import</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">fmt</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">log</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">path/to/fileutil</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// 替换为你的实际导入路径</span></span>
<span class="line"><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">main</span><span style="color: #D8DEE9FF">() </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 1. 判断路径是否存在</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Println</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">存在:</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fileutil</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Exists</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">/tmp/test</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">))</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 2. 确保目录存在</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fileutil</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">EnsureDir</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">./data/logs</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fatal</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 3. 写入文件</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fileutil</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">WriteString</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">./data/config.txt</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Hello, Gopher!</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fatal</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 4. 安全写入（原子操作）</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fileutil</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">SafeWrite</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">./data/important.dat</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> []</span><span style="color: #88C0D0">byte</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">atomic write</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0644</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fatal</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 5. 复制文件</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fileutil</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">CopyFile</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">./data/config.txt</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">./data/backup/config_copy.txt</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fatal</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 6. 复制目录</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fileutil</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">CopyDir</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">./data</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">./data_backup</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fatal</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 7. 列出所有 .go 文件</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">goFiles</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fileutil</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">ListFiles</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">.</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> []</span><span style="color: #D8DEE9">string</span><span style="color: #ECEFF4">{</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">.go</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fatal</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Printf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">找到 %d 个 Go 文件</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">len</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">goFiles</span><span style="color: #D8DEE9FF">))</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 8. 计算目录大小</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">size</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fileutil</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">DirSize</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">./data</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fatal</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">fmt</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Printf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">目录大小: %.2f MB</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">float64</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">size</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">/</span><span style="color: #B48EAD">1024</span><span style="color: #81A1C1">/</span><span style="color: #B48EAD">1024</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 9. 移动文件</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fileutil</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">MoveFile</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">./data/old.txt</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">./data/archive/new.txt</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fatal</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 10. 删除目录</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> err </span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fileutil</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">DeleteDir</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">./data_backup</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Fatal</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">err</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<h2 class="wp-block-heading">要点说明</h2>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th class="has-text-align-left" data-align="left">函数</th><th class="has-text-align-left" data-align="left">注意事项</th></tr></thead><tbody><tr><td><code>CopyFile</code></td><td>覆盖已存在的目标文件；保留源文件权限</td></tr><tr><td><code>MoveFile</code></td><td>跨文件系统自动降级为复制+删除</td></tr><tr><td><code>CopyDir</code></td><td>递归复制，目标目录存在时会合并内容</td></tr><tr><td><code>SafeWrite</code></td><td>原子写入，先写临时文件再 rename，防止写一半时进程崩溃导致数据损坏</td></tr><tr><td><code>ListFiles</code></td><td>支持按扩展名过滤，返回绝对路径</td></tr><tr><td><code>EnsureDir</code></td><td>已存在但不是目录时报错，避免误用</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">这个工具包覆盖了日常开发中 90% 的文件目录操作场景。你可以根据项目需要继续扩展，比如增加压缩/解压、权限批量修改等功能。直接拷贝到项目中使用即可。</p>
<p>The post <a href="https://www.atomic-cube.cn/go-%e6%96%87%e4%bb%b6%e5%92%8c%e6%96%87%e4%bb%b6%e5%a4%b9%e5%b7%a5%e5%85%b7%e7%b1%bb%ef%bc%9a%e4%b8%80%e4%b8%aa%e5%bc%80%e7%ae%b1%e5%8d%b3%e7%94%a8%e7%9a%84-fileutil-%e5%8c%85/">Go 文件和文件夹工具类：一个开箱即用的 fileutil 包</a> appeared first on <a href="https://www.atomic-cube.cn">原立方</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.atomic-cube.cn/go-%e6%96%87%e4%bb%b6%e5%92%8c%e6%96%87%e4%bb%b6%e5%a4%b9%e5%b7%a5%e5%85%b7%e7%b1%bb%ef%bc%9a%e4%b8%80%e4%b8%aa%e5%bc%80%e7%ae%b1%e5%8d%b3%e7%94%a8%e7%9a%84-fileutil-%e5%8c%85/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
