<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title><![CDATA[刘新修]]></title> 
<link>http://liuxinxiu.com:80/index.php</link> 
<description><![CDATA[刘新修的个人博客 (Liuxinxiu'S Blog)]]></description> 
<language>zh-cn</language> 
<copyright><![CDATA[刘新修]]></copyright>
<item>
<link>http://liuxinxiu.com:80/s/349/</link>
<title><![CDATA[jpgjpeg有什么区别]]></title> 
<author>刘新修 &lt;admin@yourname.com&gt;</author>
<category><![CDATA[技术分类]]></category>
<pubDate>Thu, 03 Jul 2025 01:04:48 +0000</pubDate> 
<guid>http://liuxinxiu.com:80/s/349/</guid> 
<description>
<![CDATA[ 
	<p>&nbsp;</p><p><img src="/attachment/2025/07/image/2025-07-03_085647.png" width="1061" height="921" alt="" /></p>
]]>
</description>
</item><item>
<link>http://liuxinxiu.com:80/s/348/</link>
<title><![CDATA[字符串遍历拼接、字符串局部清除、字符串局部插入，等逻辑]]></title> 
<author>刘新修 &lt;admin@yourname.com&gt;</author>
<category><![CDATA[H5/JS/CSS]]></category>
<pubDate>Tue, 24 Dec 2024 11:57:56 +0000</pubDate> 
<guid>http://liuxinxiu.com:80/s/348/</guid> 
<description>
<![CDATA[ 
	<div class="codeText"><div class="codeHead">JavaScript代码</div><ol start="1" class="dp-c"><li class="alt"><span><span class="comment">/*** 清空指定字符串之间的内容(包括起始字符) ***/</span><span>&nbsp;&nbsp;</span></span></li><li><span><span class="keyword">function</span><span> deleteBetweenCharacters(str, startChar, endChar) &#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">/*** 容错处理 ***/</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span> (</span><span class="keyword">typeof</span><span>(str) == </span><span class="string">'undefined'</span><span> &#124;&#124;&nbsp;&nbsp; </span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">typeof</span><span>(startChar) == </span><span class="string">'undefined'</span><span> &#124;&#124;&nbsp;&nbsp; </span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">typeof</span><span>(endChar) == </span><span class="string">'undefined'</span><span>) &#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span> </span><span class="keyword">false</span><span>;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">/*** 起始替换逻辑 ***/</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;let startIndex = str.indexOf(startChar);&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;let endIndex = str.indexOf(endChar);&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">while</span><span> (startIndex !== -1 &amp;&amp; endIndex !== -1) &#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;str = str.substring(0, startIndex) + str.substring(endIndex + 1);&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;startIndex = str.indexOf(startChar);&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;endIndex = str.indexOf(endChar);&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span> str;&nbsp;&nbsp;</span></span></li><li><span>&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li><span><span class="comment">/*** 向字符串指定位置插入字符(业务标记符) ***/</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span><span class="keyword">function</span><span> insertString(str, insertStr, index) &#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">/*** 容错处理 ***/</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span> (</span><span class="keyword">typeof</span><span>(str) == </span><span class="string">'undefined'</span><span> &#124;&#124;&nbsp;&nbsp; </span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">typeof</span><span>(insertStr) == </span><span class="string">'undefined'</span><span> &#124;&#124;&nbsp;&nbsp; </span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">typeof</span><span>(index) == </span><span class="string">'undefined'</span><span>) &#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span> </span><span class="keyword">false</span><span>;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span> str.slice(0, index) + insertStr + str.slice(index);&nbsp;&nbsp;</span></span></li><li class="alt"><span>&#125;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span><span class="comment">/*** 业务处理主函数 ***/</span><span>&nbsp;&nbsp;</span></span></li><li><span><span class="keyword">function</span><span> main (str, startChar, endChar, itemNames) &#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">/*** 容错处理 ***/</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span> (</span><span class="keyword">typeof</span><span>(str) == </span><span class="string">'undefined'</span><span> &#124;&#124;&nbsp;&nbsp; </span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">typeof</span><span>(startChar) == </span><span class="string">'undefined'</span><span> &#124;&#124;&nbsp;&nbsp; </span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">typeof</span><span>(endChar) == </span><span class="string">'undefined'</span><span> &#124;&#124;&nbsp;&nbsp; </span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">typeof</span><span>(itemNames) == </span><span class="string">'undefined'</span><span>) &#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span> </span><span class="keyword">false</span><span>;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">var</span><span> newStrData = str;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">var</span><span> startIndex = str.indexOf(startChar);&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">/*** 如果是首次出现 ***/</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span> (str.indexOf(</span><span class="string">'--'</span><span>) == -1) &#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;str = insertString(newStrData, <span class="string">'--'</span><span>, startIndex);&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">/*** 调用清除方法 ***/</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;let data = deleteBetweenCharacters(str, startChar, endChar);&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;let replacedStr = data? data.replace(/--/g, itemNames) : <span class="string">''</span><span>;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span> replacedStr;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&#125;&nbsp;&nbsp;</span></li><li><span><span class="comment">// 示例用法</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span><span class="comment">// const inputString = &quot;This is 【some】 example 【string】 with 【special】 characters.&quot;;</span><span>&nbsp;&nbsp;</span></span></li><li><span><span class="comment">// const str = main(inputString, &quot;【&quot;, &quot;】&quot;)</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span><span class="comment">// console.log(str);</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span><span class="comment">/*** 业务源数据 ***/</span><span>&nbsp;&nbsp;</span></span></li><li><span>let dataList = [&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&#123;idStorage: 1, name: <span class="string">'red'</span><span>, riskItem: </span><span class="string">''</span><span>, suggest: </span><span class="string">'建议核实【】实际配件价格，剔除多定金额。'</span><span>&#125;,&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&#123;idStorage: 1, name: <span class="string">'red1'</span><span>, riskItem: </span><span class="string">''</span><span>, suggest: </span><span class="string">'建议核实【高压电池包】【高压电池包】实际配件价格，剔除多定金额。'</span><span>&#125;,&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&#123;idStorage: 2, name: <span class="string">'sese'</span><span>, riskItem: </span><span class="string">''</span><span>, suggest: </span><span class="string">'建议核实【】实际配件价格，剔除多定金额。'</span><span>&#125;,&nbsp;&nbsp;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&#123;idStorage: 1, name: <span class="string">'red'</span><span>, riskItem: </span><span class="string">''</span><span>, suggest: </span><span class="string">'建议核实【】实际配件价格，剔除多定金额。'</span><span>&#125;,&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&#123;idStorage: 3, name: <span class="string">'bbbd'</span><span>, riskItem: </span><span class="string">''</span><span>, suggest: </span><span class="string">'建议核实【】实际配件价格，剔除多定金额。'</span><span>&#125;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;];&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li><span><span class="comment">/*** 拼接字符串小模板(备用) ***/</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>let itemNames = <span class="string">'【'</span><span> + dataList.filter(item =&gt; item.idStorage === 1).map(item =&gt; item.name).join(</span><span class="string">'】【'</span><span>) + </span><span class="string">'】'</span><span>;&nbsp;&nbsp;</span></span></li><li><span>console.log(<span class="string">'itemNames'</span><span>, itemNames)&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li><span><span class="comment">/*** 拷贝源数据备用 ***/</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span><span class="keyword">var</span><span> arr = JSON.parse(JSON.stringify(dataList))&nbsp;&nbsp;</span></span></li><li><span><span class="keyword">for</span><span> (</span><span class="keyword">var</span><span> i = 0; i &lt; arr.length; i++) &#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span> (arr[i].idStorage == 1) &#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;arr[i].suggest = main(arr[i].suggest, <span class="string">&quot;【&quot;</span><span>, </span><span class="string">&quot;】&quot;</span><span>, itemNames)&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li><span>&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>console.log(<span class="string">'arr'</span><span>, arr)&nbsp;&nbsp;</span></span></li></ol></div><div class="codeText"><div class="codeHead">JavaScript代码</div><ol start="1" class="dp-c"><li class="alt"><span><span class="keyword">const</span><span>&nbsp;inputString&nbsp;=&nbsp;</span><span class="string">&quot;This&nbsp;is&nbsp;【some】&nbsp;example&nbsp;【string】&nbsp;with&nbsp;【special】&nbsp;characters.&quot;</span><span>;&nbsp;&nbsp;</span></span></li><li><span><span class="keyword">const</span><span>&nbsp;str&nbsp;=&nbsp;deleteBetweenCharacters(inputString,&nbsp;</span><span class="string">&quot;【&quot;</span><span>,&nbsp;</span><span class="string">&quot;】&quot;</span><span>)&nbsp;&nbsp;</span></span></li><li class="alt"><span>console.log(str);&nbsp;&nbsp;</span></li><li><span>//This&nbsp;is&nbsp;&nbsp;example&nbsp;&nbsp;<span class="keyword">with</span><span>&nbsp;&nbsp;characters.&nbsp;&nbsp;</span></span></li></ol></div><p>&nbsp;</p>
]]>
</description>
</item><item>
<link>http://liuxinxiu.com:80/s/347/</link>
<title><![CDATA[JS时间格式排序]]></title> 
<author>刘新修 &lt;admin@yourname.com&gt;</author>
<category><![CDATA[H5/JS/CSS]]></category>
<pubDate>Tue, 17 Dec 2024 14:15:27 +0000</pubDate> 
<guid>http://liuxinxiu.com:80/s/347/</guid> 
<description>
<![CDATA[ 
	<div class="codeText"><div class="codeHead">JavaScript代码</div><ol start="1" class="dp-c"><li class="alt"><span><span class="comment">/**</span>&nbsp;</span></li><li><span><span class="comment">&nbsp;*&nbsp;@description&nbsp;2.根据日期时间混合排序</span>&nbsp;</span></li><li class="alt"><span><span class="comment">&nbsp;*&nbsp;@param&nbsp;&#123;Object[]&#125;&nbsp;dataList&nbsp;-&nbsp;要排序的数组</span>&nbsp;</span></li><li><span><span class="comment">&nbsp;*&nbsp;@param&nbsp;&#123;string&#125;&nbsp;property&nbsp;-&nbsp;传入需要排序的字段</span>&nbsp;</span></li><li class="alt"><span><span class="comment">&nbsp;*&nbsp;@param&nbsp;&#123;boolean&#125;&nbsp;bol&nbsp;-&nbsp;true:&nbsp;升序；false:&nbsp;降序；默认为true&nbsp;升序</span>&nbsp;</span></li><li><span><span class="comment">&nbsp;*&nbsp;@return&nbsp;&#123;Object[]&#125;&nbsp;dataList&nbsp;-&nbsp;返回改变完顺序的数组</span>&nbsp;</span></li><li class="alt"><span><span class="comment">&nbsp;*/</span><span>&nbsp;&nbsp;</span></span></li><li><span><span class="keyword">function</span><span>&nbsp;dateSort(dataList,&nbsp;property,&nbsp;bol&nbsp;=&nbsp;</span><span class="keyword">true</span><span>)&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;dataList.sort(<span class="keyword">function</span><span>&nbsp;(a,&nbsp;b)&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;(bol)&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;return&nbsp;a[property].localeCompare(b[property]);&nbsp;//&nbsp;升序</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;Date.parse(a[property])&nbsp;-&nbsp;Date.parse(b[property]);&nbsp;&nbsp;</span><span class="comment">//&nbsp;升序</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;<span class="keyword">else</span><span>&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;return&nbsp;b[property].localeCompare(a[property]);&nbsp;//&nbsp;降序</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;Date.parse(b[property])&nbsp;-&nbsp;Date.parse(a[property]);&nbsp;&nbsp;</span><span class="comment">//&nbsp;降序</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&#125;)&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;dataList;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&#125;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span>let&nbsp;arrList&nbsp;=&nbsp;[&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;&nbsp;id:&nbsp;1,&nbsp;name:&nbsp;<span class="string">'test1'</span><span>,&nbsp;score:&nbsp;99,&nbsp;dateTime:&nbsp;</span><span class="string">'2024-03-25&nbsp;13:51:03'</span><span>&nbsp;&#125;,&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;&nbsp;id:&nbsp;2,&nbsp;name:&nbsp;<span class="string">'test2'</span><span>,&nbsp;score:&nbsp;89,&nbsp;dateTime:&nbsp;</span><span class="string">'2024-03-24&nbsp;23:01:52'</span><span>&nbsp;&#125;,&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;&nbsp;id:&nbsp;3,&nbsp;name:&nbsp;<span class="string">'test3'</span><span>,&nbsp;score:&nbsp;102,&nbsp;dateTime:&nbsp;</span><span class="string">'2024-03-15&nbsp;01:51:12'</span><span>&nbsp;&#125;,&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;&nbsp;id:&nbsp;4,&nbsp;name:&nbsp;<span class="string">'test4'</span><span>,&nbsp;score:&nbsp;100,&nbsp;dateTime:&nbsp;</span><span class="string">'2024-03-23&nbsp;10:30:39'</span><span>&nbsp;&#125;,&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;&nbsp;id:&nbsp;5,&nbsp;name:&nbsp;<span class="string">'test5'</span><span>,&nbsp;score:&nbsp;111,&nbsp;dateTime:&nbsp;</span><span class="string">'2024-03-23&nbsp;11:21:42'</span><span>&nbsp;&#125;,&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;]&nbsp;&nbsp;</span></li><li><span><span class="comment">//&nbsp;console.log('升序：',&nbsp;dateSort(arrList,&nbsp;'dateTime'));&nbsp;//&nbsp;升序</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>console.log(<span class="string">'降序：'</span><span>,&nbsp;dateSort(arrList,&nbsp;</span><span class="string">'dateTime'</span><span>,&nbsp;</span><span class="keyword">false</span><span>));&nbsp;</span><span class="comment">//&nbsp;降序</span><span>&nbsp;&nbsp;</span></span></li></ol></div><p>&nbsp;<img src="/attachment/2024/12/image/2024-12-17_221432.png" width="1089" height="236" alt="" /></p>
]]>
</description>
</item><item>
<link>http://liuxinxiu.com:80/s/346/</link>
<title><![CDATA[Dioxus由DioxusLabs开发的全栈应用框架 旨在简化跨平台应用的开发]]></title> 
<author>刘新修 &lt;admin@yourname.com&gt;</author>
<category><![CDATA[PHP/Java/Go]]></category>
<pubDate>Tue, 10 Dec 2024 09:14:59 +0000</pubDate> 
<guid>http://liuxinxiu.com:80/s/346/</guid> 
<description>
<![CDATA[ 
	<p>Dioxus是一个现代的、轻量级的、用于构建跨平台UI的库，灵感来源于React。它以其高性能、简洁的API和丰富的生态系统，为开发者提供了一种高效开发原生应用的新方式。</p><div>&nbsp;</div><div><strong>项目简介</strong></div><div>Dioxus的核心目标是为移动应用、Web应用、桌面应用以及服务器端渲染提供一致性的开发体验。使用Rust编程语言编写，它提供了与React类似的语法和概念，但利用了Rust的强大功能，如类型安全和编译时检查，从而在性能上取得显著优势。</div><div>&nbsp;</div><div><strong>技术分析</strong></div><div>Dioxus的架构基于组件模型，允许开发者以声明式的方式创建可复用的UI元素。与React类似，它使用虚拟DOM来减少对实际DOM的操作，不过Dioxus进一步优化，通过Rust的静态分析能力避免了不必要的更新，实现了更快的渲染速度。</div><div>&nbsp;</div><div>此外，Dioxus还支持SSR（Server-Side Rendering）和SSG（Static Site Generation），并可以无缝地与WebAssembly集成，这使得它能够被广泛应用于Web开发和后端渲染场景。</div><div>&nbsp;</div><div><strong>应用场景</strong></div><div>移动应用开发：Dioxus提供了一套完整的工具链，让你可以用Rust直接开发iOS和Android应用。</div><div>Web应用：你可以创建高效的单页应用，并受益于Rust的安全性和性能。</div><div>桌面应用：借助Electron或其它桌面应用框架，Dioxus可以轻松构建桌面应用。</div><div>服务器渲染：对于SEO友好的网站或者需要快速首屏加载的应用，Dioxus的SSR功能非常实用。</div><div>&nbsp;</div><div><strong>特点</strong></div><div>高性能：Rust的零成本抽象和编译时优化，使Dioxus的性能远超传统的JavaScript解决方案。</div><div>类型安全：利用Rust的类型系统，Dioxus确保代码在运行前无类型错误，提高了软件的稳定性。</div><div>简洁API：Dioxus的API设计借鉴了React，对熟悉React的开发者来说，学习曲线较平缓。</div><div>跨平台兼容：一套代码，多平台运行，大大提高了开发效率。</div><div>强大的社区支持：Dioxus拥有活跃的开发者社区，不断推出新的库和工具，丰富生态体系。</div><div>&nbsp;</div><div><div>快速使用指南</div><div>安装 CLI 工具：首先，安装 Dioxus 提供的 CLI 工具。可以通过以下命令进行安装：</div><div>cargo install dioxus-cli</div><div>&nbsp;</div><div>创建新项目：使用 CLI 工具创建一个新的 Dioxus 项目：</div><div>dioxus new my_project</div><div>&nbsp;</div><div>运行开发服务器：进入项目目录并启动开发服务器：</div><div>cd my_project dioxus serve</div><div>&nbsp;</div><div>编写代码：在 src 目录下编写你的应用代码。Dioxus 使用类似 JSX 的语法，使得编写 UI 代码变得简单直观。</div><div>&nbsp;</div><div>打包和部署：当应用开发完成后，可以使用以下命令进行打包和部署：</div><div>dioxus bundle --release</div><div>&nbsp;</div><div>通过以上步骤，你可以快速上手 Dioxus 并开始构建跨平台应用。Dioxus 的高性能、易用性和强大的功能使其成为现代应用开发的理想选择。</div><div>&nbsp;</div></div><div>&nbsp;</div><div><strong>结论</strong></div><div>如果你正在寻找一种能够提升应用性能，同时保持开发效率的技术栈，Dioxus值得你尝试。其结合了React的易用性和Rust的高性能，为开发者带来前所未有的开发体验。无论是新项目还是现有项目的重构，Dioxus都能作为一个强大且灵活的选择。</div>
]]>
</description>
</item><item>
<link>http://liuxinxiu.com:80/s/345/</link>
<title><![CDATA[chrome 公网地址页面，无法访问内网地址图片及接口解决方法]]></title> 
<author>刘新修 &lt;admin@yourname.com&gt;</author>
<category><![CDATA[浏览器相关]]></category>
<pubDate>Thu, 14 Nov 2024 10:37:03 +0000</pubDate> 
<guid>http://liuxinxiu.com:80/s/345/</guid> 
<description>
<![CDATA[ 
	<div class="codeText"><div class="codeHead">C#代码</div><ol start="1" class="dp-c"><li class="alt"><span><span>地址栏输入：chrome:</span><span class="comment">//flags/，回车。搜索找到Block insecure private network requests，设置禁用（Disabled），然后重启浏览器即可</span><span>&nbsp;&nbsp;</span></span></li></ol></div>
]]>
</description>
</item><item>
<link>http://liuxinxiu.com:80/s/344/</link>
<title><![CDATA[fetch获取wasm模块实例]]></title> 
<author>刘新修 &lt;admin@yourname.com&gt;</author>
<category><![CDATA[H5/JS/CSS]]></category>
<pubDate>Thu, 14 Nov 2024 03:18:52 +0000</pubDate> 
<guid>http://liuxinxiu.com:80/s/344/</guid> 
<description>
<![CDATA[ 
	<p>fetch获取wasm模块实例</p><div class="codeText"><div class="codeHead">JavaScript代码</div><ol start="1" class="dp-c"><li class="alt"><span><span class="comment">/*** 完整的实例 ***/</span><span>&nbsp;&nbsp;</span></span></li><li><span>fetch(<span class="string">&quot;/pkg/wasm-lib/wasm_lib_bg.wasm&quot;</span><span>)&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;.then((response) =&gt; response.arrayBuffer())&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;.then(bytes =&gt; &#123;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">var</span><span> module = </span><span class="keyword">new</span><span> WebAssembly.Module(bytes);&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">var</span><span> imports = &#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">&quot;__wbindgen_init_externref_table&quot;</span><span>: ()=&gt; &#123;&#125;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">var</span><span> instance = </span><span class="keyword">new</span><span> WebAssembly&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.Instance(module, &#123; wbg: imports&#125;);&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.log(instance.exports.add(1,2));&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;)&nbsp;&nbsp;</span></li></ol></div><p>axios获取wasm模块实例</p><div class="codeText"><div class="codeHead">JavaScript代码</div><ol start="1" class="dp-c"><li class="alt"><span><span class="comment">/***&nbsp;请求.wasm文件流&nbsp;***/</span><span>&nbsp;&nbsp;</span></span></li><li><span><span class="keyword">this</span><span>.$store.dispatch(</span><span class="string">&quot;test/getWasmStream&quot;</span><span>,&#123;&#125;).then(async&nbsp;(res)&nbsp;=&gt;&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;WebAssembly.compile(res.data).then(module&nbsp;=&gt;&nbsp;&#123;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;使用编译好的模块</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;打印静态属性</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">var</span><span>&nbsp;imports&nbsp;=&nbsp;WebAssembly.Module.imports(module);&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">var</span><span>&nbsp;exports&nbsp;=&nbsp;WebAssembly.Module.exports(module);&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">var</span><span>&nbsp;strJSON&nbsp;=&nbsp;JSON.stringify(&#123;</span><span class="string">&quot;imports&quot;</span><span>:imports,&nbsp;</span><span class="string">&quot;exports&quot;</span><span>:exports&#125;,&nbsp;</span><span class="keyword">null</span><span>,&nbsp;2)&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.log(<span class="string">'strJSON'</span><span>,&nbsp;strJSON)&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">var</span><span>&nbsp;imports&nbsp;=&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">&quot;__wbindgen_init_externref_table&quot;</span><span>:&nbsp;()=&gt;&nbsp;&#123;&#125;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">/***&nbsp;使用WASM模块(运行实例)&nbsp;***/</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">var</span><span>&nbsp;instance&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;WebAssembly.Instance(module,&nbsp;&#123;wbg:&nbsp;imports&#125;)&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//console.log(instance.exports.add(3,6))</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">this</span><span>.exports&nbsp;=&nbsp;instance.exports;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;).<span class="keyword">catch</span><span>(error&nbsp;=&gt;&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.error(error);&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;);&nbsp;&nbsp;</span></li><li class="alt"><span>&#125;).<span class="keyword">catch</span><span>((error)=&gt;&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;console.log(<span class="string">'Action&nbsp;failed'</span><span>,&nbsp;error);&nbsp;&nbsp;</span></span></li><li class="alt"><span>&#125;);&nbsp;&nbsp;</span></li></ol></div><p>loadWebAssemblyModule.js 封装</p><div class="codeText"><div class="codeHead">JavaScript代码</div><ol start="1" class="dp-c"><li class="alt"><span><span class="comment">/*</span>&nbsp;</span></li><li><span><span class="comment">&nbsp;*&nbsp;@Description:&nbsp;loadWebAssemblyModule.js</span>&nbsp;</span></li><li class="alt"><span><span class="comment">&nbsp;*&nbsp;@Version:&nbsp;1.0</span>&nbsp;</span></li><li><span><span class="comment">&nbsp;*&nbsp;@Author:&nbsp;Jesse&nbsp;Liu</span>&nbsp;</span></li><li class="alt"><span><span class="comment">&nbsp;*&nbsp;@Date:&nbsp;2022-11-14&nbsp;10:32:06</span>&nbsp;</span></li><li><span><span class="comment">&nbsp;*&nbsp;@LastEditors:&nbsp;Jesse&nbsp;Liu</span>&nbsp;</span></li><li class="alt"><span><span class="comment">&nbsp;*&nbsp;@LastEditTime:&nbsp;2024-11-14&nbsp;10:55:25</span>&nbsp;</span></li><li><span><span class="comment">&nbsp;*/</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li><span><span class="comment">/***&nbsp;AJAX加载WASM模块的函数&nbsp;***/</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span><span class="keyword">const</span><span>&nbsp;getWebAssemblyModule&nbsp;=&nbsp;(url)&nbsp;=&gt;&nbsp;&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">/***&nbsp;返回一个Promise&nbsp;***/</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;</span><span class="keyword">new</span><span>&nbsp;Promise(</span><span class="keyword">function</span><span>(resolve,&nbsp;reject)&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">var</span><span>&nbsp;xhr&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;XMLHttpRequest();&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xhr.open(<span class="string">'GET'</span><span>,&nbsp;url,&nbsp;</span><span class="keyword">true</span><span>);&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xhr.responseType&nbsp;=&nbsp;<span class="string">'arraybuffer'</span><span>;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xhr.onload&nbsp;=&nbsp;<span class="keyword">function</span><span>()&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;(xhr.status&nbsp;===&nbsp;200)&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;加载成功，编译WASM模块</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">var</span><span>&nbsp;arrayBuffer&nbsp;=&nbsp;xhr.response;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WebAssembly.compile(arrayBuffer).then(<span class="keyword">function</span><span>(module)&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;resolve(module);&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;).<span class="keyword">catch</span><span>(</span><span class="keyword">function</span><span>(error)&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reject(error);&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;);&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;<span class="keyword">else</span><span>&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;出错了</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reject(<span class="keyword">new</span><span>&nbsp;Error(</span><span class="string">'Error&nbsp;loading&nbsp;WASM&nbsp;module:&nbsp;'</span><span>&nbsp;+&nbsp;xhr.statusText));&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xhr.send();&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;);&nbsp;&nbsp;</span></li><li class="alt"><span>&#125;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span><span class="comment">/***&nbsp;fetch加载WASM模块的函数&nbsp;***/</span><span>&nbsp;&nbsp;</span></span></li><li><span><span class="keyword">const</span><span>&nbsp;fetchWebAssemblyModule&nbsp;=&nbsp;(url)&nbsp;=&gt;&nbsp;&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">/***&nbsp;返回一个Promise&nbsp;***/</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;</span><span class="keyword">new</span><span>&nbsp;Promise(</span><span class="keyword">function</span><span>(resolve,&nbsp;reject)&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fetch(url)&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.then((response)&nbsp;=&gt;&nbsp;response.arrayBuffer())&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.then(bytes&nbsp;=&gt;&nbsp;&#123;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">var</span><span>&nbsp;module&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;WebAssembly.Module(bytes);&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;resolve(module);&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;)&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.<span class="keyword">catch</span><span>(error&nbsp;=&gt;&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.error(<span class="string">'Fetch&nbsp;错误:'</span><span>,&nbsp;error)&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reject(error);&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;);&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;);&nbsp;&nbsp;</span></li><li><span>&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li><span><span class="keyword">export</span><span>&nbsp;&#123;&nbsp;fetchWebAssemblyModule,&nbsp;getWebAssemblyModule&nbsp;&#125;;&nbsp;&nbsp;</span></span></li></ol></div><p>&nbsp;</p><p>&nbsp;</p>
]]>
</description>
</item><item>
<link>http://liuxinxiu.com:80/Emscripten/</link>
<title><![CDATA[centos 安装 Emscripten]]></title> 
<author>刘新修 &lt;admin@yourname.com&gt;</author>
<category><![CDATA[Linux/Unix]]></category>
<pubDate>Fri, 08 Nov 2024 07:53:05 +0000</pubDate> 
<guid>http://liuxinxiu.com:80/Emscripten/</guid> 
<description>
<![CDATA[ 
	<p><strong>centos 安装 Emscripten</strong></p><div>在CentOS上安装Emscripten需要几个步骤。以下是基本的安装指南：</div><div>&nbsp;</div><div>更新系统包：</div><div>&nbsp;</div><div><strong><span style="color: rgb(128, 0, 0);">sudo yum update</span></strong></div><div>安装Emscripten需要的依赖项：</div><div>&nbsp;</div><div><span style="color: rgb(128, 0, 0);"><strong>sudo yum install git clang make python nodejs</strong></span></div><div>获取Emscripten源代码：</div><div>&nbsp;</div><div><strong><span style="color: rgb(128, 0, 0);">git clone https://github.com/emscripten-core/emsdk.git</span></strong></div><div>进入emsdk目录并安装最新的Emscripten SDK：</div><div>&nbsp;</div><div><strong><span style="color: rgb(128, 0, 0);">cd emsdk</span></strong></div><div><strong><span style="color: rgb(128, 0, 0);">./emsdk install latest</span></strong></div><div>激活安装的SDK：</div><div>&nbsp;</div><div><strong><span style="color: rgb(128, 0, 0);">./emsdk activate latest</span></strong></div><div>加载Emscripten环境变量，可以将以下命令加入到你的.bashrc或.bash_profile中，以便在每个新的终端会话中自动设置环境变量：</div><div>&nbsp;</div><div><strong><span style="color: rgb(128, 0, 0);">source ./emsdk_env.sh</span></strong></div><div>完成以上步骤后，Emscripten应该就安装并配置好了。</div><div>&nbsp;</div><div>可以通过运行<strong>emcc --version</strong>来检查是否安装成功。</div><div>&nbsp;</div><div><div>[root@localhost emsdk]# <span style="color: rgb(128, 0, 0);"><strong>emcc --version</strong></span></div><div>shared:INFO: (Emscripten: Running sanity checks)</div><div>emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.71 (4171ae200b77a6c266b0e1ebb507d61d1ade3501)</div><div>Copyright (C) 2014 the Emscripten authors (see AUTHORS.txt)</div><div>This is free and open source software under the MIT license.</div><div>There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.</div></div>
]]>
</description>
</item><item>
<link>http://liuxinxiu.com:80/s/342/</link>
<title><![CDATA[安装Rust]]></title> 
<author>刘新修 &lt;admin@yourname.com&gt;</author>
<category><![CDATA[Linux/Unix]]></category>
<pubDate>Fri, 08 Nov 2024 07:50:14 +0000</pubDate> 
<guid>http://liuxinxiu.com:80/s/342/</guid> 
<description>
<![CDATA[ 
	<p><span style="box-sizing: border-box; outline: 0px; font-weight: 700; font-synthesis-style: auto; overflow-wrap: break-word; color: rgb(77, 77, 77); font-family: -apple-system, &quot;SF UI Text&quot;, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif; font-size: 16px; font-variant-ligatures: no-common-ligatures;">Rust 是一种系统级编程语言，旨在提供高性能和内存安全，同时避免常见的编程错误。</span><br style="box-sizing: border-box; outline: 0px; font-synthesis-style: auto; overflow-wrap: break-word; color: rgb(77, 77, 77); font-family: -apple-system, &quot;SF UI Text&quot;, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif; font-size: 16px; font-variant-ligatures: no-common-ligatures;" /><span style="color: rgb(77, 77, 77); font-family: -apple-system, &quot;SF UI Text&quot;, Arial, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif; font-size: 16px; font-variant-ligatures: no-common-ligatures;">由 Mozilla Research 推出，Rust 自推出以来因其独特的设计理念和强大的功能而在开发者社区中迅速获得了广泛的关注和采用。</span></p><p>安装命令：</p><p><span style="color: rgb(128, 0, 0);"><strong>curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs &#124; sh</strong></span></p><p>分解说明：</p><p>curl：这是一个用于在命令行下传输数据的工具，支持多种协议（如 HTTP、HTTPS、FTP 等）。</p><p>--proto '=https'：指定只允许使用 HTTPS 协议进行传输，确保数据传输的安全性。</p><p>--tlsv1.2：强制 curl 使用 TLS 1.2 协议，这是一种安全的传输层协议。</p><p>-s：静默模式（silent），在执行过程中不会显示进度条或错误信息。</p><p>-Sf：</p><p>-S：当使用 -s（静默模式）时，-S 可以让 curl 在发生错误时仍然显示错误信息。</p><p>-f：如果服务器返回一个错误状态码（如 404），curl 会失败并返回一个错误，而不是输出错误页面的内容。</p><p>https://sh.rustup.rs：这是 Rust 官方提供的安装脚本的 URL。</p><p>&#124; sh：管道符号（&#124;）将前一个命令（curl）的输出传递给后一个命令（sh）。也就是说，下载的安装脚本将直接由 sh（shell）执行。</p><p>整体作用：</p><p>这个命令通过安全的 HTTPS 连接下载 Rust 的安装脚本，并立即在您的终端中执行该脚本，以便安装 Rust 编程语言及其工具链。</p><p>&nbsp;</p><p>输出解释</p><div class="codeText"><div class="codeHead">C#代码</div><ol start="1" class="dp-c"><li class="alt"><span><span>info: downloading installer&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span>Welcome to Rust!&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span>This will download and install the official compiler <span class="keyword">for</span><span> the Rust&nbsp;&nbsp;</span></span></li><li><span>programming language, and its package manager, Cargo.&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li><span>Rustup metadata and toolchains will be installed into the Rustup&nbsp;&nbsp;</span></li><li class="alt"><span>home directory, located at:&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;/home/jjmczd/.rustup&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span>This can be modified with the RUSTUP_HOME environment variable.&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span>The Cargo home directory <span class="keyword">is</span><span> located at:&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;/home/jjmczd/.cargo&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span>This can be modified with the CARGO_HOME environment variable.&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span>The cargo, rustc, rustup and other commands will be added to&nbsp;&nbsp;</span></li><li><span>Cargo's bin directory, located at:&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;/home/jjmczd/.cargo/bin&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li><span>This path will then be added to your PATH environment variable by&nbsp;&nbsp;</span></li><li class="alt"><span>modifying the profile files located at:&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;/home/jjmczd/.profile&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;/home/jjmczd/.bashrc&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li><span>You can uninstall at any time with rustup self uninstall and&nbsp;&nbsp;</span></li><li class="alt"><span>these changes will be reverted.&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span>Current installation options:&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp; <span class="keyword">default</span><span> host triple: x86_64-unknown-linux-gnu&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">default</span><span> toolchain: stable (</span><span class="keyword">default</span><span>)&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; profile: <span class="keyword">default</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;modify PATH variable: yes&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li><span>1) Proceed with standard installation (<span class="keyword">default</span><span> - just press enter)&nbsp;&nbsp;</span></span></li><li class="alt"><span>2) Customize installation&nbsp;&nbsp;</span></li><li><span>3) Cancel installation&nbsp;&nbsp;</span></li></ol></div><p>逐行解释：</p><p>info: downloading installer</p><p>解释：安装程序正在下载过程中。</p><p>Welcome to Rust!</p><p>解释：欢迎使用 Rust！</p><p>接下来的几行</p><p>解释：这些行说明了安装过程将会下载和安装 Rust 官方编译器（rustc）以及其包管理器（Cargo）。</p><p>Rustup metadata and toolchains will be installed into the Rustup home directory, located at:</p><p>解释：Rustup 的元数据和工具链将被安装到指定的 Rustup 主目录中，默认路径为 /home/jjmczd/.rustup。您可以通过设置 RUSTUP_HOME 环境变量来修改此路径。</p><p>The Cargo home directory is located at:</p><p>解释：Cargo 的主目录位于 /home/jjmczd/.cargo。同样，您可以通过设置 CARGO_HOME 环境变量来修改此路径。</p><p>The cargo, rustc, rustup and other commands will be added to Cargo's bin directory, located at:</p><p>解释：cargo、rustc、rustup 以及其他相关命令将被添加到 Cargo 的 bin 目录中，即 /home/jjmczd/.cargo/bin。</p><p>This path will then be added to your PATH environment variable by modifying the profile files located at:</p><p>解释：安装程序会将上述 bin 目录路径添加到您的 PATH 环境变量中，这通过修改您的 shell 配置文件（如 /home/jjmczd/.profile 和 /home/jjmczd/.bashrc）来实现。这样，您可以在任何终端会话中直接运行 Rust 的命令。</p><p>You can uninstall at any time with rustup self uninstall and these changes will be reverted.</p><p>解释：如果您在任何时候想要卸载 Rust，可以运行 rustup self uninstall 命令，这将撤销所有安装的更改。</p><p>Current installation options:</p><p>解释：当前的安装选项如下：</p><p>default host triple: x86_64-unknown-linux-gnu</p><p>解释：默认的主机三元组（host triple）是 x86_64-unknown-linux-gnu，表示安装的是适用于 64 位 Linux 系统的 Rust 工具链。</p><p>default toolchain: stable (default)</p><p>解释：默认的工具链是 stable 版本，这是 Rust 的稳定版本，适合大多数用户和生产环境使用。</p><p>profile: default</p><p>解释：使用的是默认的安装配置文件，包含基本的组件和设置。</p><p>modify PATH variable: yes</p><p>解释：安装程序将修改您的 PATH 环境变量，以便您可以在终端中直接使用 Rust 的命令。</p><p>安装选项菜单：</p><div class="codeText"><div class="codeHead">C#代码</div><ol start="1" class="dp-c"><li class="alt"><span><span>1) Proceed with standard installation (</span><span class="keyword">default</span><span> - just press enter)&nbsp;&nbsp;</span></span></li><li><span>2) Customize installation&nbsp;&nbsp;</span></li><li class="alt"><span>3) Cancel installation&nbsp;&nbsp;</span></li></ol></div><div><div>1) Proceed with standard installation (default - just press enter)</div><div>&nbsp;</div><div>解释：继续标准安装（默认选项）。如果您按回车键，将使用上述默认设置进行安装。</div><div>2) Customize installation</div><div>&nbsp;</div><div>解释：自定义安装。选择此选项可以让您自定义安装路径、选择不同的工具链版本或调整其他安装选项。</div><div>3) Cancel installation</div><div>&nbsp;</div><div>解释：取消安装。选择此选项将终止 Rust 的安装过程。</div><div>接下来的步骤</div><div>选择安装选项：</div><div>&nbsp;</div><div>标准安装：如果您不需要自定义安装，直接按回车键继续。这将使用默认设置进行安装。</div><div>自定义安装：如果您需要更改安装路径或选择特定的工具链版本，可以输入 2 并按照提示进行操作。</div><div>取消安装：如果您暂时不想安装 Rust，可以输入 3 取消。</div><div>完成安装：</div><div>&nbsp;</div><div><div>安装完成后，确保重新启动终端或重新加载 shell 配置文件，以便新的 PATH 设置生效。</div><div>&nbsp;</div><div>您可以通过运行以下命令来验证 Rust 是否安装成功：</div></div><div>&nbsp;</div><div class="codeText"><div class="codeHead">C#代码</div><ol start="1" class="dp-c"><li class="alt"><span><span>rustc --version&nbsp;&nbsp;</span></span></li><li><span>cargo --version&nbsp;&nbsp;</span></li></ol></div><div>&nbsp;</div></div><div><div>这两个命令应分别返回 Rust 编译器和 Cargo 的版本信息。</div><div>更新 Rust（可选）：</div><div>如果您已经安装过 Rust，可以通过以下命令更新到最新版本：</div><div>&nbsp;</div><div class="codeText"><div class="codeHead">C#代码</div><ol start="1" class="dp-c"><li class="alt"><span><span>rustup update&nbsp;&nbsp;</span></span></li></ol></div><div><div>PATH 环境变量未更新：</div><div>如果安装后运行 rustc --version 提示找不到命令，可能是因为 PATH 环境变量未正确更新。您可以手动添加 Cargo 的 bin 目录到 PATH 中，例如：</div><div><span style="color: rgb(128, 0, 0);"><strong>export PATH=&quot;$HOME/.cargo/bin:$PATH&quot;</strong></span></div><div>&nbsp;</div><div>将上述行添加到您的 ~/.bashrc 或 ~/.profile 文件中，然后重新加载配置：</div><div><span style="color: rgb(128, 0, 0);"><strong>source ~/.bashrc</strong></span></div><div>&nbsp;</div><div>&nbsp;</div><div><span style="font-family: &quot;Courier New&quot;, monospace; font-size: 12px;">wasm-pack编译命令：</span></div><div>&nbsp;</div><div class="codeText"><div class="codeHead">C#代码</div><ol start="1" class="dp-c"><li class="alt"><span><span>[root@localhost&nbsp;brotli-compress-vue-component]#&nbsp;wasm-pack&nbsp;build&nbsp;--target&nbsp;web&nbsp;--</span><span class="keyword">out</span><span>-name&nbsp;compress&nbsp;--</span><span class="keyword">out</span><span>-dir&nbsp;pkg&nbsp;&nbsp;</span></span></li></ol></div><div>&nbsp;</div></div><div>&nbsp;</div></div>
]]>
</description>
</item><item>
<link>http://liuxinxiu.com:80/s/341/</link>
<title><![CDATA[新版chrome无法请求内网接口的解决方法]]></title> 
<author>刘新修 &lt;admin@yourname.com&gt;</author>
<category><![CDATA[浏览器相关]]></category>
<pubDate>Wed, 30 Oct 2024 06:14:35 +0000</pubDate> 
<guid>http://liuxinxiu.com:80/s/341/</guid> 
<description>
<![CDATA[ 
	<p>&nbsp;<span style="color: rgb(85, 85, 85); font-family: &quot;Microsoft YaHei&quot;; font-size: 16px;">开发本地项目时，访问接口如果遇到类似</span><span style="color: rgb(85, 85, 85); font-family: &quot;Microsoft YaHei&quot;; font-size: 16px; margin: 0px; padding: 0px; box-sizing: border-box; vertical-align: baseline; word-break: break-word; overflow-wrap: break-word; font-weight: 700;">net::ERR_FAILED、Error: Network Error、Preflight</span><span style="color: rgb(85, 85, 85); font-family: &quot;Microsoft YaHei&quot;; font-size: 16px;">等情形，可排查下是否因浏览器自身所致。</span></p><p style="margin-top: 1em; margin-bottom: 1em; box-sizing: border-box; vertical-align: baseline; word-break: break-word; overflow-wrap: break-word; color: rgb(85, 85, 85); font-family: &quot;Microsoft YaHei&quot;; font-size: 16px;">用新版chrome（版本92+）跨域请求本地接口，比如http://192.168.xxx.xxx/token。</p><p style="margin-top: 1em; margin-bottom: 1em; box-sizing: border-box; vertical-align: baseline; word-break: break-word; overflow-wrap: break-word; color: rgb(85, 85, 85); font-family: &quot;Microsoft YaHei&quot;; font-size: 16px;"><img src="/attachment/2024/10/image/c_1-1024x305.png" width="1024" height="305" alt="" /></p><p style="margin-top: 1em; margin-bottom: 1em; box-sizing: border-box; vertical-align: baseline; word-break: break-word; overflow-wrap: break-word; color: rgb(85, 85, 85); font-family: &quot;Microsoft YaHei&quot;; font-size: 16px;">点击右上角红色警告按钮，提示如下：</p><p style="margin-top: 1em; margin-bottom: 1em; box-sizing: border-box; vertical-align: baseline; word-break: break-word; overflow-wrap: break-word; color: rgb(85, 85, 85); font-family: &quot;Microsoft YaHei&quot;; font-size: 16px;"><img src="/attachment/2024/10/image/c_2-1024x433.png" width="1024" height="433" alt="" /></p><p style="margin-top: 1em; margin-bottom: 1em; box-sizing: border-box; vertical-align: baseline; word-break: break-word; overflow-wrap: break-word; color: rgb(85, 85, 85); font-family: &quot;Microsoft YaHei&quot;; font-size: 16px;">大概意思：</p><p class="has-background" style="margin-top: 1em; margin-bottom: 1em; padding: 1.25em 2.375em; box-sizing: border-box; vertical-align: baseline; word-break: break-word; overflow-wrap: break-word; color: rgb(85, 85, 85); font-family: &quot;Microsoft YaHei&quot;; font-size: 16px; background-color: rgb(217, 221, 224);">一个站点从一个网络中请求一个资源，由于其用户的特权网络位置，它只能访问这个资源。这些请求将设备和服务器暴露给互联网，增加了跨站点请求伪造(CSRF)攻击和/或信息泄漏的风险。<br style="margin: 0px; padding: 0px; box-sizing: border-box; vertical-align: baseline; word-break: break-word; overflow-wrap: break-word;" />为了降低这些风险，Chrome弃用从非安全上下文发起的对非公共子资源的请求，并将在Chrome 92中开始屏蔽它们(2021年7月)。<br style="margin: 0px; padding: 0px; box-sizing: border-box; vertical-align: baseline; word-break: break-word; overflow-wrap: break-word;" />若要解决此问题，请将需要访问本地资源的网站迁移到HTTPS。如果目标资源不在本地主机上提供服务，那么它也必须通过HTTPS提供服务，以避免混合内容问题。<br style="margin: 0px; padding: 0px; box-sizing: border-box; vertical-align: baseline; word-break: break-word; overflow-wrap: break-word;" />管理员可以使用企业策略的insecureprivatenetworkrequestallowed和InsecurePrivateNetworkRequestsAllowedForUrls在所有或某些网站上暂时禁用此限制。</p><p style="margin-top: 1em; margin-bottom: 1em; box-sizing: border-box; vertical-align: baseline; word-break: break-word; overflow-wrap: break-word; color: rgb(85, 85, 85); font-family: &quot;Microsoft YaHei&quot;; font-size: 16px;">其实还是跨域方面的问题，按照要求设置即可。最简单的方法是暂时禁用此功能，毕竟本地测试的项目，一般安全性都不需要太高。</p><p style="margin-top: 1em; margin-bottom: 1em; box-sizing: border-box; vertical-align: baseline; word-break: break-word; overflow-wrap: break-word; color: rgb(85, 85, 85); font-family: &quot;Microsoft YaHei&quot;; font-size: 16px;">地址栏输入：<span style="margin: 0px; padding: 0px; box-sizing: border-box; vertical-align: baseline; word-break: break-word; overflow-wrap: break-word; font-weight: 700;">chrome://flags/</span>，回车。搜索找到<span style="margin: 0px; padding: 0px; box-sizing: border-box; vertical-align: baseline; word-break: break-word; overflow-wrap: break-word; font-weight: 700;">Block&nbsp;insecure&nbsp;private network requests</span>，设置禁用（Disabled），然后重启浏览器即可</p><p style="margin-top: 1em; margin-bottom: 1em; box-sizing: border-box; vertical-align: baseline; word-break: break-word; overflow-wrap: break-word; color: rgb(85, 85, 85); font-family: &quot;Microsoft YaHei&quot;; font-size: 16px;"><img src="/attachment/2024/10/image/c_3.png" width="729" height="152" alt="" /></p>
]]>
</description>
</item><item>
<link>http://liuxinxiu.com:80/s/340/</link>
<title><![CDATA[Go实现多线程分片下载文件]]></title> 
<author>刘新修 &lt;admin@yourname.com&gt;</author>
<category><![CDATA[PHP/Java/Go]]></category>
<pubDate>Mon, 21 Oct 2024 08:39:22 +0000</pubDate> 
<guid>http://liuxinxiu.com:80/s/340/</guid> 
<description>
<![CDATA[ 
	<p>&nbsp;我们在下载大文件时，通常会使用多线程下载的方式来加快下载速度。例如常用的多线程下载工具（Gopeed、Aria2、XDM等等），都是通过多线程下载技术充分利用了网络带宽，以提高下载速度。</p><div><div><p>那么多线程下载是怎么实现的呢？多个线程发送网络请求，是怎么做到同时下载一个文件呢？事实上，借助HTTP协议中的一些机制就可以实现了！</p><p>今天我们就通过使用Go语言为例，从了解HTTP请求相关的一些机制开始，实现一个多线程下载的示例。</p><h2 data-id="heading-0">1，多线程下载原理</h2><p>事实上，多线程下载的原理很简单，主要的步骤如下：</p><ul><li>获取待下载文件大小</li><li>每个线程下载文件的一部分</li><li>全部下载完成后，拼接为完整文件</li></ul><p>实现这些步骤，就涉及到HTTP协议的下列相关机制。</p><h3 data-id="heading-1">(1) <code>HEAD</code>请求 - 只获取请求头</h3><p>我们通常发送HTTP请求大多数是<code>GET</code>或者<code>POST</code>类型，发送请求后我们会立即获取响应体，浏览器则会根据响应体的类型来处理内容，例如返回的是<code>text/html</code>就会作为网页显示，返回<code>image/png</code>就会解码为图片等等，响应体的类型由响应头<code>Content-Type</code>标识。当我们下载文件时，事实上也是发送HTTP请求，只不过<strong>服务器返回的响应体就是文件本身</strong>了！其类型则是<code>application/octet-stream</code>，浏览器也知道这是个文件需要下载。</p><p>当然，文件作为响应体通常比起网页、图片要大得多，在多线程下载时，<strong>我们就要先获取文件的大小，而不是立即获取文件本身</strong>，这时我们就可以向服务器发起<code>HEAD</code>请求而不是<code>GET</code>请求。</p><p>服务器收到<code>HEAD</code>请求后，就<strong>只会返回对应的响应头，而不会返回响应体</strong>，这样我们就可以在下载文件之前，读取响应头中的<code>Content-Length</code>来先获取待下载文件大小。</p><h3 data-id="heading-2">(2) <code>Range</code>请求头 - 只获取部分响应体</h3><p>知道了文件大小，我们就需要让每个线程只下载一部分文件，借助HTTP的<code>Range</code>请求头，就可以实现只让服务端返回响应体内容的一部分，而不是返回完整的响应体。</p><p>这里我们先来借助书籍《图解HTTP》中对<code>Range</code>请求头的讲解，来学习一下：</p></div><p><img src="/attachment/2024/10/image/2024-10-21_163241.png" width="610" height="946" alt="" /></p><div class="codeText"><div class="codeHead">XML/HTML代码</div><ol start="1" class="dp-xml"><li class="alt"><span><span>Range:&nbsp;</span><span class="attribute">bytes</span><span>=</span><span class="attribute-value">5001</span><span>-10000&nbsp;&nbsp;</span></span></li></ol></div><div><div><p>那么服务端就只会返回响应体的第<code>5001</code>到第<code>10000</code>字节的内容部分，包含第<code>5001</code>和第<code>10000</code>字节，<code>0</code>表示响应体的第一个字节。</p><p>这样，在多个线程同时下载文件时，我们在每个线程的请求中使用<code>Range</code>请求头，就可以实现一个线程只下载文件的一部分了！</p><h3 data-id="heading-3">(3) 为什么多线程下载可以提升速度？</h3><p>事实上，在我们客户端（下载文件的）和服务端双向网络通信情况都很好的情况下，使用单线程和多线程下载的速度是几乎没有差异的，也就是说能够跑满我们客户端的全部带宽，那么这种情况下我们使用单线程下载反而更能够节省硬件和网络资源。</p><p>但是在我们客户端和服务端之间网络波动较大的情况下，例如我们国内从Github下载文件的时候，就会发现多线程下载速度比单线程快得多，反之使用单线程完全无法充分利用我们的网络带宽。</p><p>这种现象事实上是因为TCP连接的<strong>慢启动机制</strong>导致的，众所周知HTTP是基于TCP的协议，每次我们建立HTTP连接时，包括下载文件，都是在传输层基于TCP协议进行传输。<strong>TCP慢启动机制</strong>是TCP 协议中一种拥塞控制的机制，目的是在开始数据传输时逐步探测网络的容量，避免瞬间发送大量数据而导致网络拥塞。慢启动不是字面意义上的&ldquo;慢&rdquo;，而是相对于立即使用最大带宽而言，它会逐渐增加传输速率。</p><p>慢启动机制的过程简要概括如下：</p><ul><li><strong>一开始建立连接</strong>：当一个新的TCP连接建立后，发送方并不知道当前网络的拥塞情况。因此，发送方不会马上发送大量数据，而是会使用<strong>慢启动机制</strong>来逐步增加数据传输的速率，在TCP中使用阻塞窗口<code>cwnd</code>来限制发送的数据量，也就是说一开始<code>cwnd</code>是非常小的</li><li><strong>拥塞窗口增长</strong>：在建立连接后，每当接收到一个确认<code>ACK</code>包时，<code>cwnd</code>会<strong>指数级增长</strong>，直到达到网络的带宽限制或者某个拥塞控制的阈值（称为慢启动阈值<code>ssthresh</code>），这个过程会一直持续，直到发送方探测到网络出现了拥塞（比如丢包或者确认延迟变长），或者<code>cwnd</code>达到了某个预定义的慢启动阈值<code>ssthresh</code></li><li><strong>慢启动的终止</strong>：慢启动机制会在以下情况终止：<ul><li><strong>达到慢启动阈值<code>ssthresh</code></strong>：当拥塞窗口<code>cwnd</code>增长到慢启动阈值<code>ssthresh</code>时，慢启动机制停止，此时TCP会进入另一种拥塞控制机制，称为<strong>拥塞避免</strong>，这时<code>cwnd</code>增长变为线性而非指数级</li><li><strong>发生拥塞（如丢包或超时）</strong>：如果发送方检测到数据包丢失（例如没有收到确认），它会认为网络已经出现拥塞，此时<code>ssthresh</code>会被调整为当前<code>cwnd</code>的一半，然后<code>cwnd</code>会重置为<code>1 MSS</code>，重新进入慢启动阶段</li></ul></li></ul><p>可见TCP连接使用<code>cwnd</code>限制两者发送的数据量的大小，并逐步&ldquo;试探&rdquo;两者传输数据速率的上限并增加传输的数据量。</p><p>在我们下载文件时，事实上是服务端在向我们发送文件，如果网络波动较大、不稳定，TCP连接机会一直将<code>cwnd</code>限制在一个较小的值，在单位时间内，服务端也无法向我们发送更大的数据量。</p><p>此时，如果我们使用多线程下载，和服务端建立多个TCP连接，这样即使每个TCP连接的<code>cwnd</code>较小，所有TCP连接加起来传输的数据量仍然可以占满我们的带宽。</p><h2 data-id="heading-4">2，Go代码实现</h2><p>知道了HTTP的上述几个机制，相信大家就知道如何实现一个简单的多线程下载了！我们可以总结主要步骤如下：</p><ul><li>发送<code>HEAD</code>类型请求，通过<code>Content-Length</code>请求头获取待下载文件大小</li><li>根据给定的线程数量，结合待下载文件大小，确定每个线程下载的范围部分，也就是每个线程的<code>Range</code>请求头字节范围</li><li>启动所有线程，使得每个线程下载它们对应的部分文件，并等待全部线程下载完成</li><li>合并每个线程下载的部分为最终文件</li><li>清理每个线程下载的文件部分</li></ul><p>这里分别设计下列类（结构体），用于存放多线程下载时的传入参数和状态量：</p></div><div><img src="/attachment/2024/10/image/2024-10-21_163453.png" alt="" /></div><div>&nbsp;</div><div><div><p>上述<code>ShardTask</code>类表示<strong>一个线程的下载任务</strong>，其中会完成一个分片（文件的一部分）的下载请求操作，它有如下作为参数的属性：</p><ul><li><code>Url</code> 下载的文件地址</li><li><code>Order</code> 分片序号</li><li><code>ShardFilePath</code> 这个分片文件的保存路径</li><li><code>RangeStart</code>和<code>RangeEnd</code> 下载的文件起始范围和结束范围，用于设定<code>Range</code>请求头</li></ul><p>此外，还有作为下载状态的属性：</p><ul><li><code>DownloadSize</code> 下载任务进行时，这个线程已下载的文件部分大小</li><li><code>TaskDone</code> 这个线程的下载任务是否完成</li></ul><p>该类的成员方法如下：</p><ul><li><code>DoShardGet</code> 执行分片下载任务，在其中会根据<code>RangeStart</code>和<code>RangeEnd</code>设定对应的HTTP请求头，发送请求并下载对应的文件部分</li></ul><p>然后就是<code>ParallelGetTask</code>类，表示<strong>一整个多线程下载任务</strong>，其中包含了一个多线程下载任务的参数和状态量，并且实现了多线程下载的每个步骤，它有如下作为参数的属性：</p><ul><li><code>Url</code> 文件的下载链接</li><li><code>FilePath</code> 文件下载完成后的保存位置</li><li><code>Concurrent</code> 下载并发数，即同时下载的分片数量</li><li><code>TempFolder</code> 临时分片文件的保存文件夹</li></ul><p>此外还有作为状态的属性：</p><ul><li><code>TotalSize</code> 待下载文件的总大小</li><li><code>ShardTaskList</code> 存储所有分片任务对象指针的列表</li></ul><p>该类中的方法主要是分片下载的一些步骤如下：</p><ul><li><code>getLength</code> 发送<code>HEAD</code>请求获取<code>Content-Length</code>以获取文件大小，获取后将其设定到<code>TotalSize</code>属性</li><li><code>allocateTask</code> 根据给定的线程数和获取到的文件大小，计算每个线程下载的文件内容范围，并创建对应的<code>ShardTask</code>结构体放入<code>ShardTaskList</code>中</li><li><code>downloadShard</code> 为每一个<code>ShardTask</code>对象创建一个线程（Goroutine）并在新的线程中调用<code>ShardTask</code>对象的下载分片方法，以启动所有线程的下载任务，并通过<code>sync.WaitGroup</code>来等待全部线程完成</li><li><code>mergeFile</code> 下载完成后，合并每个分片为最终文件</li><li><code>cleanShard</code> 合并完成后，清理下载的每个分片文件</li><li><code>printTotalProcess</code> 这是一个附加的辅助方法，用于实时输出下载进度</li><li><code>Run</code> 启动整个多线程下载任务，该函数是暴露的公开函数，其中对上述每个步骤函数进行了组织，按顺序调用执行</li></ul><p>下面，我们来看一下它们的代码实现。</p><h3 data-id="heading-5">(1) <code>ShardTask</code> - 一个线程的下载任务</h3></div><div class="codeText"><div class="codeHead">C#代码</div><ol start="1" class="dp-c"><li class="alt"><span><span>package&nbsp;model&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span>import&nbsp;(&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">&quot;bufio&quot;</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">&quot;fmt&quot;</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">&quot;github.com/fatih/color&quot;</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">&quot;io&quot;</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">&quot;net/http&quot;</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">&quot;os&quot;</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">&quot;sync&quot;</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>)&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span><span class="comment">//&nbsp;全局HTTP客户端</span><span>&nbsp;&nbsp;</span></span></li><li><span>var&nbsp;httpClient&nbsp;=&nbsp;http.Client&#123;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;Transport:&nbsp;&amp;http.Transport&#123;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;关闭keep-alive确保一个线程就使用一个TCP连接</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DisableKeepAlives:&nbsp;<span class="keyword">true</span><span>,&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;,&nbsp;&nbsp;</span></li><li class="alt"><span>&#125;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span><span class="comment">//&nbsp;ShardTask&nbsp;单个分片下载任务的任务参数和状态量</span><span>&nbsp;&nbsp;</span></span></li><li><span>type&nbsp;ShardTask&nbsp;<span class="keyword">struct</span><span>&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;下载链接</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;Url&nbsp;<span class="keyword">string</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;分片序号，从1开始</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;Order&nbsp;<span class="keyword">int</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;这个分片文件的路径</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;ShardFilePath&nbsp;<span class="keyword">string</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;分片的起始范围（字节，包含）</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;RangeStart&nbsp;int64&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;分片的结束范围（字节，包含）</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;RangeEnd&nbsp;int64&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;已下载的部分（字节）</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;DownloadSize&nbsp;int64&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;该任务是否完成</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;TaskDone&nbsp;<span class="keyword">bool</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&#125;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span><span class="comment">//&nbsp;NewShardTask&nbsp;构造函数</span><span>&nbsp;&nbsp;</span></span></li><li><span>func&nbsp;NewShardTask(url&nbsp;<span class="keyword">string</span><span>,&nbsp;order&nbsp;</span><span class="keyword">int</span><span>,&nbsp;shardFilePath&nbsp;</span><span class="keyword">string</span><span>,&nbsp;rangeStart&nbsp;int64,&nbsp;rangeEnd&nbsp;int64)&nbsp;*ShardTask&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;&amp;ShardTask&#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;设定任务参数</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Url:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;url,&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Order:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;order,&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ShardFilePath:&nbsp;shardFilePath,&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RangeStart:&nbsp;&nbsp;&nbsp;&nbsp;rangeStart,&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RangeEnd:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rangeEnd,&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;初始化状态量</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DownloadSize:&nbsp;0,&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TaskDone:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">false</span><span>,&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li><span>&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li><span><span class="comment">//&nbsp;DoShardGet&nbsp;开始下载这个分片（该方法在goroutine中执行）</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>func&nbsp;(task&nbsp;*ShardTask)&nbsp;DoShardGet(waitGroup&nbsp;*sync.WaitGroup)&nbsp;&#123;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;创建文件</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;file,&nbsp;e&nbsp;:=&nbsp;os.OpenFile(task.ShardFilePath,&nbsp;os.O_CREATE&#124;os.O_WRONLY&#124;os.O_TRUNC,&nbsp;0755)&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;e&nbsp;!=&nbsp;nil&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color.Red(<span class="string">&quot;任务%d创建文件失败！&quot;</span><span>,&nbsp;task.Order)&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color.HiRed(<span class="string">&quot;%s&quot;</span><span>,&nbsp;e)&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;准备请求</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;request,&nbsp;e&nbsp;:=&nbsp;http.NewRequest(<span class="string">&quot;GET&quot;</span><span>,&nbsp;task.Url,&nbsp;nil)&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;e&nbsp;!=&nbsp;nil&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color.Red(<span class="string">&quot;任务%d创建请求出错！&quot;</span><span>,&nbsp;task.Order)&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color.HiRed(<span class="string">&quot;%s&quot;</span><span>,&nbsp;e)&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;设定请求头</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;request.Header.Set(<span class="string">&quot;Range&quot;</span><span>,&nbsp;fmt.Sprintf(</span><span class="string">&quot;bytes=%d-%d&quot;</span><span>,&nbsp;task.RangeStart,&nbsp;task.RangeEnd))&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;发送请求</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;response,&nbsp;e&nbsp;:=&nbsp;httpClient.Do(request)&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;e&nbsp;!=&nbsp;nil&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color.Red(<span class="string">&quot;任务%d发送下载请求出错！&quot;</span><span>,&nbsp;task.Order)&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color.HiRed(<span class="string">&quot;%s&quot;</span><span>,&nbsp;e)&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;读取请求体</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;body&nbsp;:=&nbsp;response.Body&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;读取缓冲区</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;buffer&nbsp;:=&nbsp;make([]<span class="keyword">byte</span><span>,&nbsp;8092)&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;准备写入文件</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;writer&nbsp;:=&nbsp;bufio.NewWriter(file)&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">for</span><span>&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;读取一次内容至缓冲区</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;readSize,&nbsp;readError&nbsp;:=&nbsp;body.Read(buffer)&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;readError&nbsp;!=&nbsp;nil&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;如果读取完毕则退出循环</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;readError&nbsp;==&nbsp;io.EOF&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">break</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;<span class="keyword">else</span><span>&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color.Red(<span class="string">&quot;任务%d读取响应错误！&quot;</span><span>,&nbsp;task.Order)&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color.HiRed(<span class="string">&quot;%s&quot;</span><span>,&nbsp;readError)&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;把缓冲区内容追加至文件</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_,&nbsp;writeError&nbsp;:=&nbsp;writer.Write(buffer[0:readSize])&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;writeError&nbsp;!=&nbsp;nil&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color.Red(<span class="string">&quot;任务%d写入文件时出现错误！&quot;</span><span>,&nbsp;task.Order)&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color.HiRed(<span class="string">&quot;%s&quot;</span><span>,&nbsp;writeError)&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_&nbsp;=&nbsp;writer.Flush()&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;记录下载进度</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;task.DownloadSize&nbsp;+=&nbsp;int64(readSize)&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;关闭全部资源</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;_&nbsp;=&nbsp;body.Close()&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;_&nbsp;=&nbsp;file.Close()&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;标记任务完成</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;task.TaskDone&nbsp;=&nbsp;<span class="keyword">true</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;使线程组中计数器-1</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;waitGroup.Done()&nbsp;&nbsp;</span></li><li><span>&#125;&nbsp;&nbsp;</span></li></ol></div><div><div><p>构造函数<code>NewShardTask</code>负责完成<code>ShardTask</code>的参数传入和状态量初始化，而<code>DoShardGet</code>方法实现了下载一个文件分片的完整步骤，从创建文件准备写入，到设定请求头，发出请求，最后读取响应体保存到文件。</p><p>此外，可见这里的<code>http.Client</code>对象中，我们将其<code>DisableKeepAlives</code>设为了<code>true</code>即关闭<code>keep-alive</code>，这是因为<strong>默认情况下Go语言的HTTP客户端会复用TCP连接，即使你多个线程发起请求，也会使用一个TCP连接进行</strong>。</p><p>而多线程下载需要<strong>每个线程持有一个单独的TCP连接</strong>来达到突破<code>cwnd</code>的限制，因此这里关闭<code>keep-alive</code>实现每个线程发起请求时，使用单独的TCP连接。</p><h3 data-id="heading-6">(2) <code>ParallelGetTask</code> - 一整个多线程下载任务</h3></div><div class="codeText"><div class="codeHead">C#代码</div><ol start="1" class="dp-c"><li class="alt"><span><span>package&nbsp;model&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span>import&nbsp;(&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">&quot;bufio&quot;</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">&quot;fmt&quot;</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">&quot;gitee.com/swsk33/shard-download-demo/util&quot;</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">&quot;github.com/fatih/color&quot;</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">&quot;io&quot;</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">&quot;net/http&quot;</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">&quot;os&quot;</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">&quot;path/filepath&quot;</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">&quot;strconv&quot;</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">&quot;sync&quot;</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">&quot;time&quot;</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>)&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span><span class="comment">//&nbsp;ParallelGetTask&nbsp;多线程下载任务类，存放一个多线程下载任务的参数和状态量</span><span>&nbsp;&nbsp;</span></span></li><li><span>type&nbsp;ParallelGetTask&nbsp;<span class="keyword">struct</span><span>&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;文件的下载链接</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;Url&nbsp;<span class="keyword">string</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;文件的最终保存位置</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;FilePath&nbsp;<span class="keyword">string</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;下载并发数</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;Concurrent&nbsp;<span class="keyword">int</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;下载的分片临时文件保存文件夹</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;TempFolder&nbsp;<span class="keyword">string</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;下载文件的总大小</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;TotalSize&nbsp;int64&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;全部的下载分片任务参数列表</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;ShardTaskList&nbsp;[]*ShardTask&nbsp;&nbsp;</span></li><li class="alt"><span>&#125;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span><span class="comment">//&nbsp;NewParallelGetTask&nbsp;构造函数</span><span>&nbsp;&nbsp;</span></span></li><li><span>func&nbsp;NewParallelGetTask(url&nbsp;<span class="keyword">string</span><span>,&nbsp;filePath&nbsp;</span><span class="keyword">string</span><span>,&nbsp;concurrent&nbsp;</span><span class="keyword">int</span><span>,&nbsp;tempFolder&nbsp;</span><span class="keyword">string</span><span>)&nbsp;*ParallelGetTask&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;&amp;ParallelGetTask&#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;参数赋值</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Url:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;url,&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FilePath:&nbsp;&nbsp;&nbsp;filePath,&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Concurrent:&nbsp;concurrent,&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TempFolder:&nbsp;tempFolder,&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;初始化状态量</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TotalSize:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0,&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ShardTaskList:&nbsp;make([]*ShardTask,&nbsp;0),&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&#125;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span><span class="comment">//&nbsp;发送HEAD请求获取待下载文件的大小</span><span>&nbsp;&nbsp;</span></span></li><li><span>func&nbsp;(task&nbsp;*ParallelGetTask)&nbsp;getLength()&nbsp;error&nbsp;&#123;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;发送请求</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;response,&nbsp;e&nbsp;:=&nbsp;http.Head(task.Url)&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;e&nbsp;!=&nbsp;nil&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color.Red(<span class="string">&quot;发送HEAD请求出错！&quot;</span><span>)&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;e&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;读取并设定长度</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;task.TotalSize&nbsp;=&nbsp;response.ContentLength&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;nil&nbsp;&nbsp;</span></span></li><li><span>&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li><span><span class="comment">//&nbsp;根据待下载文件的大小和设定的并发数，创建每个分片任务对象</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>func&nbsp;(task&nbsp;*ParallelGetTask)&nbsp;allocateTask()&nbsp;&#123;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;如果并发数大于总大小，则进行调整</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;int64(task.Concurrent)&nbsp;&gt;&nbsp;task.TotalSize&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;task.Concurrent&nbsp;=&nbsp;<span class="keyword">int</span><span>(task.TotalSize)&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;开始计算每个分片的下载范围</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;eachSize&nbsp;:=&nbsp;task.TotalSize&nbsp;/&nbsp;int64(task.Concurrent)&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;创建任务对象</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">for</span><span>&nbsp;i&nbsp;:=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;task.Concurrent;&nbsp;i++&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;task.ShardTaskList&nbsp;=&nbsp;append(task.ShardTaskList,&nbsp;NewShardTask(task.Url,&nbsp;i+1,&nbsp;filepath.Join(task.TempFolder,&nbsp;strconv.Itoa(i+1)),&nbsp;int64(i)*eachSize,&nbsp;int64(i+1)*eachSize-1))&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;处理末尾部分</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;task.TotalSize%int64(task.Concurrent)&nbsp;!=&nbsp;0&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;task.ShardTaskList[task.Concurrent-1].RangeEnd&nbsp;=&nbsp;task.TotalSize&nbsp;-&nbsp;1&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li><span>&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li><span><span class="comment">//&nbsp;根据任务列表进行多线程分片下载操作</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>func&nbsp;(task&nbsp;*ParallelGetTask)&nbsp;downloadShard()&nbsp;&#123;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;创建线程组</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;waitGroup&nbsp;:=&nbsp;&amp;sync.WaitGroup&#123;&#125;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;开始执行全部分片下载线程</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">for</span><span>&nbsp;_,&nbsp;task&nbsp;:=&nbsp;range&nbsp;task.ShardTaskList&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;go&nbsp;task.DoShardGet(waitGroup)&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;waitGroup.Add(1)&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;等待全部下载完成</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;waitGroup.Wait()&nbsp;&nbsp;</span></li><li class="alt"><span>&#125;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span><span class="comment">//&nbsp;下载完成后，合并分片文件</span><span>&nbsp;&nbsp;</span></span></li><li><span>func&nbsp;(task&nbsp;*ParallelGetTask)&nbsp;mergeFile()&nbsp;error&nbsp;&#123;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;创建目的文件</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;targetFile,&nbsp;e&nbsp;:=&nbsp;os.OpenFile(task.FilePath,&nbsp;os.O_CREATE&#124;os.O_WRONLY&#124;os.O_APPEND,&nbsp;0755)&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;e&nbsp;!=&nbsp;nil&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color.Red(<span class="string">&quot;创建目标文件出错！&quot;</span><span>)&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;e&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;创建写入器</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;writer&nbsp;:=&nbsp;bufio.NewWriter(targetFile)&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;准备读取每个分片文件</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">for</span><span>&nbsp;_,&nbsp;shard&nbsp;:=&nbsp;range&nbsp;task.ShardTaskList&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;shardFile,&nbsp;e&nbsp;:=&nbsp;os.OpenFile(shard.ShardFilePath,&nbsp;os.O_RDONLY,&nbsp;0755)&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;e&nbsp;!=&nbsp;nil&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color.Red(<span class="string">&quot;读取分片文件出错！&quot;</span><span>)&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;e&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reader&nbsp;:=&nbsp;bufio.NewReader(shardFile)&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;readBuffer&nbsp;:=&nbsp;make([]<span class="keyword">byte</span><span>,&nbsp;1024*1024)&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">for</span><span>&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;读取每个分片文件，一次读取1KB</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;readSize,&nbsp;readError&nbsp;:=&nbsp;reader.Read(readBuffer)&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;处理结束或错误</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;readError&nbsp;!=&nbsp;nil&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;readError&nbsp;==&nbsp;io.EOF&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">break</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;<span class="keyword">else</span><span>&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color.Red(<span class="string">&quot;读取分片文件出错！&quot;</span><span>)&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;readError&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;写入到最终合并的文件</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_,&nbsp;writeError&nbsp;:=&nbsp;writer.Write(readBuffer[0:readSize])&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;writeError&nbsp;!=&nbsp;nil&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color.Red(<span class="string">&quot;写入合并文件出错！&quot;</span><span>)&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;writeError&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_&nbsp;=&nbsp;writer.Flush()&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;关闭分片文件资源</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_&nbsp;=&nbsp;shardFile.Close()&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;关闭目的文件资源</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;_&nbsp;=&nbsp;targetFile.Close()&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;nil&nbsp;&nbsp;</span></span></li><li><span>&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li><span><span class="comment">//&nbsp;删除分片临时文件</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>func&nbsp;(task&nbsp;*ParallelGetTask)&nbsp;cleanShard()&nbsp;error&nbsp;&#123;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">for</span><span>&nbsp;_,&nbsp;shard&nbsp;:=&nbsp;range&nbsp;task.ShardTaskList&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e&nbsp;:=&nbsp;os.Remove(shard.ShardFilePath)&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;e&nbsp;!=&nbsp;nil&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color.Red(<span class="string">&quot;删除分片临时文件%s出错！&quot;</span><span>,&nbsp;shard.ShardFilePath)&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;e&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;nil&nbsp;&nbsp;</span></span></li><li><span>&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li><span><span class="comment">//&nbsp;在一个新线程中，实时输出每个分片的下载进度和总进度</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>func&nbsp;(task&nbsp;*ParallelGetTask)&nbsp;printTotalProcess()&nbsp;&#123;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;go&nbsp;func()&nbsp;&#123;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;上一次统计时的已下载大小，用于计算速度</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var&nbsp;lastDownloadSize&nbsp;int64&nbsp;=&nbsp;0&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">for</span><span>&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;如果全部任务完成则结束输出，并统计并发数</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;allDone&nbsp;:=&nbsp;<span class="keyword">true</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;当前并发数</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;currentTaskCount&nbsp;:=&nbsp;0&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">for</span><span>&nbsp;_,&nbsp;shardTask&nbsp;:=&nbsp;range&nbsp;task.ShardTaskList&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;!shardTask.TaskDone&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;allDone&nbsp;=&nbsp;<span class="keyword">false</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;currentTaskCount&nbsp;+=&nbsp;1&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;allDone&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">break</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;统计所有分片已下载大小之和</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var&nbsp;totalDownloadSize&nbsp;int64&nbsp;=&nbsp;0&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">for</span><span>&nbsp;_,&nbsp;shardTask&nbsp;:=&nbsp;range&nbsp;task.ShardTaskList&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;totalDownloadSize&nbsp;+=&nbsp;shardTask.DownloadSize&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;计算速度</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;currentDownload&nbsp;:=&nbsp;totalDownloadSize&nbsp;-&nbsp;lastDownloadSize&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lastDownloadSize&nbsp;=&nbsp;totalDownloadSize&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;speedString&nbsp;:=&nbsp;util.ComputeSpeed(currentDownload,&nbsp;300)&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;输出到控制台</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fmt.Printf(<span class="string">&quot;&#92;r当前并发数：%3d&nbsp;速度：%s&nbsp;总进度：%3.2f%%&quot;</span><span>,&nbsp;currentTaskCount,&nbsp;speedString,&nbsp;float32(totalDownloadSize)/float32(task.TotalSize)*100)&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;等待300ms</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;time.Sleep(300&nbsp;*&nbsp;time.Millisecond)&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;()&nbsp;&nbsp;</span></li><li><span>&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li><span><span class="comment">//&nbsp;Run&nbsp;开始执行整个分片多线程下载任务</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>func&nbsp;(task&nbsp;*ParallelGetTask)&nbsp;Run()&nbsp;error&nbsp;&#123;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;获取文件大小</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;e&nbsp;:=&nbsp;task.getLength()&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;e&nbsp;!=&nbsp;nil&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color.Red(<span class="string">&quot;%s&quot;</span><span>,&nbsp;e)&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;e&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;color.HiYellow(<span class="string">&quot;已获取到下载文件大小：%d字节&quot;</span><span>,&nbsp;task.TotalSize)&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;分配任务</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;task.allocateTask()&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;color.HiYellow(<span class="string">&quot;已完成分片任务分配，共计%d个任务&quot;</span><span>,&nbsp;len(task.ShardTaskList))&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;开启进度输出</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;task.printTotalProcess()&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;开始下载分片</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;task.downloadShard()&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;color.HiYellow(<span class="string">&quot;&#92;n所有分片已下载完成！&quot;</span><span>)&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;开始合并文件</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;e&nbsp;=&nbsp;task.mergeFile()&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;e&nbsp;!=&nbsp;nil&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color.Red(<span class="string">&quot;%s&quot;</span><span>,&nbsp;e)&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;e&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;color.HiYellow(<span class="string">&quot;合并分片完成！&quot;</span><span>)&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;清理临时分片文件</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;e&nbsp;=&nbsp;task.cleanShard()&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;e&nbsp;!=&nbsp;nil&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color.Red(<span class="string">&quot;%s&quot;</span><span>,&nbsp;e)&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;e&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;color.HiYellow(<span class="string">&quot;清理分片临时文件完成！&quot;</span><span>)&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;color.Green(<span class="string">&quot;分片下载任务完成！&quot;</span><span>)&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;nil&nbsp;&nbsp;</span></span></li><li class="alt"><span>&#125;&nbsp;&nbsp;</span></li></ol></div><p><span style="color: rgb(37, 41, 51); font-family: -apple-system, system-ui, &quot;Segoe UI&quot;, Roboto, Ubuntu, Cantarell, &quot;Noto Sans&quot;, sans-serif, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial; font-size: 16px;">上述</span><code style="font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; font-size: 0.87em; word-break: break-word; border-radius: 2px; overflow-x: auto; background-color: rgb(255, 245, 245); color: rgb(255, 80, 44); padding: 0.065em 0.4em;">printTotalProcess</code><span style="color: rgb(37, 41, 51); font-family: -apple-system, system-ui, &quot;Segoe UI&quot;, Roboto, Ubuntu, Cantarell, &quot;Noto Sans&quot;, sans-serif, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial; font-size: 16px;">函数中，</span><code style="font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; font-size: 0.87em; word-break: break-word; border-radius: 2px; overflow-x: auto; background-color: rgb(255, 245, 245); color: rgb(255, 80, 44); padding: 0.065em 0.4em;">util.ComputeSpeed</code><span style="color: rgb(37, 41, 51); font-family: -apple-system, system-ui, &quot;Segoe UI&quot;, Roboto, Ubuntu, Cantarell, &quot;Noto Sans&quot;, sans-serif, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial; font-size: 16px;">函数用于计算下载速度并自动转换为可读单位，代码如下：</span></p><div class="codeText"><div class="codeHead">C#代码</div><ol start="1" class="dp-c"><li class="alt"><span><span>package&nbsp;util&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span>import&nbsp;(&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">&quot;fmt&quot;</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">&quot;math&quot;</span><span>&nbsp;&nbsp;</span></span></li><li><span>)&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li><span><span class="comment">//&nbsp;关于单位的实用工具函数</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li><span><span class="comment">//&nbsp;ComputeSpeed&nbsp;计算网络速度</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span><span class="comment">//&nbsp;size&nbsp;一段时间内下载的数据大小，单位字节</span><span>&nbsp;&nbsp;</span></span></li><li><span><span class="comment">//&nbsp;timeElapsed&nbsp;经过的时间长度，单位毫秒</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span><span class="comment">//&nbsp;返回计算得到的网速，会自动换算单位</span><span>&nbsp;&nbsp;</span></span></li><li><span>func&nbsp;ComputeSpeed(size&nbsp;int64,&nbsp;timeElapsed&nbsp;<span class="keyword">int</span><span>)&nbsp;</span><span class="keyword">string</span><span>&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;bytePerSecond&nbsp;:=&nbsp;size&nbsp;/&nbsp;int64(timeElapsed)&nbsp;*&nbsp;1000&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;0&nbsp;&lt;=&nbsp;bytePerSecond&nbsp;&amp;&amp;&nbsp;bytePerSecond&nbsp;&lt;=&nbsp;1024&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;fmt.Sprintf(</span><span class="string">&quot;%4d&nbsp;Byte/s&quot;</span><span>,&nbsp;bytePerSecond)&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;bytePerSecond&nbsp;&gt;&nbsp;1024&nbsp;&amp;&amp;&nbsp;bytePerSecond&nbsp;&lt;=&nbsp;int64(math.Pow(1024,&nbsp;2))&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;fmt.Sprintf(</span><span class="string">&quot;%6.2f&nbsp;KB/s&quot;</span><span>,&nbsp;float64(bytePerSecond)/1024)&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;bytePerSecond&nbsp;&gt;&nbsp;1024*1024&nbsp;&amp;&amp;&nbsp;bytePerSecond&nbsp;&lt;=&nbsp;int64(math.Pow(1024,&nbsp;3))&nbsp;&#123;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;fmt.Sprintf(</span><span class="string">&quot;%6.2f&nbsp;MB/s&quot;</span><span>,&nbsp;float64(bytePerSecond)/math.Pow(1024,&nbsp;2))&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;fmt.Sprintf(</span><span class="string">&quot;%6.2f&nbsp;GB/s&quot;</span><span>,&nbsp;float64(bytePerSecond)/math.Pow(1024,&nbsp;3))&nbsp;&nbsp;</span></span></li><li><span>&#125;&nbsp;&nbsp;</span></li></ol></div><p>&nbsp;</p><div><div><p>可见通过构造函数<code>NewParallelGetTask</code>完成参数传递和状态量设定后，其它每个私有函数都对应我们多线程下载中的一个步骤，最后由公开函数<code>Run</code>统筹组织起所有的步骤，完成整个多线程下载任务。</p><h2 data-id="heading-7">3，实现效果</h2><p>现在我们在<code>main</code>函数中创建一个<code>ParallelGetTask</code>对象，设定好参数后调用其<code>Run</code>方法即可开始多线程下载文件的任务：</p></div><div class="codeText"><div class="codeHead">C#代码</div><ol start="1" class="dp-c"><li class="alt"><span><span>package&nbsp;main&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span>import&nbsp;(&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">&quot;gitee.com/swsk33/shard-download-demo/model&quot;</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>)&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span>func&nbsp;main()&nbsp;&#123;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;创建任务</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;task&nbsp;:=&nbsp;model.NewParallelGetTask(&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">&quot;https://github.com/jgraph/drawio-desktop/releases/download/v24.7.17/draw.io-24.7.17-windows-installer.exe&quot;</span><span>,&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">&quot;C:&#92;&#92;Users&#92;&#92;swsk33&#92;&#92;Downloads&#92;&#92;draw.io.exe&quot;</span><span>,&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;64,&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">&quot;C:&#92;&#92;Users&#92;&#92;swsk33&#92;&#92;Downloads&#92;&#92;temp&quot;</span><span>,&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;)&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;执行任务</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;_&nbsp;=&nbsp;task.Run()&nbsp;&nbsp;</span></li><li class="alt"><span>&#125;&nbsp;&nbsp;</span></li></ol></div><div>&nbsp;</div></div></div></div></div></div>
]]>
</description>
</item>
</channel>
</rss>